Miniwindow plugin - campaign tracker for Aardwolf

Posted by Nick Gammon on Sun 27 Jul 2008 12:26 AM — 25 posts, 134,947 views.

Australia Forum Administrator #0
The plugin below illustrates using the new miniwindows concept in a plugin.

Miniwindows were released in version 4.34, see here for a copy:

http://www.gammon.com.au/forum/?id=8811

This plugin tracks your current campaign. Type "cp check" to get it to capture the campaign data.

Its design is similar to the quest tracker described here:

http://www.gammon.com.au/forum/?id=8814

To use, copy between the lines, and save as Aardwolf_Campaign_Noter.xml in your Plugins directory, and then use File -> Plugins to install it.


<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Tuesday, July 15, 2008, 11:06 AM -->
<!-- MuClient version 4.33 -->

<!-- Plugin "Aardwolf_Campaign_Noter" generated by Plugin Wizard -->

<muclient>
<plugin
   name="Aardwolf_Campaign_Noter_v2"
   author="Nick Gammon"
   id="23c3c91af0a26790c625f5d1"
   language="Lua"
   purpose="Shows remaining campaign objectives"
   date_written="2008-07-22"
   requires="4.34"
   version="1.0"
   save_state="y"
   >
<description trim="y">
<![CDATA[
Shows outstanding campaign objectives.

To activate, type: cp check
]]>
</description>

</plugin>


<!--  Triggers  -->

<triggers>
  <trigger
   match="You have * to finish this campaign."
   name="cp_time"
   script="cp_time"
   sequence="100"
  >
  </trigger>
  <trigger
   match="^You still have to kill \* (.*?)$"
   name="campaign_item"
   script="campaign_item"
   sequence="100"
   regexp="y"
  >
  </trigger>
  
 <trigger
   enabled="y"
   match="Congratulations, that was one of your CAMPAIGN mobs!"
   send_to="10"
   sequence="100"
  >
  <send>cp check</send>
  </trigger>

 <trigger
   enabled="y"
   match="CONGRATULATIONS! You have completed your campaign."
   sequence="100"
   script="campaign_done"
  >
  </trigger>


</triggers>

<!--  Aliases  -->

<aliases>
  <alias
   name="cp_check"
   script="cp_check"
   match="^(cp|campaign) check$"
   enabled="y"
   regexp="y"
   sequence="100"
  >
  <send>cp check</send>
  </alias>
</aliases>


<!--  Timers  -->


<timers>
  <timer 
      script="show_campaign_text" 
      enabled="y" 
      second="30.00" 
      >
  </timer>
</timers>


<!--  Script  -->


