Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
@nickgammon
1634 lines (1290 sloc) 54.665 kb
-- mapper.lua
--[[
Author: Nick Gammon
Date: 11th March 2010
Amended: 15th August 2010
Amended: 2nd October 2010
Amended: 18th October 2010 to added find callback
Amended: 16th November 2010 to add symbolic constants (miniwin.xxxx)
Amended: 18th November 2010 to add more timing and count of times called
Also added zooming with the mouse wheel.
Amended: 26th November 2010 to check timers are enabled when speedwalking.
Amended: 11th November 2014 to allow for detecting mouse-overs of rooms
Generic MUD mapper.
Exposed functions:
init (t) -- call once, supply:
t.config -- ie. colours, sizes
t.get_room -- info about room (uid)
t.show_help -- function that displays some help
t.room_click -- function that handles RH click on room (uid, flags)
t.room_mouseover -- function that handles mouse-over a room (uid, flags)
t.room_cancelmouseover -- function that handles cancelled mouse-over of a room (uid, flags)
t.timing -- true to show timing
t.show_completed -- true to show "Speedwalk completed."
t.show_other_areas -- true to show non-current areas
t.show_up_down -- follow up/down exits
t.show_area_exits -- true to draw a circle around rooms leading to other areas
t.speedwalk_prefix -- if not nil, speedwalk by prefixing with this
zoom_in () -- zoom in map view
zoom_out () -- zoom out map view
mapprint (message) -- like print, but uses mapper colour
maperror (message) -- like print, but prints in red
hide () -- hides map window (eg. if plugin disabled)
show () -- show map window (eg. if plugin enabled)
save_state () -- call to save plugin state (ie. in OnPluginSaveState)
draw (uid) -- draw map - starting at room 'uid'
start_speedwalk (path) -- starts speedwalking. path is a table of directions/uids
build_speedwalk (path) -- builds a client speedwalk string from path
cancel_speedwalk () -- cancel current speedwalk, if any
check_we_can_find () -- returns true if doing a find is OK right now
find (f, show_uid, count, walk) -- generic room finder
find_paths (uid, f) -- lower-level room finder (for getting back a path)
Exposed variables:
win -- the window (in case you want to put up menus)
VERSION -- mapper version
last_hyperlink_uid -- room uid of last hyperlink click (destination)
last_speedwalk_uid -- room uid of last speedwalk attempted (destination)
<various functions> -- functions required to be global by the client (eg. for mouseup)
Room info should include:
name (what to show as room name)
exits (table keyed by direction, value is exit uid)
area (area name)
hovermessage (what to show when you mouse-over the room)
bordercolour (colour of room border) - RGB colour
borderpen (pen style of room border) - see WindowCircleOp (values 0 to 6)
borderpenwidth(pen width of room border) - eg. 1 for normal, 2 for current room
fillcolour (colour to fill room) - RGB colour, nil for default
fillbrush (brush to fill room) - see WindowCircleOp (values 0 to 12)
--]]
module (..., package.seeall)
VERSION = 2.6 -- for querying by plugins
require "movewindow"
require "copytable"
require "gauge"
require "pairsbykeys"
require "mw"
local FONT_ID = "fn" -- internal font identifier
local FONT_ID_UL = "fnu" -- internal font identifier - underlined
-- size of room box
local ROOM_SIZE = 10
-- how far away to draw rooms from each other
local DISTANCE_TO_NEXT_ROOM = 15
-- supplied in init
local config -- configuration table
local supplied_get_room
local room_click
local room_mouseover
local room_cancelmouseover
local timing -- true to show timing and other info
local show_completed -- true to show "Speedwalk completed."
local show_other_areas -- true to draw other areas
local show_area_exits -- true to show area exits
local show_up_down -- true to show up/down exits
-- current room number
local current_room
-- our copy of rooms info
local rooms = {}
local last_visited = {}
-- other locals
local HALF_ROOM, connectors, half_connectors, arrows
local plan_to_draw, speedwalks, drawn, drawn_coords
local last_drawn, depth, windowinfo, font_height
local walk_to_room_name
local total_times_drawn = 0
local total_time_taken = 0
local function build_room_info ()
HALF_ROOM = ROOM_SIZE / 2
local THIRD_WAY = DISTANCE_TO_NEXT_ROOM / 3
local DISTANCE_LESS1 = DISTANCE_TO_NEXT_ROOM - 1
-- how to draw a line from this room to the next one (relative to the center of the room)
connectors = {
n = { x1 = 0, y1 = - HALF_ROOM, x2 = 0, y2 = - HALF_ROOM - DISTANCE_LESS1, at = { 0, -1 } },
s = { x1 = 0, y1 = HALF_ROOM, x2 = 0, y2 = HALF_ROOM + DISTANCE_LESS1, at = { 0, 1 } },
e = { x1 = HALF_ROOM, y1 = 0, x2 = HALF_ROOM + DISTANCE_LESS1, y2 = 0, at = { 1, 0 }},
w = { x1 = - HALF_ROOM, y1 = 0, x2 = - HALF_ROOM - DISTANCE_LESS1, y2 = 0, at = { -1, 0 }},
ne = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + DISTANCE_LESS1 , y2 = - HALF_ROOM - DISTANCE_LESS1, at = { 1, -1 } },
se = { x1 = HALF_ROOM, y1 = HALF_ROOM, x2 = HALF_ROOM + DISTANCE_LESS1 , y2 = HALF_ROOM + DISTANCE_LESS1, at = { 1, 1 } },
nw = { x1 = - HALF_ROOM, y1 = - HALF_ROOM, x2 = - HALF_ROOM - DISTANCE_LESS1 , y2 = - HALF_ROOM - DISTANCE_LESS1, at = {-1, -1 } },
sw = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - DISTANCE_LESS1 , y2 = HALF_ROOM + DISTANCE_LESS1, at = {-1, 1 } },
} -- end connectors
-- how to draw a stub line
half_connectors = {
n = { x1 = 0, y1 = - HALF_ROOM, x2 = 0, y2 = - HALF_ROOM - THIRD_WAY, at = { 0, -1 } },
s = { x1 = 0, y1 = HALF_ROOM, x2 = 0, y2 = HALF_ROOM + THIRD_WAY, at = { 0, 1 } },
e = { x1 = HALF_ROOM, y1 = 0, x2 = HALF_ROOM + THIRD_WAY, y2 = 0, at = { 1, 0 }},
w = { x1 = - HALF_ROOM, y1 = 0, x2 = - HALF_ROOM - THIRD_WAY, y2 = 0, at = { -1, 0 }},
ne = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + THIRD_WAY , y2 = - HALF_ROOM - THIRD_WAY, at = { 1, -1 } },
se = { x1 = HALF_ROOM, y1 = HALF_ROOM, x2 = HALF_ROOM + THIRD_WAY , y2 = HALF_ROOM + THIRD_WAY, at = { 1, 1 } },
nw = { x1 = - HALF_ROOM, y1 = - HALF_ROOM, x2 = - HALF_ROOM - THIRD_WAY , y2 = - HALF_ROOM - THIRD_WAY, at = {-1, -1 } },
sw = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - THIRD_WAY , y2 = HALF_ROOM + THIRD_WAY, at = {-1, 1 } },
} -- end half_connectors
-- how to draw one-way arrows (relative to the center of the room)
arrows = {
n = { - 2, - HALF_ROOM - 2, 2, - HALF_ROOM - 2, 0, - HALF_ROOM - 6 },
s = { - 2, HALF_ROOM + 2, 2, HALF_ROOM + 2, 0, HALF_ROOM + 6 },
e = { HALF_ROOM + 2, -2, HALF_ROOM + 2, 2, HALF_ROOM + 6, 0 },
w = { - HALF_ROOM - 2, -2, - HALF_ROOM - 2, 2, - HALF_ROOM - 6, 0 },
ne = { HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3, - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3 },
se = { HALF_ROOM + 3, HALF_ROOM, HALF_ROOM + 3, HALF_ROOM + 3, HALF_ROOM, HALF_ROOM + 3 },
nw = { - HALF_ROOM - 3, - HALF_ROOM, - HALF_ROOM - 3, - HALF_ROOM - 3, - HALF_ROOM, - HALF_ROOM - 3 },
sw = { - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3, HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3},
} -- end of arrows
end -- build_room_info
local default_config = {
-- assorted colours
BACKGROUND_COLOUR = { name = "Background", colour = ColourNameToRGB "lightseagreen", },
ROOM_COLOUR = { name = "Room", colour = ColourNameToRGB "cyan", },
EXIT_COLOUR = { name = "Exit", colour = ColourNameToRGB "darkgreen", },
EXIT_COLOUR_UP_DOWN = { name = "Exit up/down", colour = ColourNameToRGB "darkmagenta", },
EXIT_COLOUR_IN_OUT = { name = "Exit in/out", colour = ColourNameToRGB "#3775E8", },
UNKNOWN_ROOM_COLOUR = { name = "Unknown room", colour = ColourNameToRGB "#00CACA", },
MAPPER_NOTE_COLOUR = { name = "Messages", colour = ColourNameToRGB "lightgreen" },
ROOM_NAME_TEXT = { name = "Room name text", colour = ColourNameToRGB "#BEF3F1", },
ROOM_NAME_FILL = { name = "Room name fill", colour = ColourNameToRGB "#105653", },
ROOM_NAME_BORDER = { name = "Room name box", colour = ColourNameToRGB "black", },
AREA_NAME_TEXT = { name = "Area name text", colour = ColourNameToRGB "#BEF3F1",},
AREA_NAME_FILL = { name = "Area name fill", colour = ColourNameToRGB "#105653", },
AREA_NAME_BORDER = { name = "Area name box", colour = ColourNameToRGB "black", },
FONT = { name = get_preferred_font {"Dina", "Lucida Console", "Fixedsys", "Courier", "Sylfaen",} ,
size = 8
} ,
-- size of map window
WINDOW = { width = 400, height = 400 },
-- how far from where we are standing to draw (rooms)
SCAN = { depth = 30 },
-- speedwalk delay
DELAY = { time = 0.3 },
-- how many seconds to show "recent visit" lines (default 3 minutes)
LAST_VISIT_TIME = { time = 60 * 3 },
}
local expand_direction = {
n = "north",
s = "south",
e = "east",
w = "west",
u = "up",
d = "down",
ne = "northeast",
sw = "southwest",
nw = "northwest",
se = "southeast",
['in'] = "in",
out = "out",
} -- end of expand_direction
local function get_room (uid)
local room = supplied_get_room (uid)
room = room or { unknown = true }
-- defaults in case they didn't supply them ...
room.name = room.name or string.format ("Room %s", uid or "<none>")
room.name = mw.strip_colours (room.name) -- no colour codes for now
room.exits = room.exits or {}
room.area = room.area or "<No area>"
room.hovermessage = room.hovermessage or "<Unexplored room>"
room.bordercolour = room.bordercolour or config.ROOM_COLOUR.colour
room.borderpen = room.borderpen or 0 -- solid
room.borderpenwidth = room.borderpenwidth or 1
room.fillcolour = room.fillcolour or 0x000000
room.fillbrush = room.fillbrush or 1 -- no fill
return room
end -- get_room
local function check_connected ()
if not IsConnected() then
mapprint ("You are not connected to", WorldName())
return false
end -- if not connected
return true
end -- check_connected
local function make_number_checker (title, min, max, decimals)
return function (s)
local n = tonumber (s)
if not n then
utils.msgbox (title .. " must be a number", "Incorrect input", "ok", "!", 1)
return false -- bad input
end -- if
if n < min or n > max then
utils.msgbox (title .. " must be in range " .. min .. " to " .. max, "Incorrect input", "ok", "!", 1)
return false -- bad input
end -- if
if not decimals then
if string.match (s, "%.") then
utils.msgbox (title .. " cannot have decimal places", "Incorrect input", "ok", "!", 1)
return false -- bad input
end -- if
end -- no decimals
return true -- good input
end -- generated function
end -- make_number_checker
local function get_number_from_user (msg, title, current, min, max, decimals)
local max_length = math.ceil (math.log10 (max) + 1)
-- if decimals allowed, allow room for them
if decimals then
max_length = max_length + 2 -- allow for 0.x
end -- if
-- if can be negative, allow for minus sign
if min < 0 then
max_length = max_length + 1
end -- if can be negative
return tonumber (utils.inputbox (msg, title, current, nil, nil,
{ validate = make_number_checker (title, min, max, decimals),
prompt_height = 14,
box_height = 130,
box_width = 300,
reply_width = 150,
max_length = max_length,
} -- end extra stuff
))
end -- get_number_from_user
local function draw_configuration ()
local width = max_text_width (win, FONT_ID, {"Configuration", "Font", "Width", "Height", "Depth"}, true)
local lines = 6 -- "Configuration", font, width, height, depth, delay
local GAP = 5
local suppress_colours = false
for k, v in pairs (config) do
if v.colour then
width = math.max (width, WindowTextWidth (win, FONT_ID, v.name, true))
lines = lines + 1
end -- a colour item
end -- for each config item
if (config.WINDOW.height - 13 - font_height * lines) < 10 then
suppress_colours = true
lines = 6 -- forget all the colours
end -- if
local x = 3
local y = config.WINDOW.height - 13 - font_height * lines
local box_size = font_height - 2
local rh_size = math.max (box_size, max_text_width (win, FONT_ID,
{config.FONT.name .. " " .. config.FONT.size,
tostring (config.WINDOW.width),
tostring (config.WINDOW.height),
tostring (config.SCAN.depth)},
true))
local frame_width = GAP + width + GAP + rh_size + GAP -- gap / text / gap / box / gap
-- fill entire box with grey
WindowRectOp (win, miniwin.rect_fill, x, y, x + frame_width, y + font_height * lines + 10, 0xDCDCDC)
-- frame it
draw_3d_box (win, x, y, frame_width, font_height * lines + 10)
y = y + GAP
x = x + GAP
-- title
WindowText (win, FONT_ID, "Configuration", x, y, 0, 0, 0x808080, true)
-- close box
WindowRectOp (win,
miniwin.rect_frame,
x + frame_width - box_size - GAP * 2,
y + 1,
x + frame_width - GAP * 2,
y + 1 + box_size,
0x808080)
WindowLine (win,
x + frame_width - box_size - GAP * 2 + 3,
y + 4,
x + frame_width - GAP * 2 - 3,
y - 2 + box_size,
0x808080,
miniwin.pen_solid, 1)
WindowLine (win,
x - 4 + frame_width - GAP * 2,
y + 4,
x - 1 + frame_width - box_size - GAP * 2 + 3,
y - 2 + box_size,
0x808080,
miniwin.pen_solid, 1)
-- close configuration hotspot
WindowAddHotspot(win, "$<close_configure>",
x + frame_width - box_size - GAP * 2,
y + 1,
x + frame_width - GAP * 2,
y + 1 + box_size, -- rectangle
"", "", "", "", "mapper.mouseup_close_configure", -- mouseup
"Click to close",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
if not suppress_colours then
for k, v in pairsByKeys (config) do
if v.colour then
WindowText (win, FONT_ID, v.name, x, y, 0, 0, 0x000000, true)
WindowRectOp (win,
miniwin.rect_fill,
x + width + rh_size / 2,
y + 1,
x + width + rh_size / 2 + box_size,
y + 1 + box_size,
v.colour)
WindowRectOp (win,
miniwin.rect_frame,
x + width + rh_size / 2,
y + 1,
x + width + rh_size / 2 + box_size,
y + 1 + box_size,
0x000000)
-- colour change hotspot
WindowAddHotspot(win,
"$colour:" .. k,
x + GAP,
y + 1,
x + width + rh_size / 2 + box_size,
y + 1 + box_size, -- rectangle
"", "", "", "", "mapper.mouseup_change_colour", -- mouseup
"Click to change colour",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
end -- a colour item
end -- for each config item
end -- if
-- depth
WindowText (win, FONT_ID, "Depth", x, y, 0, 0, 0x000000, true)
WindowText (win, FONT_ID_UL, tostring (config.SCAN.depth), x + width + GAP, y, 0, 0, 0x808080, true)
-- depth hotspot
WindowAddHotspot(win,
"$<depth>",
x + GAP,
y,
x + frame_width,
y + font_height, -- rectangle
"", "", "", "", "mapper.mouseup_change_depth", -- mouseup
"Click to change scan depth",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
-- font
WindowText (win, FONT_ID, "Font", x, y, 0, 0, 0x000000, true)
WindowText (win, FONT_ID_UL, config.FONT.name .. " " .. config.FONT.size, x + width + GAP, y, 0, 0, 0x808080, true)
-- colour font hotspot
WindowAddHotspot(win,
"$<font>",
x + GAP,
y,
x + frame_width,
y + font_height, -- rectangle
"", "", "", "", "mapper.mouseup_change_font", -- mouseup
"Click to change font",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
-- width
WindowText (win, FONT_ID, "Width", x, y, 0, 0, 0x000000, true)
WindowText (win, FONT_ID_UL, tostring (config.WINDOW.width), x + width + GAP, y, 0, 0, 0x808080, true)
-- width hotspot
WindowAddHotspot(win,
"$<width>",
x + GAP,
y,
x + frame_width,
y + font_height, -- rectangle
"", "", "", "", "mapper.mouseup_change_width", -- mouseup
"Click to change window width",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
-- height
WindowText (win, FONT_ID, "Height", x, y, 0, 0, 0x000000, true)
WindowText (win, FONT_ID_UL, tostring (config.WINDOW.height), x + width + GAP, y, 0, 0, 0x808080, true)
-- height hotspot
WindowAddHotspot(win,
"$<height>",
x + GAP,
y,
x + frame_width,
y + font_height, -- rectangle
"", "", "", "", "mapper.mouseup_change_height", -- mouseup
"Click to change window height",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
-- delay
WindowText (win, FONT_ID, "Walk delay", x, y, 0, 0, 0x000000, true)
WindowText (win, FONT_ID_UL, tostring (config.DELAY.time), x + width + GAP, y, 0, 0, 0x808080, true)
-- height hotspot
WindowAddHotspot(win,
"$<delay>",
x + GAP,
y,
x + frame_width,
y + font_height, -- rectangle
"", "", "", "", "mapper.mouseup_change_delay", -- mouseup
"Click to change speedwalk delay",
miniwin.cursor_hand, 0) -- hand cursor
y = y + font_height
end -- draw_configuration
-- for calculating one-way paths
local inverse_direction = {
n = "s",
s = "n",
e = "w",
w = "e",
u = "d",
d = "u",
ne = "sw",
sw = "ne",
nw = "se",
se = "nw",
['in'] = "out",
out = "in",
} -- end of inverse_direction
local function add_another_room (uid, path, x, y)
local path = path or {}
return {uid=uid, path=path, x = x, y = y}
end -- add_another_room
local function draw_room (uid, path, x, y)
local coords = string.format ("%i,%i", math.floor (x), math.floor (y))
-- need this for the *current* room !!!
drawn_coords [coords] = uid
-- print ("drawing", uid, "at", coords)
if drawn [uid] then
return
end -- done this one
-- don't draw the same room more than once
drawn [uid] = { coords = coords, path = path }
local room = rooms [uid]
-- not cached - get from caller
if not room then
room = get_room (uid)
rooms [uid] = room
end -- not in cache
local left, top, right, bottom = x - HALF_ROOM, y - HALF_ROOM, x + HALF_ROOM, y + HALF_ROOM
-- forget it if off screen
if x < HALF_ROOM or y < HALF_ROOM or
x > config.WINDOW.width - HALF_ROOM or y > config.WINDOW.height - HALF_ROOM then
return
end -- if
-- exits
local texits = {}
for dir, exit_uid in pairs (room.exits) do
table.insert (texits, dir)
local exit_info = connectors [dir]
local stub_exit_info = half_connectors [dir]
local exit_line_colour = config.EXIT_COLOUR.colour
local arrow = arrows [dir]
-- draw up in the ne/nw position if not already an exit there at this level
if dir == "u" then
if not room.exits.nw then
exit_info = connectors.nw
stub_exit_info = half_connectors.nw
arrow = arrows.nw
exit_line_colour = config.EXIT_COLOUR_UP_DOWN.colour
end -- if available
elseif dir == "in" then
if not room.exits.ne then
exit_info = connectors.ne
stub_exit_info = half_connectors.ne
arrow = arrows.ne
exit_line_colour = config.EXIT_COLOUR_IN_OUT.colour
end -- if
elseif dir == "d" then
if not room.exits.se then
exit_info = connectors.se
stub_exit_info = half_connectors.se
arrow = arrows.se
exit_line_colour = config.EXIT_COLOUR_UP_DOWN.colour
end -- if available
elseif dir == "out" then
if not room.exits.sw then
exit_info = connectors.sw
stub_exit_info = half_connectors.sw
arrow = arrows.sw
exit_line_colour = config.EXIT_COLOUR_IN_OUT.colour
end -- if
end -- if down
if exit_info then
local linetype = miniwin.pen_solid -- unbroken
local linewidth = 1 -- not recent
-- try to cache room
if not rooms [exit_uid] then
rooms [exit_uid] = get_room (exit_uid)
end -- if
if rooms [exit_uid].unknown then
linetype = miniwin.pen_dot -- dots
end -- if
local next_x = x + exit_info.at [1] * (ROOM_SIZE + DISTANCE_TO_NEXT_ROOM)
local next_y = y + exit_info.at [2] * (ROOM_SIZE + DISTANCE_TO_NEXT_ROOM)
local next_coords = string.format ("%i,%i", math.floor (next_x), math.floor (next_y))
-- remember if a zone exit (first one only)
if show_area_exits and room.area ~= rooms [exit_uid].area then
area_exits [ rooms [exit_uid].area ] = area_exits [ rooms [exit_uid].area ] or {x = x, y = y}
end -- if
-- if another room (not where this one leads to) is already there, only draw "stub" lines
if drawn_coords [next_coords] and drawn_coords [next_coords] ~= exit_uid then
exit_info = stub_exit_info
elseif exit_uid == uid then
-- here if room leads back to itself
exit_info = stub_exit_info
linetype = miniwin.pen_dash -- dash
else
if (not show_other_areas and rooms [exit_uid].area ~= current_area) or
(not show_up_down and (dir == "u" or dir == "d")) then
exit_info = stub_exit_info -- don't show other areas
else
-- if we are scheduled to draw the room already, only draw a stub this time
if plan_to_draw [exit_uid] and plan_to_draw [exit_uid] ~= next_coords then
-- here if room already going to be drawn
exit_info = stub_exit_info
linetype = miniwin.pen_dash -- dash
else
-- remember to draw room next iteration
local new_path = copytable.deep (path)
table.insert (new_path, { dir = dir, uid = exit_uid })
table.insert (rooms_to_be_drawn, add_another_room (exit_uid, new_path, next_x, next_y))
drawn_coords [next_coords] = exit_uid
plan_to_draw [exit_uid] = next_coords
-- if exit room known
if not rooms [exit_uid].unknown then
local exit_time = last_visited [exit_uid] or 0
local this_time = last_visited [uid] or 0
local now = os.time ()
if exit_time > (now - config.LAST_VISIT_TIME.time) and
this_time > (now - config.LAST_VISIT_TIME.time) then
linewidth = 2
end -- if
end -- if
end -- if
end -- if
end -- if drawn on this spot
WindowLine (win, x + exit_info.x1, y + exit_info.y1, x + exit_info.x2, y + exit_info.y2, exit_line_colour, linetype, linewidth)
-- one-way exit?
if not rooms [exit_uid].unknown then
local dest = rooms [exit_uid]
-- if inverse direction doesn't point back to us, this is one-way
if dest.exits [inverse_direction [dir]] ~= uid then
-- turn points into string, relative to where the room is
local points = string.format ("%i,%i,%i,%i,%i,%i",
x + arrow [1],
y + arrow [2],
x + arrow [3],
y + arrow [4],
x + arrow [5],
y + arrow [6])
-- draw arrow
WindowPolygon(win, points,
exit_line_colour, miniwin.pen_solid, 1,
exit_line_colour, miniwin.brush_solid,
true, true)
end -- one way
end -- if we know of the room where it does
end -- if we know what to do with this direction
end -- for each exit
if room.unknown then
WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
config.UNKNOWN_ROOM_COLOUR.colour, miniwin.pen_dot, 1, -- dotted single pixel pen
-1, miniwin.brush_null) -- opaque, no brush
else
WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
0, miniwin.pen_null, 0, -- no pen
room.fillcolour, room.fillbrush) -- brush
WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
room.bordercolour, room.borderpen, room.borderpenwidth, -- pen
-1, miniwin.brush_null) -- opaque, no brush
end -- if
-- show up and down in case we can't get a line in
if room.exits.u then -- line at top
WindowLine (win, left, top, left + ROOM_SIZE, top, config.EXIT_COLOUR_UP_DOWN.colour, miniwin.pen_solid, 1)
end -- if
if room.exits.d then -- line at bottom
WindowLine (win, left, bottom, left + ROOM_SIZE, bottom, config.EXIT_COLOUR_UP_DOWN.colour, miniwin.pen_solid, 1)
end -- if
if room.exits ['in'] then -- line at right
WindowLine (win, left + ROOM_SIZE, top, left + ROOM_SIZE, bottom, config.EXIT_COLOUR_IN_OUT.colour, miniwin.pen_solid, 1)
end -- if
if room.exits.out then -- line at left
WindowLine (win, left, top, left, bottom, config.EXIT_COLOUR_IN_OUT.colour, miniwin.pen_solid , 1)
end -- if
speedwalks [uid] = path -- so we know how to get here
WindowAddHotspot(win, uid,
left, top, right, bottom, -- rectangle
"mapper.mouseover_room", -- mouseover
"mapper.cancelmouseover_room", -- cancelmouseover
"", -- mousedown
"", -- cancelmousedown
"mapper.mouseup_room", -- mouseup
room.hovermessage,
miniwin.cursor_hand, 0) -- hand cursor
WindowScrollwheelHandler (win, uid, "mapper.zoom_map")
end -- draw_room
local function changed_room (uid)
hyperlink_paths = nil -- those hyperlinks are meaningless now
speedwalks = {} -- old speedwalks are irrelevant
if current_speedwalk then
if uid ~= expected_room then
local exp = rooms [expected_room]
if not exp then
exp = get_room (expected_room) or { name = expected_room }
end -- if
local here = rooms [uid]
if not here then
here = get_room (uid) or { name = uid }
end -- if
exp = expected_room
here = uid
maperror (string.format ("Speedwalk failed! Expected to be in '%s' but ended up in '%s'.", exp or "<none>", here))
cancel_speedwalk ()
else
if #current_speedwalk > 0 then
local dir = table.remove (current_speedwalk, 1)
SetStatus ("Walking " .. (expand_direction [dir.dir] or dir.dir) ..
" to " .. walk_to_room_name ..
". Speedwalks to go: " .. #current_speedwalk + 1)
expected_room = dir.uid
if config.DELAY.time > 0 then
if GetOption ("enable_timers") ~= 1 then
maperror ("WARNING! Timers not enabled. Speedwalking will not work properly.")
end -- if timers disabled
DoAfter (config.DELAY.time, dir.dir)
else
Send (dir.dir)
end -- if
else
last_hyperlink_uid = nil
last_speedwalk_uid = nil
if show_completed then
mapprint ("Speedwalk completed.")
end -- if wanted
cancel_speedwalk ()
end -- if any left
end -- if expected room or not
end -- if have a current speedwalk
end -- changed_room
local function draw_zone_exit (exit)
local x, y = exit.x, exit.y
local offset = ROOM_SIZE
-- draw circle around us
WindowCircleOp (win, miniwin.circle_ellipse,
x - offset, y - offset,
x + offset, y + offset,
ColourNameToRGB "cornflowerblue", -- pen colour
miniwin.pen_solid, -- solid pen
3, -- pen width
0, -- brush colour
miniwin.brush_null) -- null brush
WindowCircleOp (win, miniwin.circle_ellipse,
x - offset, y - offset,
x + offset, y + offset,
ColourNameToRGB "cyan", -- pen colour
miniwin.pen_solid, -- solid pen
1, -- pen width
0, -- brush colour
miniwin.brush_null) -- null brush
end -- draw_zone_exit
----------------------------------------------------------------------------------
-- EXPOSED FUNCTIONS
----------------------------------------------------------------------------------
-- can we find another room right now?
function check_we_can_find ()
if not check_connected () then
return
end -- if
if not current_room then
mapprint ("I don't know where you are right now - try: LOOK")
return false
end -- if
if current_speedwalk then
mapprint ("No point doing this while you are speedwalking.")
return false
end -- if
return true
end -- check_we_can_find
-- see: http://www.gammon.com.au/forum/?id=7306&page=2
-- Thanks to Ked.
-- uid is starting room
-- f returns true (or a "reason" string) if we want to store this one, and true,true if
-- we have done searching (ie. all wanted rooms found)
function find_paths (uid, f)
local function make_particle (curr_loc, prev_path)
local prev_path = prev_path or {}
return {current_room=curr_loc, path=prev_path}
end
local depth = 0
local count = 0
local done = false
local found, reason
local explored_rooms, particles = {}, {}
-- this is where we collect found paths
-- the table is keyed by destination, with paths as values
local paths = {}
-- create particle for the initial room
table.insert (particles, make_particle (uid))
while (not done) and #particles > 0 and depth < config.SCAN.depth do
-- create a new generation of particles
new_generation = {}
depth = depth + 1
SetStatus (string.format ("Scanning: %i/%i depth (%i rooms)", depth, config.SCAN.depth, count))
-- process each active particle
for i, part in ipairs (particles) do
count = count + 1
if not rooms [part.current_room] then
rooms [part.current_room] = get_room (part.current_room)
end -- if not in memory yet
-- if room doesn't exist, forget it
if rooms [part.current_room] then
-- get a list of exits from the current room
exits = rooms [part.current_room].exits
-- create one new particle for each exit
for dir, dest in pairs(exits) do
-- if we've been in this room before, drop it
if not explored_rooms[dest] then
explored_rooms[dest] = true
rooms [dest] = supplied_get_room (dest) -- make sure this room in table
if rooms [dest] then
new_path = copytable.deep (part.path)
table.insert(new_path, { dir = dir, uid = dest } )
-- if this room is in the list of destinations then save its path
found, done = f (dest)
if found then
paths[dest] = { path = new_path, reason = found }
end -- found one!
-- make a new particle in the new room
table.insert(new_generation, make_particle(dest, new_path))
end -- if room exists
end -- not explored this room
if done then
break
end
end -- for each exit
end -- if room exists
if done then
break
end
end -- for each particle
particles = new_generation
end -- while more particles
SetStatus "Ready"
return paths, count, depth
end -- function find_paths
-- draw our map starting at room: uid
function draw (uid)
if not uid then
maperror "Cannot draw map right now, I don't know where you are - try: LOOK"
return
end -- if
if current_room and current_room ~= uid then
changed_room (uid)
end -- if
current_room = uid -- remember where we are
-- timing
local start_time = utils.timer ()
-- start with initial room
rooms = { [uid] = get_room (uid) }
-- lookup current room
local room = rooms [uid]
room = room or { name = "<Unknown room>", area = "<Unknown area>" }
last_visited [uid] = os.time ()
current_area = room.area
-- we are recreating the window so any mouse-over is not valid any more
if WindowInfo (win, 19) and WindowInfo (win, 19) ~= "" then
if type (room_cancelmouseover) == "function" then
room_cancelmouseover (WindowInfo (win, 19), 0) -- cancelled mouse over
end -- if
end -- have a hotspot
WindowDeleteAllHotspots (win)
WindowCreate (win,
windowinfo.window_left,
windowinfo.window_top,
config.WINDOW.width,
config.WINDOW.height,
windowinfo.window_mode, -- top right
windowinfo.window_flags,
config.BACKGROUND_COLOUR.colour)
-- let them move it around
movewindow.add_drag_handler (win, 0, 0, 0, font_height + 4)
-- for zooming
WindowAddHotspot(win,
"zzz_zoom",
0, 0, 0, 0,
"", "", "", "", "",
"", -- hint
miniwin.cursor_arrow, 0)
WindowScrollwheelHandler (win, "zzz_zoom", "mapper.zoom_map")
-- set up for initial room, in middle
drawn, drawn_coords, rooms_to_be_drawn, speedwalks, plan_to_draw, area_exits = {}, {}, {}, {}, {}, {}
depth = 0
-- insert initial room
table.insert (rooms_to_be_drawn, add_another_room (uid, {}, config.WINDOW.width / 2, config.WINDOW.height / 2))
while #rooms_to_be_drawn > 0 and depth < config.SCAN.depth do
local old_generation = rooms_to_be_drawn
rooms_to_be_drawn = {} -- new generation
for i, part in ipairs (old_generation) do
draw_room (part.uid, part.path, part.x, part.y)
end -- for each existing room
depth = depth + 1
end -- while all rooms_to_be_drawn
for area, zone_exit in pairs (area_exits) do
draw_zone_exit (zone_exit)
end -- for
local room_name = room.name
local name_width = WindowTextWidth (win, FONT_ID, room_name, true)
local add_dots = false
-- truncate name if too long
while name_width > (config.WINDOW.width - 10) do
-- get rid of last word
local s = string.match (" " .. room_name .. "...", "(%s%S*)$")
if not s or #s == 0 then break end
room_name = room_name:sub (1, - (#s - 2)) -- except the last 3 dots but add the space
name_width = WindowTextWidth (win, FONT_ID, room_name .. " ...", true)
add_dots = true
end -- while
if add_dots then
room_name = room_name .. " ..."
end -- if
-- room name
draw_text_box (win, FONT_ID,
(config.WINDOW.width - WindowTextWidth (win, FONT_ID, room_name, true)) / 2, -- left
3, -- top
room_name, true, -- what to draw, utf8
config.ROOM_NAME_TEXT.colour, -- text colour
config.ROOM_NAME_FILL.colour, -- fill colour
config.ROOM_NAME_BORDER.colour) -- border colour
-- area name
local areaname = room.area
if areaname then
draw_text_box (win, FONT_ID,
(config.WINDOW.width - WindowTextWidth (win, FONT_ID, areaname, true)) / 2, -- left
config.WINDOW.height - 6 - font_height, -- top
areaname, true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
end -- if area known
-- configure?
if draw_configure_box then
draw_configuration ()
else
local x = 5
local y = config.WINDOW.height - 6 - font_height
local width = draw_text_box (win, FONT_ID,
x, -- left
y, -- top (ie. at bottom)
"*", true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
WindowAddHotspot(win, "<configure>",
x, y, x + width, y + font_height, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"", -- mousedown
"", -- cancelmousedown
"mapper.mouseup_configure", -- mouseup
"Click to configure map",
miniwin.cursor_hand, 0) -- hand cursor
end -- if
if type (show_help) == "function" then
local x = config.WINDOW.width - WindowTextWidth (win, FONT_ID, "?", true) - 5
local y = config.WINDOW.height - 6 - font_height
local width = draw_text_box (win, FONT_ID,
x, -- left
y, -- top (ie. at bottom)
"?", true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
WindowAddHotspot(win, "<help>",
x, y, x + width, y + font_height, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"", -- mousedown
"", -- cancelmousedown
"mapper.show_help", -- mouseup
"Click for help",
miniwin.cursor_hand, 0) -- hand cursor
end -- if
-- 3D box around whole thing
draw_3d_box (win, 0, 0, config.WINDOW.width, config.WINDOW.height)
-- make sure window visible
WindowShow (win, not hidden)
last_drawn = uid -- last room number we drew (for zooming)
local end_time = utils.timer ()
-- timing stuff
if timing then
local count= 0
for k in pairs (drawn) do
count = count + 1
end
print (string.format ("Time to draw %i rooms = %0.3f seconds, search depth = %i", count, end_time - start_time, depth))
total_times_drawn = total_times_drawn + 1
total_time_taken = total_time_taken + end_time - start_time
print (string.format ("Total times map drawn = %i, average time to draw = %0.3f seconds",
total_times_drawn,
total_time_taken / total_times_drawn))
end -- if
end -- draw
local credits = {
"MUSHclient mapper",
string.format ("Version %0.1f", VERSION),
"Written by Nick Gammon",
WorldName (),
GetInfo (3),
}
-- call once to initialize the mapper
function init (t)
-- make copy of colours, sizes etc.
config = t.config
assert (type (config) == "table", "No 'config' table supplied to mapper.")
supplied_get_room = t.get_room
assert (type (supplied_get_room) == "function", "No 'get_room' function supplied to mapper.")
show_help = t.show_help -- "help" function
room_click = t.room_click -- RH mouse-click function
room_mouseover = t.room_mouseover -- mouse-over function
room_cancelmouseover = t.room_cancelmouseover -- cancel mouse-over function
timing = t.timing -- true for timing info
show_completed = t.show_completed -- true to show "Speedwalk completed." message
show_other_areas = t.show_other_areas -- true to show other areas
show_up_down = t.show_up_down -- true to show up or down
show_area_exits = t.show_area_exits -- true to show area exits
speedwalk_prefix = t.speedwalk_prefix -- how to speedwalk (prefix)
-- force some config defaults if not supplied
for k, v in pairs (default_config) do
config [k] = config [k] or v
end -- for
win = GetPluginID () .. "_mapper"
WindowCreate (win, 0, 0, 0, 0, 0, 0, 0)
-- add the fonts
WindowFont (win, FONT_ID, config.FONT.name, config.FONT.size)
WindowFont (win, FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
-- see how high it is
font_height = WindowFontInfo (win, FONT_ID, 1) -- height
-- find where window was last time
windowinfo = movewindow.install (win, miniwin.pos_center_right)
-- calculate box sizes, arrows, connecting lines etc.
build_room_info ()
WindowCreate (win,
windowinfo.window_left,
windowinfo.window_top,
config.WINDOW.width,
config.WINDOW.height,
windowinfo.window_mode, -- top right
windowinfo.window_flags,
config.BACKGROUND_COLOUR.colour)
-- let them move it around
movewindow.add_drag_handler (win, 0, 0, 0, font_height + 4)
local top = (config.WINDOW.height - #credits * font_height) /2
for _, v in ipairs (credits) do
local width = WindowTextWidth (win, FONT_ID, v, true)
local left = (config.WINDOW.width - width) / 2
WindowText (win, FONT_ID, v, left, top, 0, 0, config.ROOM_COLOUR.colour, true)
top = top + font_height
end -- for
draw_3d_box (win, 0, 0, config.WINDOW.width, config.WINDOW.height)
WindowShow (win, true)
end -- init
function zoom_in ()
if last_drawn and ROOM_SIZE < 40 then
ROOM_SIZE = ROOM_SIZE + 2
DISTANCE_TO_NEXT_ROOM = DISTANCE_TO_NEXT_ROOM + 2
build_room_info ()
draw (last_drawn)
end -- if
end -- zoom_in
function zoom_out ()
if last_drawn and ROOM_SIZE > 4 then
ROOM_SIZE = ROOM_SIZE - 2
DISTANCE_TO_NEXT_ROOM = DISTANCE_TO_NEXT_ROOM - 2
build_room_info ()
draw (last_drawn)
end -- if
end -- zoom_out
function mapprint (...)
local old_note_colour = GetNoteColourFore ()
SetNoteColourFore(config.MAPPER_NOTE_COLOUR.colour)
print (...)
SetNoteColourFore (old_note_colour)
end -- mapprint
function maperror (...)
local old_note_colour = GetNoteColourFore ()
SetNoteColourFore(ColourNameToRGB "red")
print (...)
SetNoteColourFore (old_note_colour)
end -- maperror
function show ()
WindowShow (win, true)
hidden = false
end -- show
function hide ()
WindowShow (win, false)
hidden = true
end -- hide
function save_state ()
movewindow.save_state (win)
end -- save_state
-- generic room finder
-- f (uid) is a function which returns: found, done
-- found is not nil if uid is a wanted room - if it is a string it is the reason it matched (eg. shop)
-- done is true if we know there is nothing else to search for (eg. all rooms found)
-- show_uid is true if you want the room uid to be displayed
-- expected_count is the number we expect to find (eg. the number found on a database)
-- if 'walk' is true, we walk to the first match rather than displaying hyperlinks
-- if fcb is a function, it is called back after displaying each line
function find (f, show_uid, expected_count, walk, fcb)
if not check_we_can_find () then
return
end -- if
if fcb then
assert (type (fcb) == "function")
end -- if
local start_time = utils.timer ()
local paths, count, depth = find_paths (current_room, f)
local end_time = utils.timer ()
local t = {}
local found_count = 0
for k in pairs (paths) do
table.insert (t, k)
found_count = found_count + 1
end -- for
-- timing stuff
if timing then
print (string.format ("Time to search %i rooms = %0.3f seconds, search depth = %i",
count, end_time - start_time, depth))
end -- if
if found_count == 0 then
mapprint ("No matches.")
return
end -- if
if found_count == 1 and walk then
uid, item = next (paths, nil)
mapprint ("Walking to:", rooms [uid].name)
start_speedwalk (item.path)
return
end -- if walking wanted
-- sort so closest ones are first
table.sort (t, function (a, b) return #paths [a].path < #paths [b].path end )
hyperlink_paths = {}
for _, uid in ipairs (t) do
local room = rooms [uid] -- ought to exist or wouldn't be in table
assert (room, "Room " .. uid .. " is not in rooms table.")
if current_room == uid then
mapprint (room.name, "is the room you are in")
else
local distance = #paths [uid].path .. " room"
if #paths [uid].path > 1 then
distance = distance .. "s"
end -- if
distance = distance .. " away"
local room_name = room.name
if show_uid then
room_name = room_name .. " (" .. uid .. ")"
end -- if
-- in case the same UID shows up later, it is only valid from the same room
local hash = utils.tohex (utils.md5 (tostring (current_room) .. "<-->" .. tostring (uid)))
Hyperlink ("!!" .. GetPluginID () .. ":mapper.do_hyperlink(" .. hash .. ")",
room_name, "Click to speedwalk there (" .. distance .. ")", "", "", false)
local info = ""
if type (paths [uid].reason) == "string" and paths [uid].reason ~= "" then
info = " [" .. paths [uid].reason .. "]"
end -- if
mapprint (" - " .. distance .. info) -- new line
-- callback to display extra stuff (like find context, room description)
if fcb then
fcb (uid)
end -- if callback
hyperlink_paths [hash] = paths [uid].path
end -- if
end -- for each room
if expected_count and found_count < expected_count then
local diff = expected_count - found_count
local were, matches = "were", "matches"
if diff == 1 then
were, matches = "was", "match"
end -- if
mapprint ("There", were, diff, matches,
"which I could not find a path to within",
config.SCAN.depth, "rooms.")
end -- if
end -- map_find_things
-- executed when the mapper draws a hyperlink to a room
function do_hyperlink (hash)
if not check_connected () then
return
end -- if
if not hyperlink_paths or not hyperlink_paths [hash] then
mapprint ("Hyperlink is no longer valid, as you have moved.")
return
end -- if
local path = hyperlink_paths [hash]
if #path > 0 then
last_hyperlink_uid = path [#path].uid
end -- if
start_speedwalk (path)
end -- do_hyperlink
-- build a speedwalk from a path into a string
function build_speedwalk (path)
-- build speedwalk string (collect identical directions)
local tspeed = {}
for _, dir in ipairs (path) do
local n = #tspeed
if n == 0 then
table.insert (tspeed, { dir = dir.dir, count = 1 })
else
if tspeed [n].dir == dir.dir then
tspeed [n].count = tspeed [n].count + 1
else
table.insert (tspeed, { dir = dir.dir, count = 1 })
end -- if different direction
end -- if
end -- for
if #tspeed == 0 then
return
end -- nowhere to go (current room?)
-- now build string like: 2n3e4(sw)
local s = ""
for _, dir in ipairs (tspeed) do
if dir.count > 1 then
s = s .. dir.count
end -- if
if #dir.dir == 1 then
s = s .. dir.dir
else
s = s .. "(" .. dir.dir .. ")"
end -- if
s = s .. " "
end -- if
return s
end -- build_speedwalk
-- start a speedwalk to a path
function start_speedwalk (path)
if not check_connected () then
return
end -- if
if current_speedwalk and #current_speedwalk > 0 then
mapprint ("You are already speedwalking! (Ctrl + LH-click on any room to cancel)")
return
end -- if
current_speedwalk = path
if current_speedwalk then
if #current_speedwalk > 0 then
last_speedwalk_uid = current_speedwalk [#current_speedwalk].uid
-- fast speedwalk: just send # 4s 3e etc.
if type (speedwalk_prefix) == "string" and speedwalk_prefix ~= "" then
local s = speedwalk_prefix .. " " .. build_speedwalk (path)
Execute (s)
current_speedwalk = nil
return
end -- if
local dir = table.remove (current_speedwalk, 1)
local room = get_room (dir.uid)
walk_to_room_name = room.name
SetStatus ("Walking " .. (expand_direction [dir.dir] or dir.dir) ..
" to " .. walk_to_room_name ..
". Speedwalks to go: " .. #current_speedwalk + 1)
Send (dir.dir)
expected_room = dir.uid
else
cancel_speedwalk ()
end -- if any left
end -- if
end -- start_speedwalk
-- cancel the current speedwalk
function cancel_speedwalk ()
if current_speedwalk and #current_speedwalk > 0 then
mapprint "Speedwalk cancelled."
end -- if
current_speedwalk = nil
expected_room = nil
hyperlink_paths = nil
SetStatus ("Ready")
end -- cancel_speedwalk
-- ------------------------------------------------------------------
-- mouse-up handlers (need to be exposed)
-- these are for clicking on the map, or the configuration box
-- ------------------------------------------------------------------
function mouseup_room (flags, hotspot_id)
local uid = hotspot_id
if bit.band (flags, miniwin.hotspot_got_rh_mouse) ~= 0 then
-- RH click
if type (room_click) == "function" then
room_click (uid, flags)
end -- if
return
end -- if RH click
-- here for LH click
-- Control key down?
if bit.band (flags, miniwin.hotspot_got_control) ~= 0 then
cancel_speedwalk ()
return
end -- if ctrl-LH click
start_speedwalk (speedwalks [uid])
end -- mouseup_room
-- ------------------------------------------------------------------
-- mouse-over handlers (need to be exposed)
-- these are for mousing over a room
-- ------------------------------------------------------------------
function mouseover_room (flags, hotspot_id)
if type (room_mouseover) == "function" then
room_mouseover (hotspot_id, flags) -- moused over
end -- if
end -- mouseover_room
function cancelmouseover_room (flags, hotspot_id)
if type (room_cancelmouseover) == "function" then
room_cancelmouseover (hotspot_id, flags) -- cancled mouse over
end -- if
end -- cancelmouseover_room
function mouseup_configure (flags, hotspot_id)
draw_configure_box = true
draw (current_room)
end -- mouseup_configure
function mouseup_close_configure (flags, hotspot_id)
draw_configure_box = false
draw (current_room)
end -- mouseup_player
function mouseup_change_colour (flags, hotspot_id)
local which = string.match (hotspot_id, "^$colour:([%a%d_]+)$")
if not which then
return -- strange ...
end -- not found
local newcolour = PickColour (config [which].colour)
if newcolour == -1 then
return
end -- if dismissed
config [which].colour = newcolour
draw (current_room)
end -- mouseup_change_colour
function mouseup_change_font (flags, hotspot_id)
local newfont = utils.fontpicker (config.FONT.name, config.FONT.size, config.ROOM_NAME_TEXT.colour)
if not newfont then
return
end -- if dismissed
config.FONT.name = newfont.name
if newfont.size > 12 then
utils.msgbox ("Maximum allowed font size is 12 points.", "Font too large", "ok", "!", 1)
else
config.FONT.size = newfont.size
end -- if
config.ROOM_NAME_TEXT.colour = newfont.colour
-- reload new font
WindowFont (win, FONT_ID, config.FONT.name, config.FONT.size)
WindowFont (win, FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
-- see how high it is
font_height = WindowFontInfo (win, FONT_ID, 1) -- height
draw (current_room)
end -- mouseup_change_font
function mouseup_change_width (flags, hotspot_id)
local width = get_number_from_user ("Choose window width (200 to 1000 pixels)", "Width", config.WINDOW.width, 200, 1000)
if not width then
return
end -- if dismissed
config.WINDOW.width = width
draw (current_room)
end -- mouseup_change_width
function mouseup_change_height (flags, hotspot_id)
local height = get_number_from_user ("Choose window height (200 to 1000 pixels)", "Width", config.WINDOW.height, 200, 1000)
if not height then
return
end -- if dismissed
config.WINDOW.height = height
draw (current_room)
end -- mouseup_change_height
function mouseup_change_depth (flags, hotspot_id)
local depth = get_number_from_user ("Choose scan depth (3 to 100 rooms)", "Depth", config.SCAN.depth, 3, 100)
if not depth then
return
end -- if dismissed
config.SCAN.depth = depth
draw (current_room)
end -- mouseup_change_depth
function mouseup_change_delay (flags, hotspot_id)
local delay = get_number_from_user ("Choose speedwalk delay time (0 to 10 seconds)", "Delay in seconds", config.DELAY.time, 0, 10, true)
if not delay then
return
end -- if dismissed
config.DELAY.time = delay
draw (current_room)
end -- mouseup_change_delay
function zoom_map (flags, hotspot_id)
if bit.band (flags, 0x100) ~= 0 then
zoom_out ()
else
zoom_in ()
end -- if
end -- zoom_map
Jump to Line
Something went wrong with that request. Please try again.