<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="Avalon_prompt_v2"
   author="Matt Adcock"
   id="053e0976013412f26cf9a395"
   language="Lua"
   purpose="A prompt replacement using a miniwindow for the mud Avalon"
   date_written="2011-05-04 15:12:17"
   requires="4.40"
   version="2.1"
   save_state="y"
   >
<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>
  <trigger
   custom_colour="6"
   enabled="y"
   group="addAffliction"
   match="^Why\, you suddenly ask\, does the world act so unfairly towards you\!$"
   regexp="y"
   sequence="100"
   name="aff_despair"
   script="addAffliction"
  >
  </trigger>
  <trigger
   custom_colour="2"
   enabled="y"
   group="addAffliction"
   match="^Your stomach is struck by violent cramps\.$"
   regexp="y"
   sequence="100"
   name="aff_vomitting"
   script="addAffliction"
  >
  </trigger>
  <trigger
   custom_colour="4"
   enabled="y"
   group="cureAffliction"
   match="^The megillos herb banishes the thoughts of tragedy from your mind\.$"
   regexp="y"
   sequence="100"
   name="cure_despair"
   script="cureAffliction"
  >
  </trigger>
    <trigger
   custom_colour="4"
   enabled="y"
   group="cureAffliction"
   match="^You are cured of vomitting\.$"
   regexp="y"
   sequence="100"
   name="cure_vomitting"
   script="cureAffliction"
  >
  </trigger>
</triggers>

<aliases>
  <alias
   script="clearAfflictions"
   match="^clearaffs$"
   enabled="y"
   regexp="y"
   sequence="100"
  >
  </alias>
</aliases>

<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",
}

-- afflictions - put them here in the order you want them displayed
affs = { ['despair'] = false, ['vomitting'] = false, ['anorexia'] = false }

-- colour coded by cure but change however you like
-- #FF80C0 - pink - megillos
-- #FF80C0 - yellow - allheale
affs_colours = { ['despair'] = "#FF80C0", ['vomitting'] = "#FFFF80", ['anorexia'] = "#FF80C0" }

-- separate multiple commands by ; or use aliases
affs_cures = { ['despair'] = "light pipe megillos;smoke pipe megillos", ['vomitting'] = "sip allheale potion", ['anorexia'] = "light pipe megillos;smoke pipe megillos" }

-- colour code exits 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" }

