Add countdown timer to text miniwindow

Posted by Nick Gammon on Thu 10 Apr 2025 09:42 PM — 7 posts, 12,030 views.

Australia Forum Administrator #0
Have a look at this page:

https://gammon.com.au/mushclient/mw_text.htm

Near the bottom is "Hints for working with text" and that shows a fairly simple bit of code for setting up a miniwindow for drawing text.

Once you have the window and font set up you basically call WindowText to display the text.

Conceptually it would be very similar to the post I just did for echoing the output window:


https://www.gammon.com.au/forum/?id=15235

The only difference would be that instead of changing the text when you type (OnPluginCommandChanged), you would change the text based on your cooldown. You could make a timer to update the window every second if the cooldown is active.

You might also change the window width (window_width) to some fixed value (eg. 50 pixels).
#1
Thanks for reply! I already have a miniwindow that I want to add the cooldown timer to. Each line in my miniwindow uses:

my_window:add_text() to display text from variables.

I'll take a look at those links and see if I can figure it out.

My issue is that I'm not even sure which lines from the action bar plugin I'd need to use.
Australia Forum Administrator #2
I've made a standalone plugin that lets you display pure text in small boxes, with a cooldown timeout.

Template:saveplugin=Cooldown_Text
To save and install the Cooldown_Text plugin do this:
  1. Copy the code below (in the code box) to the Clipboard
  2. Open a text editor (such as Notepad) and paste the plugin code into it
  3. Save to disk on your PC, preferably in your plugins directory, as Cooldown_Text.xml
    • The "plugins" directory is usually under the "worlds" directory inside where you installed MUSHclient.
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file Cooldown_Text.xml (which you just saved in step 3) as a plugin
  7. Click "Close"
  8. Save your world file, so that the plugin loads next time you open it.



<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="Cooldown_Text"
   author="Nick Gammon"
   id="2f103c42b3356ee1bcac384b"
   language="Lua"
   purpose="Shows cooldowns in a miniwindow"
   save_state="y"
   date_written="2025-04-08 11:50:44"
   requires="5.06"
   version="1.0"
   >
</plugin>

<!--  Timers  -->

<timers>
  <timer 
    script="handle_cooldowns" 
    enabled="y" second="1.00" 
    active_closed="y" >
  </timer>
</timers>

<!--  Script  -->

<script>
<![CDATA[

require "commas"  -- for convert_time

font_id = "fn"
FONT_NAME = "Sans"    -- input font name
active_windows = { }  -- our windows
MARGIN = 10  -- pixels
  
function ShowWindows ()

  top = 5
  
  for name, details in pairs (active_windows) do
    
    contents = details.contents
    if details.cooldown then
       if details.cooldown <= 0 then
         contents = contents .. " (ready)"
       else
         contents = contents .. " (wait) " .. convert_time (details.cooldown)
       end -- if ready or waiting
    end -- if it has a cooldown
    
    win = GetPluginID () .. "_" .. name
    
    -- make window so I can grab the font info
    WindowCreate (win, 0, 0, 1, 1, miniwin.pos_top_right, miniwin.create_absolute_location, 0) 

    WindowFont (win, font_id, FONT_NAME, 12, false, false, false, false, 0, 0)  -- normal  
    font_height = WindowFontInfo (win, font_id, 1)  -- height
    
    -- calculate window size
    window_height = font_height + MARGIN
    window_width  = WindowTextWidth (win, font_id, contents) + MARGIN
    
    -- work out left of window (output window width - size of window)
    left = GetInfo (281) - window_width - 5  -- position on right, slightly in from edge
    
    -- make window correct size and location
    WindowCreate (win, left, top, window_width, window_height, 
                  miniwin.pos_top_right, miniwin.create_absolute_location, ColourNameToRGB ("white"))
                  
    -- blank the window
    WindowRectOp (win, miniwin.rect_draw_edge, 0, 0, 0, 0, 
                  miniwin.rect_edge_raised, miniwin.rect_edge_at_all + miniwin.rect_option_softer_buttons)
    -- add the text
    WindowText (win, font_id, contents, MARGIN / 2, MARGIN / 2, 0, 0, ColourNameToRGB ("black"))
    -- show the window
    WindowShow (win, true)

    -- next window is further down
    top = top + window_height + 5

  end -- for each window
    
end -- ShowWindows
  
-- add a notification window
function MakeWindow (name, contents, cooldown)
  active_windows [name] = { contents = contents, cooldown = cooldown }
  ShowWindows()
end -- MakeWindow

-- call this to remove a notification window
function RemoveWindow (name)
  active_windows [name] = nil
  win = GetPluginID () .. "_" .. name
  WindowDelete (win)
  ShowWindows()
end -- RemoveWindow

-- called every second on a timer
function handle_cooldowns ()
  for name, details in pairs (active_windows) do
    if details.cooldown then
       details.cooldown = details.cooldown - 1  -- one less second
       if details.cooldown < 1 then
          details.cooldown = 0
       end -- if time is up
    end -- if has a cooldown
  end -- for each window
  ShowWindows()  
end -- function handle_cooldowns

-- remove the windows when we disconnect
function OnPluginDisconnect()
  for name, details in pairs (active_windows) do
    RemoveWindow (name)
  end -- for each window
end -- OnPluginDisconnect

]]>
</script>
</muclient>


