<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="Avalon_prompt"
   author="Matt Adcock"
   id="053e0976013412f26cf9a395"
   language="Lua"
   purpose="A prompt replacement using a miniwindow for the mud Avalon"
   date_written="2011-03-25 19:18:17"
   requires="4.40"
   version="1.0"
   >
<description trim="y">
<![CDATA[
Replaces the prompt with a bar below the output window containing
health and mana gauges, prompt letters, current exits and the room
name.

Gauges display the current and last change values in them. Exits
are colour coded for closed and open doors and are clickable
]]>
</description>

</plugin>

<!--  Triggers  -->

<triggers>
  <trigger
   enabled="y"
   match="^You are most welcome to the Land of Avalon\.$"
   regexp="y"
   script="init"
   sequence="100"
  >
  </trigger>
  
  <trigger
   enabled="y"
   match="^Health\: (\d+) out of (\d+)(.*?)Mana\: (\d+) out of (\d+)$"
   regexp="y"
   script="setmax"
   sequence="100"
  >
  </trigger>
</triggers>
  
<script>
<![CDATA[
-- table of codes used by server to represent each direction
dirs = {
    no = "N",
    ne = "NE",
    ea = "E",
    se = "SE",
    so = "S",
    sw = "SW",
    we = "W",
    nw = "NW",
    up = "U",
    ['do'] = "D",
    ['in'] = "IN",
    ou = "OUT",
}

-- 3 is a closed door, 1 is an open door and 0 is no door. Haven't seen 2 yet!
doors = { ['3']="red", ['2']="blue", ['1']="green", ['0']="white" }

-- colour certain prompt letters
prompt_colours = { p = "red", y = "orange", z = "orange", h = "red", w = "red", a = "green"}

function OnPluginPacketReceived(sText)    
    -- do we have a line carried over from the previous packet?
    if partial then
        -- prepend it to our packet
        sText = partial .. sText
        partial = nil
    end
    
    -- strip linefeeds, I think Avalon always sends \r\n but best to be on the safe side
    sText = sText:gsub('\r', "")
    
    -- add a newline to any GA so we can split on them
    sText = sText:gsub('\249', '\249\n')
    
    -- split the packet on newlines
    lines = Split(sText, '\n')
    
    -- if it doesn't end with a newline, save the last line for the next packet
    if sText:sub(-1) ~= '\n' then
        -- remove the last line and keep it for the next packet
        partial = '\n' .. table.remove(lines)
    end    
    
    -- process what we've got and build a new table of lines to print
    local output = {}
    for i,l in ipairs(lines) do
        -- ParseLine returns the line to print, or nil
        printable = ParseLine(l)
        -- omit non printing lines and skip any blank lines
        if printable and printable ~= "" then
            table.insert(output, printable)
        end
    end
    
     -- return to mushclient as a newline separated string
    if #output ~= 0 then
        return table.concat(output, '\n') .. '\n'
    else
        -- if table is empty the above sends a newline so send nothing instead
        return ""
    end

end -- function OnPluginPacketReceived

function ParseLine(line)
    -- return line to display or nil if it's to be omitted
    -- if line ends in GA we'll assume it's a prompt
    if line:sub(-1) == '\249' then
        return parsePrompt(line)
    end
    -- check for ### lines from server
    for k,v in line:gmatch('###(%a+) ([%d%a%s"]+)') do 
        -- handle each ### command here
        if k == "exits" then
            -- draw a list of exits, clickable to move in that direction
            local x = 510 -- distance from left 
            local sep = 0 -- distance separating each element
            -- clear any existing text
            WindowRectOp (win, 2, x, 0, 0, 0, ColourNameToRGB('black'))
            sep = WindowText (win, "f7", "Exits:",
                    x, 6, 0, 0, 
                    ColourNameToRGB ("silver"), false) 
            x = x + sep + 10
            -- clear existing hotspots
            WindowDeleteAllHotspots (win)
            -- iterate each direction and door code and make a clicky link
            for ex,d in v:gmatch('(..)(.)') do
                if d == '3' then -- closed door
                    id = 'open ' .. dirs[ex] .. ' door'
                else
                    id = dirs[ex]
                end
                sep = make_link (dirs[ex], id, "f9", ColourNameToRGB (doors[d]), x, 4, id)
                x = x + sep + 8
            end
            WindowShow (win, true)
        elseif k == 'brief' then
            -- ###brief Name of Room          "Area" Terrain
            temp = Split(v, '"')
            -- [1] = "Name of Room", [2] = "Area", [3] = "Terrain"
            -- draw the room name on the right of the bar 
            local x = 820 -- distance from left 
            WindowRectOp (win, 2, x, 0, 0, 0, ColourNameToRGB('black'))
            width = WindowText (win, "f7", temp[1],
                    x, 7, 0, 0, 
                    ColourNameToRGB ("yellow"),  false) 
        elseif k == 'health' then
            -- ###health current max
            for c,m in v:gmatch('(%d+) (%d+)') do
                -- set health vars
                prevHealth = currHealth
                currHealth = tonumber(c)
                maxHealth = tonumber(m)
            end
            if currHealth ~= prevHealth then
                -- redraw the gauge if health has changed
                doHealthGauge ()
            end
        elseif k == 'mana' then
            -- ###mana current max
            for c,m in v:gmatch('(%d+) (%d+)') do
                -- set mana vars
                prevMana = currMana
                currMana = tonumber(c)
                maxMana = tonumber(m)
            end
            if currMana ~= prevMana then
                -- redraw the gauge if mana has changed
                doManaGauge ()
            end
        -- handle more server codes here?
        end
        -- omit all ### lines from output
        return nil
    end
    
    -- normal line, print it
    return line
    
end -- function OnPluginLineReceived  

function parsePrompt(line)
    -- strip colour sequences
    line = line:gsub('\27%[%d+m', "")
    -- grab health, mana and prompt characters
    for h,m,s,p in line:gmatch('(%d+)h, (%d+)m (%a+)({?%[?%(?%-%)?%]?}?)') do
        -- we get health and mana from ### so ignore them in the prompt
        -- store it in prompt string incase we need it somewhere else
        prompt = s
        -- process prompt string
        local x = 350 -- starting distance from left 
        local sep = 0 -- distance separating each element
        -- clear any existing text
        WindowRectOp (win, 2, x, 0, x+160, 0, ColourNameToRGB('black'))
        sep = WindowText (win, "f7", "P:",
                x, 7, 0, 0,
                ColourNameToRGB ("white"),false)
        x = x + sep + 6 -- move left by width of previous text + 6     
        for d in s:gmatch('(.)') do
            sep = WindowText (win, "f9", d,
                x, 4, 0, 0,
                ColourNameToRGB (prompt_colours[d] or "white"), false)
            x = x + sep + 3 -- move left by width of previous text + 3
        end
        -- add the final (-)
        WindowText (win, "f9", p,
                x, 4, 0, 0,
                ColourNameToRGB ("white"), false)
        WindowShow (win, true)
        -- could check for specifics here, for example
        --if string.find(prompt, 'p', 1, true) then
        --    Note ('Potion balance')
        --end
        -- omit prompt
        return nil
    end
end -- function parsePrompt    

function doManaGauge ()
    -- clear existing bar and label
    WindowRectOp (win, 2, 173, 0, 330, 0, ColourNameToRGB('black'))
    -- draw the label
    WindowText (win, "f7", "M:",
        179, 7, 0, 0, 
        ColourNameToRGB ("white"), false) 
    -- colour gauge red if under half
    if currMana / maxMana < 0.5 then
        manaColour = "red"
    else
        manaColour = "blue"
    end
    -- draw the gauge
    gauge (win, "Mana:", currMana, maxMana, 200, 5, 140, 17, 
        ColourNameToRGB(manaColour), ColourNameToRGB ('black'),
        0, ColourNameToRGB ('silver'), -- no vertical lines on gauge
        ColourNameToRGB ('silver'),
        0)
    -- draw the current number on the gauge
    WindowText (win, "f7", currMana,
        205, 7, 0, 0, 
        ColourNameToRGB ("white"), false)
    -- draw the change from previous
    change = currMana - prevMana
    if change > 0 then
        change = '+' .. change
    end
    WindowText (win, "f7", change,
        293, 7, 0, 0,
        ColourNameToRGB ("white"), false)
    WindowShow (win, true)
end -- function doManaGauge
    
function doHealthGauge ()
    -- clear existing bar and label
    WindowRectOp (win, 2, 4, 0, 160, 0, ColourNameToRGB('black'))
    -- draw the health bar and label
    WindowText (win, "f7", "H:",
        9, 7, 0, 0, 
        ColourNameToRGB ("white"), false)
    -- colour gauge red if under half
    if currHealth / maxHealth < 0.5 then
        healthColour = "red"
    else
        healthColour = "green"
    end    
    gauge (win, "Health:", currHealth, maxHealth, 30, 5, 140, 17, 
       ColourNameToRGB ('green'), ColourNameToRGB ('black'),
       0, ColourNameToRGB ('silver'), -- no vertical lines on gauge
       ColourNameToRGB ('silver'),
       0)
    -- draw the current number on the gauge
    WindowText (win, "f7", currHealth,
        35, 7, 0, 0, 
        ColourNameToRGB ("white"), false)
    -- draw the change from previous
    change = currHealth - prevHealth
    if change > 0 then
        change = '+' .. change
    end
    WindowText (win, "f7", change,
        123, 7, 0, 0,
        ColourNameToRGB ("white"), false) 
    WindowShow (win, true)
end --function doHealthGauge

function make_link (text, id, font_id, colour, left, top, hint)
  -- work out text rectangle size
  local height = WindowFontInfo (win, font_id, 1)  
  local right = left + WindowTextWidth (win, font_id, text)
  local bottom = top + height

  -- add the hotspot
  WindowAddHotspot(win, id,  
                   left, top, right, bottom, 
                   "", -- mouseover (do nothing)
                   "", -- cancelmouseover (do nothing)
                   "", -- mousedown (do nothing)
                   "", -- cancelmousedown (do nothing)
                   "mouseup", 
                   hint,    -- hint text if they hover over it              
                   miniwin.cursor_hand, 0)
  

  -- draw the linked text               
  return WindowText (win, font_id, text, left, top, right, bottom, colour)               
end -- make_hyperlink    

function mouseup (flags, hotspot_id)
    Send (hotspot_id) -- the id is the movement command we want to send
end -- function mouseup   
     
function init (name, line, wildcards)
    -- tell the server to begin sending ### messages
    Send ("JAVA ON 2")
    -- not sure if the java data disables colour
    Send ("ANSI ON")
    -- grab current and max health and mana
    Send ("QS")
end -- function init

function setmax (name, line, wildcards)
    -- set some initial values
    currHealth, maxHealth, currMana, maxMana = tonumber(wildcards[1]), tonumber(wildcards[2]), tonumber(wildcards[4]), tonumber(wildcards[5])
    
    -- draw the bars
    doHealthGauge ()
    doManaGauge ()
end -- function setmax

function OnPluginInstall ()
    win = GetPluginID ()
  
    --require "split"
    require "gauge"
    
    -- initialise some values
    prevMana = 0
    prevHealth = 0
    currMana = 0
    currHealth = 0
    maxMana = 0
    maxHealth = 0
    prompt = ""
    -- if a packet splits a line store the partial line until the next packet
    partial = nil
    
    -- move the main window up
    TextRectangle (0,0,0,-30,10,0,0,ColourNameToRGB ("black"),0)

    -- make miniwindow and get font info
    WindowCreate (win, 
                0, -- left
                0, -- top
                1200,   -- width
                30,     -- height
                10, -- position bottom left   
                0, -- flags    
                ColourNameToRGB ('black'))

    -- load the font at two sizes
    WindowFont (win, "f9", "Verdana", 11)
    WindowFont (win, "f7", "Verdana", 9)
    
    -- try and grab the info we need from the mud
    init ()
    
end -- function OnPluginInstall

function OnPluginDisable ()
    WindowShow (win, false)
end -- OnPluginDisable

function OnPluginEnable ()
    WindowShow (win, true)
    -- grab the info we need from the mud
    init ()
end -- OnPluginEnable

-- I have this in a separate file, split.lua, but I've included it here
function Split(str, delim, maxNb)
    -- Eliminate bad cases...
    if string.find(str, delim) == nil then
        return { str }
    end
    if maxNb == nil or maxNb < 1 then
        maxNb = 0    -- No limit
    end
    local result = {}
    local pat = "(.-)" .. delim .. "()"
    local nb = 0
    local lastPos
    for part, pos in string.gmatch(str, pat) do
        nb = nb + 1
        result[nb] = part
        lastPos = pos
        if nb == maxNb then break end
    end
    -- Handle the last field
    if nb ~= maxNb then
        result[nb + 1] = string.sub(str, lastPos)
    end
    return result
end
]]>
</script> 

</muclient>