function OnPluginPacketReceived(sText)    
    -- do we have a line carried over from the previous packet?
    if partial ~= nil then
        -- prepend it to our packet
        sText = partial .. sText
        partial = nil
    end
    
    -- strip linefeeds
    sText = sText:gsub('\r', "")
    
    -- add a placeholder for double newlines
    sText = sText:gsub('\n\n', '\248\n')
    
    -- 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
        partial = 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)
        -- skip non printing lines and blank lines, sub back the placeholder for double newline
        if printable and printable ~= "" then
            printable = printable:gsub('\248', '\n')
            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
        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 = 350 -- distance from left 
            local sep = 0 -- distance separating each element
            -- clear any existing text
            WindowRectOp (win, 2, x, 25, x+240, 0, ColourNameToRGB('black'))
            sep = WindowText (win, "f8", "Exits:",
                    x, 28, 0, 0, 
                    ColourNameToRGB ("silver"), false) 
            x = x + sep + 8
            -- 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
                    send = 'open ' .. dirs[ex] .. ' door'
                else
                    send = dirs[ex]
                end
                sep = makeLink (dirs[ex], send, "f9", ColourNameToRGB (doors[d]), x, 25, 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"
            -- trim the terrain
            str = temp[3]:gsub("^%s*(.-)%s*$", "%1")
            -- trim the room name
            name = temp[1]:gsub("^%s*(.-)%s*$", "%1")
            -- build the string of name, area, terrain
            local text = name .. "  [ " .. temp[2] .. " - " .. str .. " ]"
            local x = 640
            -- clear any existing text
            WindowRectOp (win, 2, x, 25, 0, 0, ColourNameToRGB('black'))
            -- draw it to screen
            width = WindowText (win, "f8", text,
                    x, 27, 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
        elseif k == 'def' then
            -- handle all def codes - balance, eq, etc
            for c,m in v:gmatch('(%a+) (%a+)') do
                if c == 'right' then
                    if m == 'off' then
                        armBalanceR = false
                        armBalance = false
                    elseif m == 'on'then
                        armBalanceR = true
                        if armBalanceL == true then
                            armBalance = true
                        end
                    end               
                elseif c == 'left' then
                    if m == 'off' then
                        armBalanceL = false
                        armBalance = false
                    elseif m == 'on' then
                        armBalanceL = true
                        if armBalanceR == true then
                            armBalance = true
                        end    
                    end        
                elseif c == 'noequilibrium' then
                    if m == 'off' then
                        eqBalance = false
                    elseif m == 'on' then
                        eqBalance = true
                    end
                end
            end
            doBalances ()
        end    
        -- handle more server codes here?
        -- ###def cloak on
            -- diffusion cloak
        -- ###def cloak off
            -- lost diffusion cloak
        -- ###time 1
            -- hour, 1 is dawn
        -- ###date 12 3 1234
            -- date day month year
        -- ###def noequilibrium off
            -- eq off balance, not sent when using items, use prompt instead
        -- ###def noequilibrium on
            -- eq on balance
        -- ###bleeding 0
            -- bleeding amount
        -- ###gold 100
            -- amount of gold in inventory, sent on change, check if works with pilfer?
        -- ###rank 11
            -- experience level rank change
        -- ###lessons 100
            -- lesson total, sent when new lesson gained
        
        -- 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+)({?%[?%(?%-%)?%]?}?) \255\249$') 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 = 180 -- starting distance from left 
        local sep = 0 -- distance separating each element
        -- clear any existing text
        WindowRectOp (win, 2, x, 25, x+160, 0, ColourNameToRGB('black'))
        -- draw each letter in the prompt
        for d in s:gmatch('(.)') do
            sep = WindowText (win, "f9", d,
                x, 27, 0, 0,
                ColourNameToRGB ("white"), false)
            x = x + sep + 5 -- move left by width of previous text + 4
        end
        -- add the final (-)
        WindowText (win, "f9", p,
                x, 27, 0, 0,
                ColourNameToRGB ("white"), false)
        WindowShow (win, true)
        -- check for specifics here
        if string.find(prompt, 'e', 1, true) then
            eqBalance = true
        else
            eqBalance = false
        end
        if string.find(prompt, 'p', 1, true) then
            sipBalance = false
        else
            sipBalance = true
        end
        if string.find(prompt, 'h', 1, true) then
            ahBalance = false
        else
            ahBalance = true
        end
        if string.find(prompt, 't', 1, true) then
            herbBalance = false
        else
            herbBalance = true
        end
        -- draw the balance gauges
        doBalances ()
        -- omit prompt, or if you prefer a blank line return " " instead of nil
        return nil
    end
    -- line wasn't a prompt after all?
    return line
end -- function parsePrompt    

function addAffliction (name, line, wildcards)
    -- called by affliction message triggers
    n = name:gsub('aff_', "")
    affs[n] = true
    doAfflictions ()
end -- function addAffliction

function cureAffliction (name, line, wildcards)
    -- called by cure message triggers
    n = name:gsub('cure_', "")
    affs[n] = false
    doAfflictions ()
end -- function cureAffliction

function clearAfflictions (name, line, wildcards)
    -- clear all our afflictions
    for k,v in pairs(affs) do
        affs[k] = false
    end
    doAfflictions ()
    Note ("Afflictions cleared.")
end -- function clearAfflictions

function doAfflictions ()
    local sep = 0
    local x = 350
    local top = 3
    -- clear existing affs display
    WindowRectOp (win, 2, x, top, x+200, 25, ColourNameToRGB('black'))
    for k,v in pairs(affs) do 
        -- print out the ones that we have
        if v == true then
            -- make the name a clicky to do the cure
            sep = makeLink (k, affs_cures[k], "f7", ColourNameToRGB (affs_colours[k]), x, top, affs_cures[k])
            x = x + sep + 5 -- move left by width of previous text + 4
        end    
    end
end -- function doAfflictions
            
function doManaGauge ()
    -- clear existing bar
    WindowRectOp (win, 2, 170, 0, 330, 25, ColourNameToRGB('black'))
    -- 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, 175, 5, 150, 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,
        180, 7, 0, 0, 
        ColourNameToRGB ("white"), false)
    -- calculate percentage and draw it on gauge
    percentMana = (currMana / maxMana) * 100
    WindowText (win, "f7", math.floor(percentMana+0.5) .. '%',
        225, 7, 0, 0, 
        ColourNameToRGB ("white"), false)    
    -- draw the change from previous
    change = currMana - prevMana
    if change > 0 then
        change = '+' .. change
    end
    -- align this to the right of the window
    local x = 320
    x = x - WindowTextWidth (win, "f7", change)
    WindowText (win, "f7", change,
        x, 7, 0, 0,
        ColourNameToRGB ("white"), false)
    WindowShow (win, true)