To use it:

You can have a text box with or without a timeout. Each text box has a name (it can be anything, eg. "fireball") and then the text you want in the box (that you see). If you use the same name twice it will replace the previous box, if you use a different name then you will get extra boxes (one per name).

You add boxes to the screen with CallPlugin like this:


CallPlugin ("2f103c42b3356ee1bcac384b" , "MakeWindow" , "fireball", "Fireball Spell", 30 )


What that does is call MakeWindow inside the plugin, creates a box name "fireball" and puts the text "Fireball Spell" into the box. It has a timeout of 30 seconds in this example. It will automatically count down every second.

Another example:


CallPlugin ("2f103c42b3356ee1bcac384b" , "MakeWindow" , "rez", "Resurrection", 30 * 60 )


That would show "Resurrection" counting down for 30 minutes (30 times 60 seconds).

You can have a box without a timeout:


CallPlugin ("2f103c42b3356ee1bcac384b" , "MakeWindow" , "quest", "You have active quests")


You can remove a box:


CallPlugin ("2f103c42b3356ee1bcac384b" , "RemoveWindow" , "quest")


It's up to you to activate these windows by detecting game events with triggers or aliases.

This plugin is: now available on GitHub.


The version there splits the XML and Lua portions into different files, for better readability when using an editor that does syntax colouring (and on GitHub).
Amended on Sun 13 Apr 2025 07:48 PM by Nick Gammon
#3
Thank you for that plugin! I like the idea of having custom windows for timers as you made, that's a cool idea. I had read through Fiendish's miniwindow tutorial so I had already created the miniwindow below:


function draw_window()
require "themed_miniwindows"
my_window = ThemedTextWindow("win", 200, 200, 200, 200, "Target", "center", false, true, false))

   my_window:add_text("@gTarget: @G" .. GetVariable("target") .. " @g(@G" .. GetVariable("direction") .. "@g)")
   if GetVariable("abilitystatus") == "wait" then
      my_window:add_text("@gAbility: @R" .. GetVariable("abilityuses") .. "@g (@R" .. GetVariable("abilitystatus") .. "@g)")
   else
      my_window:add_text("@gAbility: @G" .. GetVariable("abilityuses") .. " @g(@G" .. GetVariable("abilitystatus") .. "@g)")
   end --if
end


I was already able to get your cooldown_text plugin working using triggers and aliases to accurately display the cooldown for my ability, so again, thank you!

I know all the pieces are in cooldown_text, but I'm afraid my scripting abilites are too limited to figure out how to modify those pieces so I can get the cooldown timer to show after "abilitystatus" in my miniwindow.
Amended on Fri 11 Apr 2025 07:30 PM by Aardwanderer
#4
I'm happy to say I got the cooldown_text plugin to sit on top of my miniwindow by modifying:

  top = 271 -- pixels down from top

and

    left = GetInfo (281) - window_width - 675  -- position on right, slightly in from edge

Now I'm looking at the code to see which parts I need to pull out, so I can get the cooldown in my miniwindow. It seems I need the following:

Necessary, but no need to modify.
  <timer 
    script="handle_cooldowns" 
    enabled="y" second="1.00" 
    active_closed="y" >
  </timer>

I'm wondering if I could use the following, or if I need to write my miniwindow in another way:

my_window:add_text()

To put the below code into my miniwindow after appropriate modifications.

Necessary, but modifications needed.

    contents = details.contents
    if details.cooldown then
       if details.cooldown <= 0 then
         contents = contents .. " (ready)"
       else
         contents = contents .. " (wait) " .. convert_time (details.cooldown)
       end -- if ready or waiting
    end -- if it has a cooldown

I know I will need to incorporate the following as well, but I need to pare down what I don't need.

-- add a notification window
function MakeWindow (name, contents, cooldown)
  active_windows [name] = { contents = contents, cooldown = cooldown }
  ShowWindows()
end -- MakeWindow

-- called every second on a timer
function handle_cooldowns ()
  for name, details in pairs (active_windows) do
    if details.cooldown then
       details.cooldown = details.cooldown - 1  -- one less second
       if details.cooldown < 1 then
          details.cooldown = 0
       end -- if time is up
    end -- if has a cooldown
  end -- for each window
  ShowWindows()  
end -- function handle_cooldowns


Does it look like I'm on the right track? And would it work to use: my_window:add_text()
or is a miniwindow re-write in order?
Amended on Sat 12 Apr 2025 03:48 PM by Aardwanderer
Australia Forum Administrator #5
I'm not clear what you are trying to achieve.

Why not just call the plugin from our other plugin, as I showed above?
#6
I'm happy to say I was able to add the timer to my miniwindow! I have to do more testing, but I think I have it working. I had to change my triggers to send to a function that set the variables, and I needed the following:

function OnPluginInstall ()
    cooldown = 490
    Send("recovery")
end
  <timer 
    script="handle_cooldowns" 
    enabled="y" second="1.00" 
    active_closed="y" >
  </timer>

  <trigger
   enabled="y"
   match="^Your ability has been used\.$"
   regexp="y"
   script="abilityStat"
   sequence="100"
  >
  </trigger>

  <trigger
   enabled="y"
   group="archery"
   match="^You do not have any recovery times.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>SetVariable("volleystat", "ready")draw_window()
  </trigger>

  <trigger
   enabled="y"
   match="^\#\# You may now use the ability again\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>SetVariable("abilitystat", "ready")cooldown = 0 draw_window()
  </trigger>

function abilityStat (name, line, wildcards) --this function does not set the cooldown to the correct time, but does send the correct cooldown time to screen
   SetVariable("abilitystat", "wait")
   cooldown = 490
   Note(cooldown) -- to verify trigger is firing
end -- function abilityStat

local cooldown = 490

local uses = GetVariable("uses")

function draw_window()
require "themed_miniwindows"
my_window = ThemedTextWindow("qtwin", 200, 200, 200, 200, "Target", "center", true, true, false, true, true, false, false, "Courier", 10, "Courier", 10)

   my_window:add_text("@gTarget: @G" .. GetVariable("target") .. " @g(@G" .. GetVariable("target_direc") .. "@g)")
   if GetVariable("abilitystat") == "wait" then
      my_window:add_text("@gAbility: @R" .. GetVariable("uses") .. "@g (@R" .. GetVariable("abilitystat") .. "@g) " .. convert_time(cooldown))
   else
      my_window:add_text("@gAbility: @G" .. GetVariable("uses") .. " @g(@G" .. GetVariable("abilitystat") .. "@g)")
   end --if
end -- draw_window()

Modified functions:
-- called every second on a timer
function handle_cooldowns ()
    if cooldown then
       cooldown = cooldown - 1  -- one less second
       if cooldown < 1 then
          cooldown = 0
       end -- if time is up
    end -- if has a cooldown
    draw_window()
end -- function handle_cooldowns


I know what I have done is a little messsy and probably inefficient, so any tips or pointers would be welcomed.

Also, now that I've added the timer, my miniwindow flickers fairly often. Is that due to using 'my_window:add_text()'? How can I change or what function to create the miniwindow should I use, so it doesn't flicker so much?

Thanks!