-- Chat with Dragons: an automapper for Mudlet.
--
-- The game sends every room you enter over GMCP (Room.Info: number, name, area, terrain,
-- "area,x,y,z" coordinates and exits). This script draws the map from it as you walk, at
-- the world's own coordinates, coloured by terrain, and lets you click a room on the map
-- to walk there.
--
-- Install: in Mudlet, open Scripts (Alt+E), add a new script, paste all of this in, save.
-- Then open the map (the Map button, or Alt+M) and walk.
--   cwdmap         this help
--   cwdmap clear   forget the whole map and start again

CWD = CWD or {}
CWD.areas = CWD.areas or {}

-- Terrain colours, as the game draws them (environment ids from 300 up).
CWD.terrain = {
  inside = { 300, 0x5a, 0x5a, 0x5a }, city = { 301, 0x80, 0x80, 0x80 }, road = { 302, 0xc0, 0xc0, 0xc0 },
  plains = { 303, 0x00, 0x80, 0x80 }, grassland = { 304, 0x80, 0xff, 0x80 }, field = { 305, 0xd8, 0xc0, 0x40 },
  forest = { 306, 0x2f, 0x7a, 0x3a }, swamp = { 307, 0x3a, 0x6a, 0x4a }, hills = { 308, 0x8a, 0x6a, 0x40 },
  mountain = { 309, 0x7a, 0x4a, 0x2a }, desert = { 310, 0xff, 0xff, 0x80 }, water = { 311, 0x20, 0x50, 0xd0 },
  lava = { 312, 0xff, 0x30, 0x10 }, snow = { 313, 0xf0, 0xf0, 0xf0 }, cavern = { 314, 0x4a, 0x3a, 0x5a },
  sky = { 315, 0xf4, 0xf4, 0xf8 },
}
for _, t in pairs(CWD.terrain) do setCustomEnvColor(t[1], t[2], t[3], t[4], 255) end

-- Which way each exit goes, on the map (the game's north is up).
CWD.step = {
  n = { 0, 1, 0 }, s = { 0, -1, 0 }, e = { 1, 0, 0 }, w = { -1, 0, 0 },
  ne = { 1, 1, 0 }, nw = { -1, 1, 0 }, se = { 1, -1, 0 }, sw = { -1, -1, 0 },
  u = { 0, 0, 1 }, d = { 0, 0, -1 },
}

local function areaId(name)
  if CWD.areas[name] then return CWD.areas[name] end
  local id = getAreaTable()[name]
  if not id then id = addAreaName(name) end
  CWD.areas[name] = id
  return id
end

-- A room the map has never seen: make it, in this area, somewhere sensible.
local function ensure(num, area, x, y, z)
  if roomExists(num) then return false end
  addRoom(num)
  setRoomArea(num, area)
  setRoomCoordinates(num, x, y, z)
  return true
end

function CWD.onRoom()
  local r = gmcp.Room and gmcp.Room.Info
  if not r or not r.num then return end
  local _, x, y, z = string.match(r.coords or "", "^(.-),(%-?%d+),(%-?%d+),(%-?%d+)$")
  x, y, z = tonumber(x) or 0, -(tonumber(y) or 0), tonumber(z) or 0
  local area = areaId(r.area or "The Realm")
  ensure(r.num, area, x, y, z)
  setRoomArea(r.num, area)
  setRoomCoordinates(r.num, x, y, z)
  setRoomName(r.num, r.name or "")
  local t = CWD.terrain[r.environment or ""]
  if t then setRoomEnv(r.num, t[1]) end
  for dir, to in pairs(r.exits or {}) do
    local s = CWD.step[dir]
    if s then
      -- Rooms not yet visited go where the exit points, until you get there.
      ensure(to, area, x + s[1], y + s[2], z + s[3])
      setExit(r.num, to, dir)
    end
  end
  centerview(r.num)
end

-- Clicking a room on the map walks you there.
function doSpeedWalk()
  for _, dir in ipairs(speedWalkDir) do send(dir, false) end
end

if CWD.handler then killAnonymousEventHandler(CWD.handler) end
CWD.handler = registerAnonymousEventHandler("gmcp.Room.Info", "CWD.onRoom")

if CWD.alias then killAlias(CWD.alias) end
CWD.alias = tempAlias("^cwdmap(?: (\\w+))?$", function()
  if matches[2] == "clear" then
    for name, id in pairs(getAreaTable()) do if name ~= "Default Area" then deleteArea(id) end end
    CWD.areas = {}
    cecho("<yellow>Chat with Dragons map cleared. Walk to draw it again.\n")
  else
    cecho("<yellow>Chat with Dragons mapper: walk and the map draws itself. Click a room to go there. 'cwdmap clear' starts over.\n")
  end
end)

cecho("<yellow>Chat with Dragons mapper loaded. Open the map (Alt+M) and walk.\n")