end -- function doManaGauge
    
function doHealthGauge ()
    -- clear existing bar and label
    WindowRectOp (win, 2, 0, 0, 155, 25, ColourNameToRGB('black'))
    -- colour gauge red if under half
    if currHealth / maxHealth < 0.5 then
        healthColour = "red"
    else
        healthColour = "green"
    end    
    gauge (win, "Health:", currHealth, maxHealth, 5, 5, 150, 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,
        10, 7, 0, 0, 
        ColourNameToRGB ("white"), false)
    -- calculate percentage and draw it on gauge    
    percentHealth = math.floor(((currHealth / maxHealth) * 100) + 0.5)
    WindowText (win, "f7", percentHealth .. '%',
        55, 7, 0, 0, 
        ColourNameToRGB ("white"), false)     
    -- draw the change from previous
    change = currHealth - prevHealth
    if change > 0 then
        change = '+' .. change
    end
    -- align this to the right of the window
    local x = 150
    x = x - WindowTextWidth (win, "f7", change)
    WindowText (win, "f7", change,
        x, 7, 0, 0,
        ColourNameToRGB ("white"), false) 
    WindowShow (win, true)
end --function doHealthGauge

function makeLink (label, send, 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, label)
  local bottom = top + height

  -- add the hotspot
  WindowAddHotspot(win, send,  
                   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, label, left, top, right, bottom, colour)               
end -- makeLink  

