	<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Saturday, April 11, 2009, 9:55 PM -->
<!-- MuClient version 4.40 -->

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

<!--
-->

<muclient>
<plugin
   name="MapWindow"
   author="Soludra"
   id="37859615f6c4f74b6872f8e6"
   language="Lua"
   purpose="Replaces MAP and adds extra functionality"
   save_state="y"
   date_written="2009-04-11 21:52:18"
   requires="4.40"
   version="1.0"
   >
<description trim="y">
<![CDATA[
MapWindow v1.0
By Soludra Ar'thela

This plugin is best used alongside Trevize's ATCP plugin. Its messages
allow the MapWindow to update every time you move, even by summons or
other "nonstandard" means of egress.

Commands:
MAP RADIUS <1-5>
-> Sets the MAP radius, which affects the mapwindow size.
-> MAP WIDTH and MAP HEIGHT also work as expected.

MAP MOVE
-> Displays the current map-window location

MAP MOVE <x> <y>
-> Positions the map window in a specific location on the screen.
-> 0 0 is the top-left corner.

MAP FONTSIZE <font size>
-> Sets the size each symbol takes up.
-> Default is 10. Is not yet saved between sessions.

MAP PARSE
-> Updates the map with MAP, gagging the output.
-> You shouldn't need to use this yourself unless you
-> don't have the ATCP plugin installed.

]]>
</description>

</plugin>


<!--  Standard constants -->
<include name="constants.lua"/>

<!--  Triggers  -->

<triggers>
  <trigger match="^(?:You see exits leading|You see a single exit leading|There are no obvious exits\.)"
           regexp="y" name="automap_check"
           keep_evaluating="y" send_to="12" sequence="100">
    <send>
      Execute("map parse")
    </send>
  </trigger>

  <trigger match="^(?:You are packed much too tightly into the earth to do that\.|You are asleep and can do nothing\. WAKE will attempt to wake you\.)$"
           regexp="y" name="error"
           omit_from_log="y" omit_from_output="y"
           send_to="14" sequence="100">
    <send>
      EnableGroup("mapbegin", false)
      EnableTrigger("mapsize", false)
      EnableTrigger("error", false)

      EnableTrigger("prompt", true)
    </send>
  </trigger>

  <trigger match="^This room has not been mapped\.$"
           regexp="y" group="mapbegin" name="notmapped"
           omit_from_log="y" omit_from_output="y"
           send_to="14" sequence="100">
    <send>
      EnableGroup("mapbegin", false)
      EnableTrigger("prompt", true)
      map:ClearGrid()
    </send>
  </trigger>

  <trigger match="^\-+(?: Area (?:\d+): [A-Za-z'&quot;-_ ]+ )?\-+$"
           regexp="y" group="mapbegin" name="arealine"
           omit_from_log="y" omit_from_output="y"
           send_to="14" sequence="100">
    <send>
      EnableGroup("mapbegin", false)
      EnableGroup("parsemap", true)
      grid = {}
    </send>
  </trigger>

  <trigger match="^.*$"
           regexp="y" group="parsemap" name="maprows"
           omit_from_log="y" omit_from_output="y"
           script="ParseLine" sequence="101"
  />

  <trigger match="^\-+(?: [A-Za-z'&quot;-_ ]+ )?(?:\-+)?(?: -?\d+:-?\d+:-?\d+ )\-+$"
           regexp="y" group="parsemap" name="coordline"
           omit_from_log="y" omit_from_output="y"
           send_to="14" sequence="100">
    <send>
      EnableGroup("parsemap", false)
      EnableTrigger("prompt", true)
      map:ClearGrid()
      map:DrawGrid(grid)
    </send>
  </trigger>

  <trigger match="^(?:\d+h, )?(?:\d+m,? )?(?:\d+e,? )?(?:\d+w,? )?c?e?x?k?d?b?@? ?(?:Vote)?-"
           regexp="y" name="prompt"
           omit_from_log="y" omit_from_output="y"
           send_to="14" sequence="100">
    <send>
      EnableTrigger("prompt", false)

      mapping = false
      if not gagging then
        gagging = true
        GagOption(true)
      end
    </send>
  </trigger>

  <trigger match="^(?:Your map view is set to (\d) by (\d)\.|You cannot set the radius greater than 5\.|Usage:)$"
           regexp="y" group="mapsize"
           send_to="12" sequence="100">
    <send>
      EnableTrigger("mapsize", false)
      EnableTrigger("error", false)

      if tonumber("%1") and tonumber("%2") then
        map:MapSize(tonumber("%1"), tonumber("%2"))
        Execute("map parse")
      end
    </send>
  </trigger>