<script>
<![CDATA[

require "commas"

background_colour = 0xE7FFFF
text_colour = 0x000000
heading_colour = 0x800080
time_colour = 0x82004B

campaign_info = {}
 
function capitalize (s)
  return string.sub (s, 1, 1):upper () .. string.sub (s, 2):lower ()
end -- capitalize 

function campaign_item (name, line, wildcards)
  local text =  wildcards [1]
  text = string.sub (text, 1, 1):upper () .. string.sub (text, 2)
  table.insert (campaign_info, text)
  max_width = math.max (max_width, WindowTextWidth (win, font_id, text))
end -- campaign_item

function campaign_done (name, line, wildcards)
  Send "score"
  WindowShow (win, false)
  campaign_info = {}
end -- campaign_done

function Display_Line (i, text, id, colour)

local left = 5
local top =  (i * font_height) - font_height

  WindowText (win, id, text, left, top, 0, 0, colour)

end -- Display_Line

function show_campaign_text ()

  -- do nothing if no campaign
  if #campaign_info == 0 or when_required == nil then
    return
  end -- if

  -- recreate the window the correct size
  check (WindowCreate (win, 
                 0, 0,   -- left, top (auto-positions)
                 max_width + 10,     -- width
                 (#campaign_info + 2)   * font_height + 5,  -- height
                 7,       -- auto-position: top middle
                 0,  -- flags
                 0xE7FFFF) )
 
  -- heading
  local text = "Current campaign. You need to kill:"
  max_width = math.max (max_width, WindowTextWidth (win, font_id, text))
  Display_Line (1, text, font_id, heading_colour)

  -- list of mobs
  for i, v in ipairs (campaign_info) do
    Display_Line (i + 1, v, font_id, text_colour)
  end -- for
  
  -- how long to go
  local time_to_go = when_required - os.time ()
  
  if time_to_go < 0 then
    return
  end -- if
  
  text = string.format ("Time to go: %s", convert_time (time_to_go))
  max_width = math.max (max_width, WindowTextWidth (win, font_id, text))
  Display_Line (#campaign_info + 2, text, font_id, time_colour)
    
  WindowShow (win, true)  
end -- show_campaign_text

function cp_time (name, line, wildcards)
  local text = wildcards [1]
    
  -- work out when campaign ends
  
  when_required = os.time ()
  
  local days = string.match (text, "(%d+) days?")
  if days then
    when_required = when_required + tonumber (days) * 60 * 60 * 24
  end -- some days left
  
  local hours = string.match (text, "(%d+) hours?")  
  if hours then
    when_required = when_required + tonumber (hours) * 60 * 60
  end -- some days left

  local minutes = string.match (text, "(%d+) minutes?")  
  if minutes then
    when_required = when_required + tonumber (minutes) * 60
  end -- some days left

  
  check (EnableTrigger ("campaign_item", false))
  check (EnableTrigger ("cp_time", false))

  show_campaign_text ()

end -- cp_time

function cp_check( name, line, wildcards)
  check (EnableTrigger ("campaign_item", true))
  check (EnableTrigger ("cp_time", true))
  campaign_info = {}
  max_width = 0
  
end -- cp_check


function OnPluginInstall ()
  
  win = GetPluginID ()
  font_id = "fn"
  
  font_name = "Comic Sans MS"    -- the actual font
  
  -- make window so I can grab the font info
  check (WindowCreate (win, 
                 0, 0, 1, 1,  
                 1,   -- irrelevant
                 0, 
                 background_colour) )

  check (WindowFont (win, font_id, font_name, 8, false, false, false, false, 0, 0))  -- normal  
  font_height = WindowFontInfo (win, font_id, 1)  -- height
end -- OnPluginInstall

function OnPluginDisable ()
  WindowShow (win, false)
end -- OnPluginDisable

]]>
</script>

</muclient>
Amended on Mon 28 Jul 2008 01:04 AM by Nick Gammon
Australia Forum Administrator #1

Example of it in operation:

#2
Very nice, is there a way to hide the mini window without removing the plugin completely (while a campaign isn't finished)?
Australia Forum Administrator #3
These are still a bit of a work in progress. Something like the two aliases below should do it, although I haven't tested them.

Copy between the lines and paste before this line:


<!--  Script  -->



Two aliases here:


<aliases>
  <alias
   match="hide_campaign"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>WindowShow (win, false)</send>
  </alias>

  <alias
   match="show_campaign"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>WindowShow (win, true)</send>
  </alias>
</aliases>


#4
Would there be a way to get the cp information output into a separate world window instead of an overlapping 'window'.

I ask this because font size vs screen size with this little window causes a significant amount of text to be covered up while its active.
Australia Forum Administrator #5
Where would you put this extra window that doesn't take up space? Just resize the main window a bit so that the tracker window moves to the right, and doesn't cover up anything.
#6
unfortunately increasing the size of my main window will result in covering up all the other windows that i use constantly (spellup monitor,status,map and chat catcher)

my idea is that with the targets in a seperate window i can have it up over the other windows for the few moments it takes me to figure out where im running to next, then minimize it or send it behind the other windows ... no need for the window to be up and visible once i get into the appropriate zone.
#7
Thank you very much for this, Nick! Using it and loving it.

The aliases work, but why do the windows appear again after each tick? o.O

I've been wanting something like this for gquests for a long time now -- have always been copying to notepad. So I tried modifying this script...e.g.

<triggers>
  <trigger
   match="^You still have to kill (.*?) \* (.*?)$"
   name="gq_item"
   script="gq_item"
   sequence="100"
   regexp="y"
  >
  </trigger>


Unfortunately, didn't really seem to work..the trigger for gq check would fire, but that's about it. -wry- Ah well, not pro enough to wrestle with the code. :(
Amended on Thu 19 Feb 2009 02:12 AM by Jennkei
Australia Forum Administrator #8
Quote:

unfortunately increasing the size of my main window will result in covering up all the other windows that i use constantly


My original campaign tracker sent to a notepad window. Here it is:


<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Tuesday, July 15, 2008, 11:06 AM -->
<!-- MuClient version 4.33 -->

<!-- Plugin "Aardwolf_Campaign_Noter" generated by Plugin Wizard -->

<muclient>
<plugin
   name="Aardwolf_Campaign_Noter"
   author="Nick Gammon"
   id="2bfb7e23fb504ae4e55f0ed6"
   language="Lua"
   purpose="Shows remaining campaign objectives"
   date_written="2008-07-15 11:04:43"
   requires="4.33"
   version="1.0"
   save_state="y"
   >
<description trim="y">
<![CDATA[
Shows outstanding campaign objectives.

To activate, type: cp check
]]>
</description>

</plugin>


<!--  Triggers  -->

<triggers>
  <trigger
   match="You have * left to finish this campaign."
   name="cp_time"
   script="cp_time"
   sequence="100"
  >
  </trigger>
  <trigger
   match="You still have to kill *"
   name="campaign_item"
   script="campaign_item"
   sequence="100"
  >
  </trigger>
  
 <trigger
   enabled="y"
   match="Congratulations, that was one of your CAMPAIGN mobs!"
   send_to="10"
   sequence="100"
  >
  <send>cp check</send>
  </trigger>

 <trigger
   enabled="y"
   match="CONGRATULATIONS! You have completed your campaign."
   sequence="100"
   script="campaign_done"
  >
  </trigger>


</triggers>

<!--  Aliases  -->

<aliases>
  <alias
   name="cp_check"
   script="cp_check"
   match="^(cp|campaign) check$"
   enabled="y"
   regexp="y"
   sequence="100"
  >
  <send>cp check</send>
  </alias>
</aliases>

<!--  Timers  -->

<timers>
  <timer script="get_notepad_position" 
         enabled="y" 
         second="10.00" 
         active_closed="y" >

  </timer>
</timers>

<!--  Script  -->


<script>
<![CDATA[

require "serialize"

pos = {}

function get_notepad_position (name)
  local newpos = GetNotepadWindowPosition ("Campaign")
  pos = newpos or pos
  
  if next (pos) then
    Left, Top, Width, Height = pos.left, pos.top, pos.width, pos.height
  end -- if 
end -- get_notepad_position

-- notepad position
Left, Top, Width, Height = 500, 200, 600, 600
MenuSize = 50

campaign_info = {}
 
function campaign_item (name, line, wildcards)
  table.insert (campaign_info, line)
end -- campaign_item

function campaign_done (name, line, wildcards)
  Send "score"
  CloseNotepad ("Campaign", false)
end -- campaign_done

function cp_time (name, line, wildcards)

  table.insert (campaign_info, line)
  
  check (EnableTrigger ("campaign_item", false))
  check (EnableTrigger ("cp_time", false))

  CloseNotepad ("Campaign", false)
  ReplaceNotepad ("Campaign", table.concat (campaign_info, "\r\n"))
  NotepadColour ("Campaign", "black", "#FFFFE7")
  NotepadSaveMethod ("Campaign", 2)  -- don't try to save
  
  local lines = #campaign_info + 1
  
  if lines < 40 then
    MoveNotepadWindow ("Campaign", Left, Top, Width, GetInfo (214) * lines + MenuSize)  
  else
    MoveNotepadWindow("Campaign", Left, Top, Width, Height)
  end -- if
  
  ActivateNotepad "Campaign"
  
  Activate ()
  
  NotepadReadOnly ("Campaign")
  
end -- cp_time

function cp_check( name, line, wildcards)
  check (EnableTrigger ("campaign_item", true))
  check (EnableTrigger ("cp_time", true))
  campaign_info = {}
  
end -- cp_check

function OnPluginSaveState ()
  get_notepad_position ()
  SetVariable ("pos", "pos = " .. serialize.save_simple (pos))
end -- function OnPluginSaveState

function OnPluginInstall ()
  assert (loadstring (GetVariable ("pos") or "")) ()
  
  if next (pos) then
    Left, Top, Width, Height = pos.left, pos.top, pos.width, pos.height
  end -- table not empty
  
end -- OnPluginInstall

]]>
</script>


<!--  Plugin help  -->

<aliases>
  <alias
   script="OnHelp"
   match="Aardwolf_Campaign_Noter:help"
   enabled="y"
  >
  </alias>
</aliases>

<script>
<![CDATA[
function OnHelp ()
  world.Note (world.GetPluginInfo (world.GetPluginID (), 3))
end
]]>
</script> 

</muclient>


Not sure how well it works now, but it could be a start for you to look at.
Australia Forum Administrator #9
Quote:

The aliases work, but why do the windows appear again after each tick? o.O


It seems I didn't think it through. :O

Give the timer a name, like this:


<timers>
  <timer 
      script="show_campaign_text" 
      enabled="y" 
      second="30.00"
      name="show_campaign_text" 
      >
  </timer>
</timers>


Modify the aliases to disable/enable the timer:


<aliases>
  <alias
   match="hide_campaign"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
WindowShow (win, false)
EnableTimer ("show_campaign_text", false)
</send>
  </alias>

  <alias
   match="show_campaign"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
WindowShow (win, true)
EnableTimer ("show_campaign_text", true)
</send>
  </alias>
</aliases>



#10
Hiding works beautifully now. Thank you so much, Nick! :)

If it's not too much trouble and you're free, could you possibly help me have a look at what's wrong with my replace-cp-with-gq version, please..? It's alright if you aren't, just thought I'd ask anyway. :D

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- From Aardwolf_Campaign_Noter_v2 by Nick Gammon -->
<!-- Saved on Tuesday, July 15, 2008, 11:06 AM -->
<!-- MuClient version 4.33 -->

<!-- Plugin "Aardwolf_Gq_Noter" generated by Plugin Wizard -->

<muclient>
<plugin
   name="Aardwolf_gq_Noter"
   author="Nick Gammon -modified-"
   id="23c3c91af0a26790c625f5d2"
   language="Lua"
   purpose="Shows remaining gq objectives"
   date_written="2008-07-22"
   requires="4.34"
   version="1.0"
   save_state="y"
   >
<description trim="y">
<![CDATA[
Shows outstanding gq objectives.

]]>
</description>

</plugin>


<!--  Triggers  -->

<triggers>
  <trigger
   match="^You still have to kill (.*?) \* (.*?)$"
   name="gq_item"
   script="gq_item"
   sequence="100"
   regexp="y"
  >
  </trigger>
  
 <trigger
   enabled="y"
   match="Congratulations, that was one of the GLOBAL QUEST mobs!"
   send_to="10"
   sequence="100"
  >
  <send>gq check</send>
  </trigger>

 <trigger
   enabled="y"
   match="Global Quest: The global quest has been won by"
   sequence="100"
   script="gq_done"
  >
  </trigger>

</triggers>

<!--  Aliases  -->

<aliases>
  <alias
   name="gq_check"
   script="gq_check"
   match="^(gq|gquest) check$"
   enabled="y"
   regexp="y"
   sequence="100"
  >
  <send>gq check</send>
  </alias>
</aliases>


<!--  Script  -->


<script>
<![CDATA[

require "commas"

background_colour = 0xE7FFFF
text_colour = 0x000000
heading_colour = 0x800080
time_colour = 0x82004B

gq_info = {}
 
function capitalize (s)
  return string.sub (s, 1, 1):upper () .. string.sub (s, 2):lower ()
end -- capitalize 

function gq_item (name, line, wildcards)
  local text =  wildcards [1]
  text = string.sub (text, 1, 1):upper () .. string.sub (text, 2)
  table.insert (gq_info, text)
  max_width = math.max (max_width, WindowTextWidth (global, font_id, text))
end -- gq_item

function gq_done (name, line, wildcards)
  WindowShow (global, false)
  gq_info = {}
end -- gq_done

function Display_Line (i, text, id, colour)

local right = 5
local top =  (i * font_height) - font_height

  WindowText (global, id, text, left, top, 0, 0, colour)

end -- Display_Line

function show_gq_text ()

  -- do nothing if no gq
  if #gq_info == 0 or when_required == nil then
    return
  end -- if

  -- recreate the Window the correct size
  check (WindowCreate (global, 
                 0, 0,   -- left, top (auto-positions)
                 max_width + 10,     -- width
                 (#gq_info + 2)   * font_height + 5,  -- height
                 7,       -- auto-position: top middle
                 0,  -- flags
                 0xE7FFFF) )
 
  -- heading
  local text = "Current gq. You need to kill:"
  max_width = math.max (max_width, WindowTextWidth (global, font_id, text))
  Display_Line (1, text, font_id, heading_colour)

  -- list of mobs
  for i, v in ipairs (gq_info) do
    Display_Line (i + 1, v, font_id, text_colour)
  end -- for
     
  WindowShow (global, true)  
end -- show_gq_text

   
  
function gq_check( name, line, wildcards)
  check (EnableTrigger ("gq_item", true))
  gq_info = {}
  max_width = 0
  
end -- gq_check


function OnPluginInstall ()
  global = GetPluginID ()
  font_id = "fn"
  
  font_name = "Comic Sans MS"    -- the actual font
  
  -- make window so I can grab the font info
  check (WindowCreate (global, 
                 0, 0, 1, 1,  
                 1,   -- irrelevant
                 0, 
                 background_colour) )

  check (WindowFont (global, font_id, font_name, 8, false, false, false, false, 0, 0))  -- normal  
  font_height = WindowFontInfo (global, font_id, 1)  -- height
end -- OnPluginInstall

function OnPluginDisable ()
  WindowShow (global, false)
end -- OnPluginDisable

]]>
</script>

</muclient>
Amended on Thu 19 Feb 2009 03:50 AM by Jennkei
#11
Has anyone out there successfully made a gq mob tracker similar to the cp mob tracker and willing to share it? I would maybe not be eternally grateful, but I would be grateful for a long, long time.
#12
I have one at http://code.google.com/p/bastmush/

Bast
#13
Thanks very much! :-) If I could only get by the parsing errors, I'd be delighted!
#14
Post the errors.
#15
I must apologize, I'm extremely dense about this stuff :-( But the error I get is:

Line 47: Error parsing script (problem in this file)

Oh, and I didn't notice this yesterday, but I also have a sizable message in my "home" window.

Run-time error
Plugin: miniwin_gq (called from world: Aardwolf)
Immediate execution
[string "Plugin"]:154: module 'miniwin' not found:
no field package.preload['miniwin']
no file '.\miniwin.lua'
no file 'H:\Documents\Curtis\Misc\MUSHclient\lua\miniwin.lua'
no file 'H:\Documents\Curtis\Misc\MUSHclient\lua\miniwin\init.lua'
no file 'H:\Documents\Curtis\Misc\MUSHclient\miniwin.lua'
no file 'H:\Documents\Curtis\Misc\MUSHclient\miniwin\init.lua'
stack traceback:
[C]: in function 'require'
[string "Plugin"]:154: in main chunk
Error context in script:
150 :
151 :
152 : require 'var'
153 : require "commas"
154*: require 'miniwin'
155 : require 'tprint'
156 : require 'pluginhelper'
157 :
158 : gqwin = Miniwin{name="GQ", windowpos=7, font_size=7, font="Verdana", header_height=0}

I'm guessing I'm missing some files necessary to run the plugin, but I don't know where to get these. Sorry for the trouble.
#16
In the zip that you downloaded, copy all the files from the lua directory into <MushclientInstallDir>/lua.

Bast
#17
Thanks for your patience! I haven't had a gq yet, but it appears to be working :-)
#18
Line 17: Plugin "id" field must be unique for this world (problem in this file)

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Tuesday, July 15, 2008, 11:06 AM -->
<!-- MuClient version 4.33 -->

<!-- Plugin "Aardwolf_Campaign_Noter" generated by Plugin Wizard -->

<muclient>
<plugin
name="Aardwolf_Campaign_Noter_v2"
author="Nick Gammon"
id="23c3c91af0a26790c625f5d1"
language="Lua"
purpose="Shows remaining campaign objectives"
date_written="2008-07-22"
requires="4.34"
version="1.0"
save_state="y"
>
<description trim="y">
<![CDATA[
Shows outstanding campaign objectives.

To activate, type: cp check
]]>
</description>

</plugin>5d1"
Australia Forum Administrator #19
Sounds like you have two plugins of the same ID. Check what other plugins you have loaded.
USA #20
Yes, that happens if you happen to add the same plugin twice, or two plugins somehow manage to have the same ID. If it's the former, just remove it from the plugins list and re-add it, or better, just click Reinstall with it selected. If it's the latter, it probably won't hurt in this case to replace the ID in the plugin's XML (where it points it out); you can go to Edit -> Generate Unique ID to get a new ID to put in there.

EDIT: Ninja'd.
Netherlands #21
shouldn't this be created in a new miniwindow?
if yes, where is that window?

[EDIT] nvm :P got it to work thnx
Amended on Sat 10 Mar 2012 11:55 PM by Jsportive-Thalanvor
#22
Hei

Need a bit help with this campaign tracker plugin.
Trying to modify it, so that every mob name on the list is hyperlink. Any good ideas ?
USA Global Moderator #23
Miniwindows do not have hyperlinks. They just have hotspots that may or may not span the height and width of an arbitrary section of text.
Australia Forum Administrator #24
Soodik said:

Trying to modify it, so that every mob name on the list is hyperlink.


Hyperlinks to what? As Fiendish says the text is not a hyperlink per se, but you could simulate one by drawing an underline and making a hotspot that spans the text.

Please don't message me asking for more details, I try to answer on the forum as best I can.