function mouseup (flags, hotspot_id)
    Execute (hotspot_id) -- the id is the 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")
    -- get mud output without wrapping
    Send ("WIDTH 255")
    -- set the prompt
    Send ("FULLPROMPT INFO")
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 doBalances ()
    -- draw a series of balance indicators
    local top = 27
    local width = 20
    local height = 20
    local x = 7
    local spacing = 12
    local colour = "black"
     -- clear existing gauges
    WindowRectOp (win, 2, x, 25, x+150, 0, ColourNameToRGB(colour))
    -- armBalance
    if armBalance then
        colour = "orange"
    else
        colour = 'black'
    end
    -- draw the indicator
    WindowCircleOp (win, miniwin.circle_round_rectangle, -- round rectangle
                x, top, x+width, top+height,                      -- Left, Top, Right, Bottom
                ColourNameToRGB("silver"), miniwin.pen_solid, 1,          -- pen width 2
                ColourNameToRGB(colour), miniwin.brush_solid,  -- brush
                2,   -- width of the ellipse used to draw the rounded corner
                2)   -- height of the ellipse used to draw the rounded corner
    x = x + width + spacing
    -- equilibrium
    if eqBalance then
        colour = "green"
    else
        colour = 'black'
    end
    -- draw the indicator
    WindowCircleOp (win, miniwin.circle_round_rectangle, -- round rectangle
                x, top, x+width, top+height,                      -- Left, Top, Right, Bottom
                ColourNameToRGB("silver"), miniwin.pen_solid, 1,          -- pen width 2
                ColourNameToRGB(colour), miniwin.brush_solid,  -- brush
                2,   -- width of the ellipse used to draw the rounded corner
                2)   -- height of the ellipse used to draw the rounded corner
    x = x + width + spacing           
    -- potion balance
    if sipBalance then
        colour = "blue"
    else
        colour = 'black'
    end
    -- draw the indicator
    WindowCircleOp (win, miniwin.circle_round_rectangle, -- round rectangle
                x, top, x+width, top+height,                      -- Left, Top, Right, Bottom
                ColourNameToRGB("silver"), miniwin.pen_solid, 1,          -- pen width 2
                ColourNameToRGB(colour), miniwin.brush_solid,  -- brush
                2,   -- width of the ellipse used to draw the rounded corner
                2)   -- height of the ellipse used to draw the rounded corner            
    x = x + width + spacing 
    -- allheale balance
    if ahBalance then
        colour = "cyan"
    else
        colour = 'black'
    end
    -- draw the indicator
    WindowCircleOp (win, miniwin.circle_round_rectangle, -- round rectangle
                x, top, x+width, top+height,                      -- Left, Top, Right, Bottom
                ColourNameToRGB("silver"), miniwin.pen_solid, 1,          -- pen width 2
                ColourNameToRGB(colour), miniwin.brush_solid,  -- brush
                2,   -- width of the ellipse used to draw the rounded corner
                2)   -- height of the ellipse used to draw the rounded corner            
    x = x + width + spacing  
    -- healing herb balance
    if herbBalance then
        colour = "lime"
    else
        colour = 'black'
    end
    -- draw the indicator
    WindowCircleOp (win, miniwin.circle_round_rectangle, -- round rectangle
                x, top, x+width, top+height,                      -- Left, Top, Right, Bottom
                ColourNameToRGB("silver"), miniwin.pen_solid, 1,          -- pen width 2
                ColourNameToRGB(colour), miniwin.brush_solid,  -- brush
                2,   -- width of the ellipse used to draw the rounded corner
                2)   -- height of the ellipse used to draw the rounded corner            
    x = x + width + spacing 
end -- function doBalances

function OnPluginInstall ()
    win = GetPluginID ()
    
    require "gauge"

    area = {}
    terrain = {} 

    -- initialise some values
    prevMana = 0
    prevHealth = 0
    currMana = 0
    currHealth = 0
    maxMana = 0
    maxHealth = 0
    prompt = ""
    armBalance, armBalanceL, armBalanceR = true, true, true
    eqBalance, sipBalance, ahBalance, herbBalance = true, true, true, true
    
    -- if a packet splits a line store the partial line until the next packet
    partial = nil
    
    -- set the prompt window size
    WIDTH = 1200
    HEIGHT = 50
    
    -- move the main window up to make room
    TextRectangle (3,0,0,-55,0,0,0,ColourNameToRGB ("black"),0)

    -- make miniwindow and get font info
    WindowCreate (win, 
                0, -- left
                0, -- top
                WIDTH,   -- width
                HEIGHT,     -- height
                10, -- position bottom left   
                0, -- flags    
                ColourNameToRGB ('black'))

    -- load the font at different sizes
    WindowFont (win, "f9", "Verdana", 11)
    WindowFont (win, "f8", "Verdana", 9)
    WindowFont (win, "f7", "Verdana", 8)
    
    -- try and grab the info we need from the mud
    init ()
    
    -- draw the balance gauges
    doBalances ()
    
    -- draw the affs tracker
    doAfflictions ()
    
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

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>