</triggers>

<!--  Aliases  -->

<aliases>
  <alias match="^\s*MAP(?:\s+(PARSE))?\s*$" regexp="y" enabled="y" ignore_case="y"
         omit_from_command_history="y" omit_from_log="y"
         script="MapParse" sequence="100"
  />

  <alias match="^\s*MAP\s+(\S+)((?:\s+\S+)*)\s*$" regexp="y" enabled="y" ignore_case="y"
         omit_from_log="y" omit_from_output="y"
         script="MapAlias" sequence="101"
  />
</aliases>


<!--  Script  -->

<script>
<![CDATA[
Window = {
  new = function(name, x, y, width, height)
    local o = {}
    setmetatable(o, Window)
    Window.__index = Window

    o.name = name
    o.x = x or 0
    o.y = y or 0
    o.width = width or 5
    o.height = height or 5
    o.grid = {}

    -- Dummy window to load font
    WindowCreate(o.name, 0, 0, 1, 1, 0, 0, 0)

    o:FontSize(10) -- set size and reload map

    return o
  end,

  -- resizes the map
  MapSize = function(self, width, height)
                    -- (3+width*4) px = number of cells
                    --       width(5) = cells(23)

    self.width = width
    self.winwidth = self.textwidth*(3+width*4) + 2
    self.height = height
    self.winheight = self.textheight*(3+height*4) + 2

    WindowCreate(self.name, self.x, self.y, self.winwidth, self.winheight, 6, 2, 0x000000)
    WindowShow(self.name, true)
  end,

  FontSize = function(self, size)
    self.fontsize = size
    WindowFont(self.name, "f", "Lucida Console", self.fontsize, false, false, false, false, 1, 0)

    self.textwidth = WindowTextWidth(self.name, "f", "#")
    self.textheight = WindowFontInfo(self.name, "f",  1)

    self:MapSize(self.width, self.height)
    self:DrawGrid(self.grid)
  end,

  MoveWindow = function(self, x, y)
    self.x = x
    self.y = y
    WindowPosition(self.name, x, y, 6, 2)
  end,

  DrawCell = function(self, cell, x, y)
    -- Index into the appropriate cell, and add for the window edge
    local x = self.textwidth*x + 1
    local y = self.textheight*y + 1

    WindowText(self.name, "f", cell.char, x, y, 0, 0, cell.style, false)
  end,

  DrawRow = function(self, line, row)
    for i = 1, math.min(table.getn(line) or 23) do
      self:DrawCell(line[i] or " ", i-1, row)
    end
  end,

  DrawGrid = function(self, grid)
    self:ClearGrid()

    -- store this grid
    self.grid = grid

  -- Draws gridlines
--[[
    for i = 1, 23 do
      WindowLine(self.name, 0, i*self.textheight, self.winwidth, i*self.textheight, 0x666666, 0, 1)
    end
    for i = 1, 23 do
      WindowLine(self.name, 3+i*self.textwidth, 0, 3+i*self.textwidth, self.winheight, 0x666666, 0, 1)
    end
--]]

    for i = 1, 23 do
      self:DrawRow(grid[i] or {}, i-1)
    end
  end,

  ClearGrid = function(self)
    self.grid = {}

    WindowRectOp(self.name, 2, 0, 0, 0, 0, 0x000000)

    -- Draws borders
--[[
    WindowLine(self.name, 0, 0, 0, self.winheight-1, 0x666666, 0, 1)
    WindowLine(self.name, 0, 0, self.winwidth-1, 0, 0x666666, 0, 1)
    WindowLine(self.name, 0, self.winheight-1, self.winwidth-1, self.winheight-1, 0x666666, 0, 1)
    WindowLine(self.name, self.winwidth-1, 0, self.winwidth-1, self.winheight-1, 0x666666, 0, 1)
--]]
  end,
}

-- translate MAP markers
roomcode = {["S"] = "$", -- shop
            ["+"] = "@", -- current location
            ["$"] = "B", -- bank
            ["N"] = "N", -- newsroom
            ["P"] = "P", -- post office
            ["@"] = "W", -- wilderness exit
           }

-- color-code "special" rooms
roomcolor = {["@"] = 0xFFFFFF, -- white
             ["B"] = 0xFF8800, -- deep blue
             ["N"] = 0xFF8800, -- deep blue
             ["P"] = 0xFF8800, -- deep blue
             ["W"] = 0x0000FF, -- red
            }

-- prevents two map parses from going off at the same time
-- otherwise, one or more MAPs wouldn't be gagged
mapping = false

-- tells the ending trigger whether to re-enable gagging
-- This allows MAP to be parsed but still be shown, but
-- MAP PARSE to be parsed without being shown
gagging = true


string.split = function(line, delim)
  local left = line:find(delim)
  if left == nil then
    return {line}
  end
  left = left + 1

  local right = 1
  local tokens = {}

  while left <= line:len() do
    right = line:find(delim, left) or line:len()+1

    local token = line:sub(left, right-1)
    if token ~= "" then
      table.insert(tokens, token)
    end

    left = right+1
  end

  return tokens
end


OnPluginInstall = function()
  local x = GetVariable("x") or 0
  local y = GetVariable("y") or 0
  local width = GetVariable("width") or 5
  local height = GetVariable("height") or 5

  map = Window.new("MapWindow", x, y, width, height)

  if IsConnected() then
    Execute("map parse")
  end
end

OnPluginSaveState = function()
  SetVariable("x", map.x)
  SetVariable("y", map.y)
  SetVariable("width", map.width)
  SetVariable("height", map.height)
end

OnPluginBroadcast = function (msg, id, name, text)
  if (id == "431a11163c945fb1f7663d3e") and (msg == 3) then
    if not mapping then
      EnableTrigger("automap_check", true)
    end -- if
  end -- if
end -- func

GagOption = function(bool)
  opt = (bool and "1" or "0")

  names = {"notmapped", "maprows",
           "coordline", "prompt" ,
           "arealine" , "error"  ,
          }

  for _, name in ipairs(names) do
    SetTriggerOption(name, "omit_from_log", opt)
    SetTriggerOption(name, "omit_from_output", opt)
  end
end


MapAlias = function(name, line, matches, styles)
  local word = (matches[1] or ""):lower()
  local args = (matches[2] or ""):split(" ")

  if word == "radius" or word == "width" or word == "height" then
    EnableGroup("mapsize", true)
    EnableTrigger("error", true)
  elseif word == "move" then
    if tonumber(args[1]) and tonumber(args[2]) then
      map:MoveWindow(args[1], args[2])
    elseif args[1] == nil then
      Note("Current location: (" .. map.x .. ", " .. map.y .. ")")
    else
      Note("Numbers only!")
    end
    line = ""
  elseif word == "fontsize" then
    if tonumber(args[1]) then
      map:FontSize(args[1])
    elseif args[1] == nil then
      Note("Current font size: " .. map.fontsize)
    else
      Note("Numbers only!")
    end
    line = ""
  end

  Send(line)
end

MapParse = function(name, line, matches, styles)
  word = (matches[1] or ""):lower()

  if word ~= "parse" then
    gagging = false
    GagOption(false)
  elseif mapping then
    return
  end

  if not mapping then
    mapping = true
    EnableGroup("mapbegin", true)
    EnableTrigger("error", true)
  end
  SendNoEcho("map")
end

-- runs for each row in the MAP output
ParseLine = function(name, line, matches, styles)
  -- expand the style runs
  local line = {}
  for _, style in ipairs(styles) do
    for i = 1, style.length do
      table.insert(line, {char = style.text:sub(i, i), textcolour = style.textcolour})
    end
  end

  -- contains {char, colour} pairs for each cell
  local row = {}

  -- Increment by two to skip over unnecessary spaces
  -- from the original (including the [ and ] marks)
  for i = 1, table.getn(line), 2 do
    cell = {}

    -- if this is true, we know it's a "[ ]"
    if (i % 4 == 3) and (line[i-1].char == "[") then
      -- replace it with an appropriate mark
      cell.char  = roomcode[line[i].char] or "#"
      cell.style = roomcolor[cell.char] or line[i-1].textcolour
    else
      -- keep the same character that was there
      cell.char  = line[i].char:upper()
      cell.style = line[i].textcolour
    end
    table.insert(row, cell)
  end

  -- after the entire map is processed, it draws the
  -- entire grid, so we store each row for later
  table.insert(grid, row)
end

]]>
</script>


<!--  Plugin help  -->

<aliases>
  <alias
   script="OnHelp"
   match="^\s*map\s+help\s*$"
   regexp="y"
   enabled="y"
  >
  </alias>
</aliases>

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

</muclient>