· 7 years ago · Jan 27, 2019, 12:22 AM
1-- mapper.lua
2
3--[[
4
5Authors: Original by Nick Gammon. Modified heavily for Aardwolf by Fiendish.
6
7Generic MUD mapper.
8
9Exposed functions:
10
11init (t) -- call once, supply:
12 t.findpath -- function for finding the path between two rooms (src, dest)
13 t.config -- ie. colours, sizes
14 t.get_room -- info about room (uid)
15 t.show_help -- function that displays some help
16 t.room_click -- function that handles RH click on room (uid, flags)
17 t.timing -- true to show timing
18 t.show_completed -- true to show "Speedwalk completed."
19 t.show_other_areas -- true to show non-current areas
20 t.show_up_down -- follow up/down exits
21 t.speedwalk_prefix -- if not nil, speedwalk by prefixing with this
22
23zoom_in () -- zoom in map view
24zoom_out () -- zoom out map view
25mapprint (message) -- like print, but uses mapper colour
26maperror (message) -- like print, but prints in red
27hide () -- hides map window (eg. if plugin disabled)
28show () -- show map window (eg. if plugin enabled)
29save_state () -- call to save plugin state (ie. in OnPluginSaveState)
30draw (uid) -- draw map - starting at room 'uid'
31start_speedwalk (path) -- starts speedwalking. path is a table of directions/uids
32build_speedwalk (path) -- builds a client speedwalk string from path
33cancel_speedwalk () -- cancel current speedwalk, if any
34check_we_can_find () -- returns true if doing a find is OK right now
35find (f, show_uid, count, walk) -- generic room finder
36
37Exposed variables:
38
39win -- the window (in case you want to put up menus)
40VERSION -- mapper version
41last_hyperlink_uid -- room uid of last hyperlink click (destination)
42last_speedwalk_uid -- room uid of last speedwalk attempted (destination)
43<various functions> -- functions required to be global by the client (eg. for mouseup)
44
45Room info should include:
46
47 name (what to show as room name)
48 exits (table keyed by direction, value is exit uid)
49 area (area name)
50 hovermessage (what to show when you mouse-over the room)
51 bordercolour (colour of room border) - RGB colour
52 borderpen (pen style of room border) - see WindowCircleOp (values 0 to 6)
53 borderpenwidth(pen width of room border) - eg. 1 for normal, 2 for current room
54 fillcolour (colour to fill room) - RGB colour, nil for default
55 fillbrush (brush to fill room) - see WindowCircleOp (values 0 to 12)
56 texture (background texture file) - cached in textures
57
58--]]
59
60module (..., package.seeall)
61
62VERSION = 2.5 -- for querying by plugins
63require "aard_register_z_on_create"
64
65require "mw_theme_base"
66require "movewindow"
67require "copytable"
68require "gauge"
69require "pairsbykeys"
70require "mw"
71
72local FONT_ID = "fn" -- internal font identifier
73local FONT_ID_UL = "fnu" -- internal font identifier - underlined
74local CONFIG_FONT_ID = "cfn"
75local CONFIG_FONT_ID_UL = "cfnu"
76
77-- size of room box
78local ROOM_SIZE = tonumber(GetVariable("ROOM_SIZE")) or 12
79
80-- how far away to draw rooms from each other
81local DISTANCE_TO_NEXT_ROOM = tonumber(GetVariable("DISTANCE_TO_NEXT_ROOM")) or 8
82
83-- supplied in init
84local supplied_get_room
85local room_click
86local timing -- true to show timing and other info
87local show_completed -- true to show "Speedwalk completed."
88
89-- current room number
90local current_room
91
92-- our copy of rooms info
93local rooms = {}
94local last_visited = {}
95local textures = {}
96
97-- other locals
98local HALF_ROOM, connectors, half_connectors, arrows
99local plan_to_draw, speedwalks, drawn, drawn_coords
100local last_drawn, depth, font_height
101local walk_to_room_name
102local total_times_drawn = 0
103local total_time_taken = 0
104
105default_width = 269
106default_height = 335
107default_x = 868
108default_y = 0
109
110function reset_pos()
111 config.WINDOW.width = default_width
112 config.WINDOW.height = default_height
113 WindowPosition(win, default_x, default_y, 0, 18)
114 WindowResize(win, default_width, default_height, BACKGROUND_COLOUR.colour)
115 Repaint() -- hack because WindowPosition doesn't immediately update coordinates
116end
117
118local function build_room_info ()
119
120 HALF_ROOM = math.ceil(ROOM_SIZE / 2)
121 local THIRD_WAY = math.ceil(DISTANCE_TO_NEXT_ROOM / 3)
122 local HALF_WAY = math.ceil(DISTANCE_TO_NEXT_ROOM / 2)
123 local DISTANCE_LESS1 = DISTANCE_TO_NEXT_ROOM - 1
124
125 barriers = {
126 n = { x1 = -HALF_ROOM, y1 = -HALF_ROOM, x2 = HALF_ROOM, y2 = -HALF_ROOM},
127 s = { x1 = -HALF_ROOM, y1 = HALF_ROOM, x2 = HALF_ROOM, y2 = HALF_ROOM},
128 e = { x1 = HALF_ROOM, y1 = -HALF_ROOM, x2 = HALF_ROOM, y2 = HALF_ROOM},
129 w = { x1 = -HALF_ROOM, y1 = -HALF_ROOM, x2 = -HALF_ROOM, y2 = HALF_ROOM},
130
131 u = { x1 = HALF_ROOM-HALF_WAY, y1 = -HALF_ROOM-HALF_WAY, x2 = HALF_ROOM+HALF_WAY, y2 = -HALF_ROOM+HALF_WAY},
132 d = { x1 = -HALF_ROOM+HALF_WAY, y1 = HALF_ROOM+HALF_WAY, x2 = -HALF_ROOM-HALF_WAY, y2 = HALF_ROOM-HALF_WAY},
133
134
135 } -- end barriers
136
137 -- how to draw a line from this room to the next one (relative to the center of the room)
138 connectors = {
139 n = { x1 = 0, y1 = - HALF_ROOM, x2 = 0, y2 = - HALF_ROOM - HALF_WAY, at = { 0, -1 } },
140 s = { x1 = 0, y1 = HALF_ROOM, x2 = 0, y2 = HALF_ROOM + HALF_WAY, at = { 0, 1 } },
141 e = { x1 = HALF_ROOM, y1 = 0, x2 = HALF_ROOM + HALF_WAY, y2 = 0, at = { 1, 0 }},
142 w = { x1 = - HALF_ROOM, y1 = 0, x2 = - HALF_ROOM - HALF_WAY, y2 = 0, at = { -1, 0 }},
143
144 u = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + HALF_WAY , y2 = - HALF_ROOM - HALF_WAY, at = { 1, -1 } },
145 d = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - HALF_WAY , y2 = HALF_ROOM + HALF_WAY, at = {-1, 1 } },
146 ne = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + DISTANCE_LESS1 , y2 = - HALF_ROOM - DISTANCE_LESS1, at = { 1, -1 } },
147 se = { x1 = HALF_ROOM, y1 = HALF_ROOM, x2 = HALF_ROOM + DISTANCE_LESS1 , y2 = HALF_ROOM + DISTANCE_LESS1, at = { 1, 1 } },
148 nw = { x1 = - HALF_ROOM, y1 = - HALF_ROOM, x2 = - HALF_ROOM - DISTANCE_LESS1 , y2 = - HALF_ROOM - DISTANCE_LESS1, at = {-1, -1 } },
149 sw = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - DISTANCE_LESS1 , y2 = HALF_ROOM + DISTANCE_LESS1, at = {-1, 1 } },
150
151
152 } -- end connectors
153
154 -- how to draw a stub line
155 half_connectors = {
156 n = { x1 = 0, y1 = - HALF_ROOM, x2 = 0, y2 = - HALF_ROOM - THIRD_WAY, at = { 0, -1 } },
157 s = { x1 = 0, y1 = HALF_ROOM, x2 = 0, y2 = HALF_ROOM + THIRD_WAY, at = { 0, 1 } },
158 e = { x1 = HALF_ROOM, y1 = 0, x2 = HALF_ROOM + THIRD_WAY, y2 = 0, at = { 1, 0 }},
159 w = { x1 = - HALF_ROOM, y1 = 0, x2 = - HALF_ROOM - THIRD_WAY, y2 = 0, at = { -1, 0 }},
160
161 u = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + THIRD_WAY , y2 = - HALF_ROOM - THIRD_WAY, at = { 1, -1 } },
162 d = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - THIRD_WAY , y2 = HALF_ROOM + THIRD_WAY, at = {-1, 1 } },
163 ne = { x1 = HALF_ROOM, y1 = - HALF_ROOM, x2 = HALF_ROOM + THIRD_WAY , y2 = - HALF_ROOM - THIRD_WAY, at = { 1, -1 } },
164 se = { x1 = HALF_ROOM, y1 = HALF_ROOM, x2 = HALF_ROOM + THIRD_WAY , y2 = HALF_ROOM + THIRD_WAY, at = { 1, 1 } },
165 nw = { x1 = - HALF_ROOM, y1 = - HALF_ROOM, x2 = - HALF_ROOM - THIRD_WAY , y2 = - HALF_ROOM - THIRD_WAY, at = {-1, -1 } },
166 sw = { x1 = - HALF_ROOM, y1 = HALF_ROOM, x2 = - HALF_ROOM - THIRD_WAY , y2 = HALF_ROOM + THIRD_WAY, at = {-1, 1 } },
167
168 } -- end half_connectors
169
170 -- how to draw one-way arrows (relative to the center of the room)
171 arrows = {
172 n = { - 2, - HALF_ROOM - 2, 2, - HALF_ROOM - 2, 0, - HALF_ROOM - 6 },
173 s = { - 2, HALF_ROOM + 2, 2, HALF_ROOM + 2, 0, HALF_ROOM + 6 },
174 e = { HALF_ROOM + 2, -2, HALF_ROOM + 2, 2, HALF_ROOM + 6, 0 },
175 w = { - HALF_ROOM - 2, -2, - HALF_ROOM - 2, 2, - HALF_ROOM - 6, 0 },
176
177 u = { HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3, - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3 },
178 d = { - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3, HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3},
179 ne = { HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3, - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3 },
180 se = { HALF_ROOM + 3, HALF_ROOM, HALF_ROOM + 3, HALF_ROOM + 3, HALF_ROOM, HALF_ROOM + 3 },
181 nw = { - HALF_ROOM - 3, - HALF_ROOM, - HALF_ROOM - 3, - HALF_ROOM - 3, - HALF_ROOM, - HALF_ROOM - 3 },
182 sw = { - HALF_ROOM - 3, HALF_ROOM, - HALF_ROOM - 3, HALF_ROOM + 3, - HALF_ROOM, HALF_ROOM + 3},
183
184 } -- end of arrows
185
186end -- build_room_info
187
188-- assorted colours
189BACKGROUND_COLOUR = { name = "Area Background", colour = ColourNameToRGB "#111111"}
190ROOM_COLOUR = { name = "Room", colour = ColourNameToRGB "#dcdcdc"}
191EXIT_COLOUR = { name = "Exit", colour = ColourNameToRGB "#e0ffff"}
192EXIT_COLOUR_UP_DOWN = { name = "Exit up/down", colour = ColourNameToRGB "#ffb6c1"}
193ROOM_NOTE_COLOUR = { name = "Room notes", colour = ColourNameToRGB "lightgreen"}
194OUR_ROOM_COLOUR = { name = "Our room", colour = ColourNameToRGB "#ff1493"}
195UNKNOWN_ROOM_COLOUR = { name = "Unknown room", colour = ColourNameToRGB "#8b0000"}
196DIFFERENT_AREA_COLOUR = { name = "Another area", colour = ColourNameToRGB "#ff0000"}
197PK_BORDER_COLOUR = { name = "PK border", colour = ColourNameToRGB "red"}
198SHOP_FILL_COLOUR = { name = "Shop", colour = ColourNameToRGB "#ffad2f"}
199HEALER_FILL_COLOUR = { name = "Healer", colour = ColourNameToRGB "#9acd32"}
200TRAINER_FILL_COLOUR = { name = "Trainer", colour = ColourNameToRGB "#9acd32"}
201QUESTOR_FILL_COLOUR = { name = "Questor", colour = ColourNameToRGB "deepskyblue"}
202BANK_FILL_COLOUR = { name = "Bank", colour = ColourNameToRGB "#ffD700"}
203GUILD_FILL_COLOUR = { name = "Guild", colour = ColourNameToRGB "magenta"}
204SAFEROOM_FILL_COLOUR = { name = "Safe room", colour = ColourNameToRGB "lightblue"}
205MAPPER_NOTE_COLOUR = { name = "Messages", colour = ColourNameToRGB "lightgreen"}
206
207ROOM_NAME_TEXT = { name = "Room name text", colour = ColourNameToRGB "#BEF3F1"}
208ROOM_NAME_FILL = { name = "Room name fill", colour = ColourNameToRGB "#105653"}
209ROOM_NAME_BORDER = { name = "Room name box", colour = ColourNameToRGB "black"}
210
211AREA_NAME_TEXT = { name = "Area name text", colour = ColourNameToRGB "#BEF3F1"}
212AREA_NAME_FILL = { name = "Area name fill", colour = ColourNameToRGB "#105653"}
213AREA_NAME_BORDER = { name = "Area name box", colour = ColourNameToRGB "black"}
214
215-- how many seconds to show "recent visit" lines (default 3 minutes)
216LAST_VISIT_TIME = 60 * 3
217
218default_config = {
219 FONT = { name = get_preferred_font {"Dina", "Lucida Console", "Fixedsys", "Courier",} ,
220 size = 8
221 } ,
222
223 -- size of map window
224 WINDOW = { width = default_width, height = default_height },
225
226 -- how far from where we are standing to draw (rooms)
227 SCAN = { depth = 300 },
228
229 -- speedwalk delay
230 DELAY = { time = 0.3 },
231
232 -- show custom tiling background textures
233 USE_TEXTURES = { enabled = true },
234
235 SHOW_ROOM_ID = false,
236
237 SHOW_AREA_EXITS = false
238}
239
240local expand_direction = {
241 n = "north",
242 s = "south",
243 e = "east",
244 w = "west",
245 u = "up",
246 d = "down",
247} -- end of expand_direction
248
249local function get_room (uid)
250 local room = supplied_get_room (uid)
251 room = room or { unknown = true }
252
253 -- defaults in case they didn't supply them ...
254 room.name = room.name or string.format ("Room %s", uid)
255 room.name = mw.strip_colours (room.name) -- no colour codes for now
256 room.exits = room.exits or {}
257 room.area = room.area or "<No area>"
258 room.hovermessage = room.hovermessage or "<Unexplored room>"
259 room.bordercolour = room.bordercolour or ROOM_COLOUR.colour
260 room.borderpen = room.borderpen or 0 -- solid
261 room.borderpenwidth = room.borderpenwidth or 1
262 room.fillcolour = room.fillcolour or 0x000000
263 room.fillbrush = room.fillbrush or 1 -- no fill
264 room.texture = room.texture or nil -- no texture
265
266 room.textimage = nil
267
268 if room.texture == nil or room.texture == "" then room.texture = "test5.png" end
269 if textures[room.texture] then
270 room.textimage = textures[room.texture] -- assign image
271 else
272 if textures[room.texture] ~= false then
273 local dir = GetInfo(66)
274 imgpath = dir .. "worlds\\plugins\\images\\" ..room.texture
275 if WindowLoadImage(win, room.texture, imgpath) ~= 0 then
276 textures[room.texture] = false -- just indicates not found
277 else
278 textures[room.texture] = room.texture -- imagename
279 room.textimage = room.texture
280 end
281 end
282 end
283
284 return room
285
286end -- get_room
287
288function check_connected ()
289 if not IsConnected() then
290 mapprint ("You are not connected to", WorldName())
291 return false
292 end -- if not connected
293 return true
294end -- check_connected
295
296local function make_number_checker (title, min, max, decimals)
297 return function (s)
298 local n = tonumber (s)
299 if not n then
300 utils.msgbox (title .. " must be a number", "Incorrect input", "ok", "!", 1)
301 return false -- bad input
302 end -- if
303 if n < min or n > max then
304 utils.msgbox (title .. " must be in range " .. min .. " to " .. max, "Incorrect input", "ok", "!", 1)
305 return false -- bad input
306 end -- if
307 if not decimals then
308 if string.match (s, "%.") then
309 utils.msgbox (title .. " cannot have decimal places", "Incorrect input", "ok", "!", 1)
310 return false -- bad input
311 end -- if
312 end -- no decimals
313 return true -- good input
314 end -- generated function
315end -- make_number_checker
316
317
318local function get_number_from_user (msg, title, current, min, max, decimals)
319 local max_length = math.ceil (math.log10 (max) + 1)
320
321 -- if decimals allowed, allow room for them
322 if decimals then
323 max_length = max_length + 2 -- allow for 0.x
324 end -- if
325
326 -- if can be negative, allow for minus sign
327 if min < 0 then
328 max_length = max_length + 1
329 end -- if can be negative
330
331 return tonumber (utils.inputbox (msg, title, current, nil, nil,
332 { validate = make_number_checker (title, min, max, decimals),
333 prompt_height = 14,
334 box_height = 130,
335 box_width = 300,
336 reply_width = 150,
337 max_length = max_length,
338 } -- end extra stuff
339 ))
340end -- get_number_from_user
341
342local function draw_configuration ()
343
344 local config_entries = {"Map Configuration", "Show Room ID", "Show Area Exits", "Font", "Depth", "Area Textures", "Room size"}
345 local width = max_text_width (config_win, CONFIG_FONT_ID, config_entries , true)
346 local GAP = 5
347
348 local x = 0
349 local y = 0
350 local box_size = font_height - 2
351 local rh_size = math.max (box_size, max_text_width (config_win, CONFIG_FONT_ID,
352 {config.FONT.name .. " " .. config.FONT.size,
353 ((config.USE_TEXTURES.enabled and "On") or "Off"),
354 "- +",
355 tostring (config.SCAN.depth)},
356 true))
357 local frame_width = GAP + width + GAP + rh_size + GAP -- gap / text / gap / box / gap
358
359 WindowCreate(config_win, windowinfo.window_left, windowinfo.window_top, frame_width, font_height * #config_entries + GAP+GAP, windowinfo.window_mode, windowinfo.window_flags, 0xDCDCDC)
360 WindowSetZOrder(config_win, 99999) -- always on top
361
362 -- frame it
363 draw_3d_box (config_win, 0, 0, frame_width, font_height * #config_entries + GAP+GAP)
364
365 y = y + GAP
366 x = x + GAP
367
368 -- title
369 WindowText (config_win, CONFIG_FONT_ID, "Map Configuration", ((frame_width-WindowTextWidth(config_win,CONFIG_FONT_ID,"Map Configuration"))/2), y, 0, 0, 0x808080)
370
371 -- close box
372 WindowRectOp (config_win,
373 miniwin.rect_frame,
374 x,
375 y + 1,
376 x + box_size,
377 y + 1 + box_size,
378 0x808080)
379 WindowLine (config_win,
380 x + 3,
381 y + 4,
382 x + box_size - 3,
383 y - 2 + box_size,
384 0x808080,
385 miniwin.pen_solid, 1)
386 WindowLine (config_win,
387 x + box_size - 4,
388 y + 4,
389 x + 2,
390 y - 2 + box_size,
391 0x808080,
392 miniwin.pen_solid, 1)
393
394 -- close configuration hotspot
395 WindowAddHotspot(config_win, "$<close_configure>",
396 x,
397 y + 1,
398 x + box_size,
399 y + 1 + box_size, -- rectangle
400 "", "", "", "", "mapper.mouseup_close_configure", -- mouseup
401 "Click to close",
402 miniwin.cursor_hand, 0) -- hand cursor
403
404 y = y + font_height
405
406 -- depth
407 WindowText(config_win, CONFIG_FONT_ID, "Depth", x, y, 0, 0, 0x000000)
408 WindowText(config_win, CONFIG_FONT_ID_UL, tostring (config.SCAN.depth), width + rh_size / 2 + box_size - WindowTextWidth(config_win, CONFIG_FONT_ID_UL, config.SCAN.depth)/2, y, 0, 0, 0x808080)
409
410 -- depth hotspot
411 WindowAddHotspot(config_win,
412 "$<depth>",
413 x + GAP,
414 y,
415 x + frame_width,
416 y + font_height, -- rectangle
417 "", "", "", "", "mapper.mouseup_change_depth", -- mouseup
418 "Click to change scan depth",
419 miniwin.cursor_hand, 0) -- hand cursor
420 y = y + font_height
421
422 -- font
423 WindowText(config_win, CONFIG_FONT_ID, "Font", x, y, 0, 0, 0x000000)
424 WindowText(config_win, CONFIG_FONT_ID_UL, config.FONT.name .. " " .. config.FONT.size, x + width + GAP, y, 0, 0, 0x808080)
425
426 -- font hotspot
427 WindowAddHotspot(config_win,
428 "$<font>",
429 x + GAP,
430 y,
431 x + frame_width,
432 y + font_height, -- rectangle
433 "", "", "", "", "mapper.mouseup_change_font", -- mouseup
434 "Click to change font",
435 miniwin.cursor_hand, 0) -- hand cursor
436 y = y + font_height
437
438 -- area textures
439 WindowText(config_win, CONFIG_FONT_ID, "Area Textures", x, y, 0, 0, 0x000000)
440 WindowText(config_win, CONFIG_FONT_ID_UL, ((config.USE_TEXTURES.enabled and "On") or "Off"), width + rh_size / 2 + box_size - WindowTextWidth(config_win, CONFIG_FONT_ID_UL, ((config.USE_TEXTURES.enabled and "On") or "Off"))/2, y, 0, 0, 0x808080)
441
442 -- area textures hotspot
443 WindowAddHotspot(config_win,
444 "$<area_textures>",
445 x + GAP,
446 y,
447 x + frame_width,
448 y + font_height, -- rectangle
449 "", "", "", "", "mapper.mouseup_change_area_textures", -- mouseup
450 "Click to toggle use of area textures",
451 miniwin.cursor_hand, 0) -- hand cursor
452 y = y + font_height
453
454
455 -- show ID
456 WindowText(config_win, CONFIG_FONT_ID, "Show Room ID", x, y, 0, 0, 0x000000)
457 WindowText(config_win, CONFIG_FONT_ID_UL, ((config.SHOW_ROOM_ID and "On") or "Off"), width + rh_size / 2 + box_size - WindowTextWidth(config_win, CONFIG_FONT_ID_UL, ((config.SHOW_ROOM_ID and "On") or "Off"))/2, y, 0, 0, 0x808080)
458
459 -- show ID hotspot
460 WindowAddHotspot(config_win,
461 "$<room_id>",
462 x + GAP,
463 y,
464 x + frame_width,
465 y + font_height, -- rectangle
466 "", "", "", "", "mapper.mouseup_change_show_id", -- mouseup
467 "Click to toggle display of room UID",
468 miniwin.cursor_hand, 0) -- hand cursor
469 y = y + font_height
470
471
472 -- show area exits
473 WindowText(config_win, CONFIG_FONT_ID, "Show Area Exits", x, y, 0, 0, 0x000000)
474 WindowText(config_win, CONFIG_FONT_ID_UL, ((config.SHOW_AREA_EXITS and "On") or "Off"), width + rh_size / 2 + box_size - WindowTextWidth(config_win, CONFIG_FONT_ID_UL, ((config.SHOW_AREA_EXITS and "On") or "Off"))/2, y, 0, 0, 0x808080)
475
476 -- show area exits hotspot
477 WindowAddHotspot(config_win,
478 "$<area_exits>",
479 x + GAP,
480 y,
481 x + frame_width,
482 y + font_height, -- rectangle
483 "", "", "", "", "mapper.mouseup_change_show_area_exits", -- mouseup
484 "Click to toggle display of area exits",
485 miniwin.cursor_hand, 0) -- hand cursor
486 y = y + font_height
487
488
489 -- room size
490 WindowText(config_win, CONFIG_FONT_ID, "Room size", x, y, 0, 0, 0x000000)
491 WindowText(config_win, CONFIG_FONT_ID, "("..tostring (ROOM_SIZE)..")", x + WindowTextWidth(config_win, CONFIG_FONT_ID, "Room size "), y, 0, 0, 0x808080)
492 WindowText(config_win, CONFIG_FONT_ID_UL, "-", width + rh_size / 2 + box_size/2 - WindowTextWidth(config_win,CONFIG_FONT_ID,"-"), y, 0, 0, 0x808080)
493 WindowText(config_win, CONFIG_FONT_ID_UL, "+", width + rh_size / 2 + box_size + GAP, y, 0, 0, 0x808080)
494
495 -- room size hotspots
496 WindowAddHotspot(config_win,
497 "$<room_size_down>",
498 width + rh_size / 2 + box_size/2 - WindowTextWidth(config_win,CONFIG_FONT_ID,"-"),
499 y,
500 width + rh_size / 2 + box_size/2 + WindowTextWidth(config_win,CONFIG_FONT_ID,"-"),
501 y + font_height, -- rectangle
502 "", "", "", "", "mapper.zoom_out", -- mouseup
503 "Click to zoom out",
504 miniwin.cursor_hand, 0) -- hand cursor
505 WindowAddHotspot(config_win,
506 "$<room_size_up>",
507 width + rh_size / 2 + box_size + GAP,
508 y,
509 width + rh_size / 2 + box_size + GAP + WindowTextWidth(config_win,CONFIG_FONT_ID,"+"),
510 y + font_height, -- rectangle
511 "", "", "", "", "mapper.zoom_in", -- mouseup
512 "Click to zoom in",
513 miniwin.cursor_hand, 0) -- hand cursor
514 y = y + font_height
515
516
517
518 WindowShow(config_win, true)
519end -- draw_configuration
520
521-- for calculating one-way paths
522local inverse_direction = {
523 n = "s",
524 s = "n",
525 e = "w",
526 w = "e",
527 u = "d",
528 d = "u",
529 ne = "sw",
530 se = "nw",
531 sw = "ne",
532 nw = "se"
533} -- end of inverse_direction
534
535local function add_another_room (uid, path, x, y)
536 local path = path or {}
537 return {uid=uid, path=path, x = x, y = y}
538end -- add_another_room
539
540local function draw_room (uid, path, x, y)
541
542 local coords = string.format ("%i,%i", math.floor (x), math.floor (y))
543
544 -- need this for the *current* room !!!
545 drawn_coords [coords] = uid
546
547 -- print ("drawing", uid, "at", coords)
548
549 if drawn [uid] then
550 return
551 end -- done this one
552
553 -- don't draw the same room more than once
554 drawn [uid] = { coords = coords, path = path }
555
556 local room = rooms [uid]
557
558 -- not cached - get from caller
559 if not room then
560 room = get_room (uid)
561 rooms [uid] = room
562 end -- not in cache
563
564
565 local left, top, right, bottom = x - HALF_ROOM, y - HALF_ROOM, x + HALF_ROOM, y + HALF_ROOM
566
567 -- forget it if off screen
568 if (x < HALF_ROOM) or (y < (title_bottom or font_height)+HALF_ROOM) or
569 (x > config.WINDOW.width - HALF_ROOM) or (y > config.WINDOW.height - HALF_ROOM) then
570 return
571 end -- if
572
573 -- exits
574
575 local texits = {}
576
577 for dir, exit_uid in pairs (room.exits) do
578 table.insert (texits, dir)
579 local exit_info = connectors [dir]
580 local stub_exit_info = half_connectors [dir]
581 local locked_exit = not (room.exit_locks == nil or room.exit_locks[dir] == nil or room.exit_locks[dir] == "0")
582 local exit_line_colour = (locked_exit and 0x0000FF) or EXIT_COLOUR.colour
583 local arrow = arrows [dir]
584
585 -- draw up in the ne/nw position if not already an exit there at this level
586 if dir == "u" then
587 exit_line_colour = (locked_exit and 0x0000FF) or EXIT_COLOUR_UP_DOWN.colour
588 elseif dir == "d" then
589 exit_line_colour = (locked_exit and 0x0000FF) or EXIT_COLOUR_UP_DOWN.colour
590 end -- if down
591
592 if exit_info then
593 local linetype = miniwin.pen_solid -- unbroken
594 local linewidth = (locked_exit and 2) or 1 -- not recent
595
596 -- try to cache room
597 if not rooms [exit_uid] then
598 rooms [exit_uid] = get_room (exit_uid)
599 end -- if
600
601 if rooms [exit_uid].unknown then
602 linetype = miniwin.pen_dot -- dots
603 end -- if
604
605 local next_x = x + exit_info.at [1] * (ROOM_SIZE + DISTANCE_TO_NEXT_ROOM)
606 local next_y = y + exit_info.at [2] * (ROOM_SIZE + DISTANCE_TO_NEXT_ROOM)
607
608 local next_coords = string.format ("%i,%i", math.floor (next_x), math.floor (next_y))
609
610 -- remember if a zone exit (first one only)
611 if config.SHOW_AREA_EXITS and room.area ~= rooms [exit_uid].area and not rooms[exit_uid].unknown then
612 area_exits [ rooms [exit_uid].area ] = area_exits [ rooms [exit_uid].area ] or {x = x, y = y, def = barriers[dir]}
613 end -- if
614
615 -- if another room (not where this one leads to) is already there, only draw "stub" lines
616 if drawn_coords [next_coords] and drawn_coords [next_coords] ~= exit_uid then
617 exit_info = stub_exit_info
618 elseif exit_uid == uid then
619 -- here if room leads back to itself
620 exit_info = stub_exit_info
621 linetype = miniwin.pen_dash -- dash
622 else
623 --if (not show_other_areas and rooms [exit_uid].area ~= current_area) or
624 if (not show_other_areas and rooms [exit_uid].area ~= current_area and not rooms[exit_uid].unknown) or
625 (not show_up_down and (dir == "u" or dir == "d")) then
626 exit_info = stub_exit_info -- don't show other areas
627 else
628 -- if we are scheduled to draw the room already, only draw a stub this time
629 if plan_to_draw [exit_uid] and plan_to_draw [exit_uid] ~= next_coords then
630 -- here if room already going to be drawn
631 exit_info = stub_exit_info
632 linetype = miniwin.pen_dash -- dash
633 else
634 -- remember to draw room next iteration
635 local new_path = copytable.deep (path)
636 table.insert (new_path, { dir = dir, uid = exit_uid })
637 table.insert (rooms_to_be_drawn, add_another_room (exit_uid, new_path, next_x, next_y))
638 drawn_coords [next_coords] = exit_uid
639 plan_to_draw [exit_uid] = next_coords
640
641 -- if exit room known
642 if not rooms [exit_uid].unknown then
643 local exit_time = last_visited [exit_uid] or 0
644 local this_time = last_visited [uid] or 0
645 local now = os.time ()
646 if exit_time > (now - LAST_VISIT_TIME) and
647 this_time > (now - LAST_VISIT_TIME) then
648 linewidth = 2
649 end -- if
650 end -- if
651 end -- if
652 end -- if
653 end -- if drawn on this spot
654
655 WindowLine (win, x + exit_info.x1, y + exit_info.y1, x + exit_info.x2, y + exit_info.y2, exit_line_colour, linetype + 0x0200, linewidth)
656
657 -- one-way exit?
658
659 if not rooms [exit_uid].unknown then
660 local dest = rooms [exit_uid]
661 -- if inverse direction doesn't point back to us, this is one-way
662 if dest.exits [inverse_direction [dir]] ~= uid then
663 -- turn points into string, relative to where the room is
664 local points = string.format ("%i,%i,%i,%i,%i,%i",
665 x + arrow [1],
666 y + arrow [2],
667 x + arrow [3],
668 y + arrow [4],
669 x + arrow [5],
670 y + arrow [6])
671
672 -- draw arrow
673 WindowPolygon(win, points,
674 exit_line_colour, miniwin.pen_solid, 1,
675 exit_line_colour, miniwin.brush_solid,
676 true, true)
677 end -- one way
678 end -- if we know of the room where it does
679 end -- if we know what to do with this direction
680 end -- for each exit
681
682
683 if room.unknown then
684 WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
685 UNKNOWN_ROOM_COLOUR.colour, miniwin.pen_dot, 1, -- dotted single pixel pen
686 -1, miniwin.brush_null) -- opaque, no brush
687 else
688 -- room fill
689 WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
690 0, miniwin.pen_null, 0, -- no pen
691 room.fillcolour, room.fillbrush) -- brush
692
693 -- room border
694 WindowCircleOp (win, miniwin.circle_rectangle, left, top, right, bottom,
695 room.bordercolour, room.borderpen, room.borderpenwidth, -- pen
696 -1, miniwin.brush_null) -- opaque, no brush
697
698 -- mark rooms with notes
699 if room.notes ~= nil and room.notes ~= "" then
700 WindowCircleOp (win, miniwin.circle_rectangle, left-1-room.borderpenwidth, top-1-room.borderpenwidth,
701 right+1+room.borderpenwidth, bottom+1+room.borderpenwidth,ROOM_NOTE_COLOUR.colour,
702 room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)
703 end
704 end -- if
705
706 speedwalks [uid] = path -- so we know how to get here
707
708 WindowAddHotspot(win, uid,
709 left, top, right, bottom, -- rectangle
710 "", -- mouseover
711 "", -- cancelmouseover
712 "", -- mousedown
713 "", -- cancelmousedown
714 "mapper.mouseup_room", -- mouseup
715 room.hovermessage,
716 miniwin.cursor_hand, 0) -- hand cursor
717
718 WindowScrollwheelHandler (win, uid, "mapper.zoom_map")
719end -- draw_room
720
721local function changed_room (uid)
722
723 hyperlink_paths = nil -- those hyperlinks are meaningless now
724 speedwalks = {} -- old speedwalks are irrelevant
725
726 if current_speedwalk then
727
728 if uid ~= expected_room then
729 local exp = rooms [expected_room]
730 if not exp then
731 exp = get_room (expected_room) or { name = expected_room }
732 end -- if
733 local here = rooms [uid]
734 if not here then
735 here = get_room (uid) or { name = uid }
736 end -- if
737 exp = expected_room
738 here = uid
739 maperror (string.format ("Speedwalk failed! Expected to be in '%s' but ended up in '%s'.", exp or "<none>", here))
740 cancel_speedwalk ()
741 else
742 if #current_speedwalk > 0 then
743 local dir = table.remove (current_speedwalk, 1)
744 SetStatus ("Walking " .. (expand_direction [dir.dir] or dir.dir) ..
745 " to " .. walk_to_room_name ..
746 ". Speedwalks to go: " .. #current_speedwalk + 1)
747 expected_room = dir.uid
748 if config.DELAY.time > 0 then
749 if GetOption ("enable_timers") ~= 1 then
750 maperror ("WARNING! Timers not enabled. Speedwalking will not work properly.")
751 end -- if timers disabled
752 DoAfter (config.DELAY.time, dir.dir)
753 else
754 Send (dir.dir)
755 end -- if
756 else
757 last_hyperlink_uid = nil
758 last_speedwalk_uid = nil
759 if show_completed then
760 mapprint ("Speedwalk completed.")
761 end -- if wanted
762 cancel_speedwalk ()
763 end -- if any left
764 end -- if expected room or not
765 end -- if have a current speedwalk
766
767end -- changed_room
768
769local function draw_zone_exit (exit)
770 local x, y, def = exit.x, exit.y, exit.def
771 local offset = ROOM_SIZE
772
773 WindowLine (win, x + def.x1, y + def.y1, x + def.x2, y + def.y2, ColourNameToRGB("yellow"), miniwin.pen_solid + 0x0200, 5)
774 WindowLine (win, x + def.x1, y + def.y1, x + def.x2, y + def.y2, ColourNameToRGB("green"), miniwin.pen_solid + 0x0200, 1)
775end -- draw_zone_exit
776
777
778----------------------------------------------------------------------------------
779-- EXPOSED FUNCTIONS
780----------------------------------------------------------------------------------
781
782-- can we find another room right now?
783
784function check_we_can_find ()
785 if not current_room then
786 mapprint ("I don't know where you are right now - try: LOOK")
787 check_connected ()
788 return false
789 end
790 if current_speedwalk then
791 mapprint ("The mapper has detected a speedwalk initiated inside another speedwalk. Aborting.")
792 return false
793 end -- if
794 return true
795end -- check_we_can_find
796
797-- draw our map starting at room: uid
798dont_draw = false
799function halt_drawing(halt)
800 dont_draw = halt
801end
802function find_paths (uid, f)
803
804 local function make_particle (curr_loc, prev_path)
805 local prev_path = prev_path or {}
806 return {current_room=curr_loc, path=prev_path}
807 end
808
809 local depth = 0
810 local count = 0
811 local done = false
812 local found, reason
813 local explored_rooms, particles = {}, {}
814
815 -- this is where we collect found paths
816 -- the table is keyed by destination, with paths as values
817 local paths = {}
818
819 -- create particle for the initial room
820 table.insert (particles, make_particle (uid))
821
822 while (not done) and #particles > 0 and depth < config.SCAN.depth do
823
824 -- create a new generation of particles
825 new_generation = {}
826 depth = depth + 1
827
828 SetStatus (string.format ("Scanning: %i/%i depth (%i rooms)", depth, config.SCAN.depth, count))
829
830 -- process each active particle
831 for i, part in ipairs (particles) do
832
833 count = count + 1
834
835 if not rooms [part.current_room] then
836 rooms [part.current_room] = get_room (part.current_room)
837 end -- if not in memory yet
838
839 -- if room doesn't exist, forget it
840 if rooms [part.current_room] then
841
842 -- get a list of exits from the current room
843 exits = rooms [part.current_room].exits
844
845 -- create one new particle for each exit
846 for dir, dest in pairs(exits) do
847
848 -- if we've been in this room before, drop it
849 if not explored_rooms[dest] then
850 explored_rooms[dest] = true
851 rooms [dest] = supplied_get_room (dest) -- make sure this room in table
852 if rooms [dest] then
853 new_path = copytable.deep (part.path)
854 table.insert(new_path, { dir = dir, uid = dest } )
855
856 -- if this room is in the list of destinations then save its path
857 found, done = f (dest)
858 if found then
859 paths[dest] = { path = new_path, reason = found }
860 end -- found one!
861
862 -- make a new particle in the new room
863 table.insert(new_generation, make_particle(dest, new_path))
864 end -- if room exists
865 end -- not explored this room
866 if done then
867 break
868 end
869
870 end -- for each exit
871
872 end -- if room exists
873
874 if done then
875 break
876 end
877 end -- for each particle
878
879 particles = new_generation
880 end -- while more particles
881
882 SetStatus "Ready"
883 return paths, count, depth
884end -- function find_paths
885
886function draw (uid)
887 if not uid then
888 maperror "Cannot draw map right now, I don't know where you are - try: LOOK"
889 return
890 end -- if
891
892 if current_room and current_room ~= uid then
893 changed_room (uid)
894 end -- if
895
896 current_room = uid -- remember where we are
897
898 if dont_draw then
899 return
900 end
901
902 -- timing
903 local start_time = utils.timer ()
904
905 -- start with initial room
906 rooms = { [uid] = get_room (uid) }
907
908 -- lookup current room
909 local room = rooms [uid]
910
911 room = room or { name = "<Unknown room>", area = "<Unknown area>" }
912 last_visited [uid] = os.time ()
913
914 current_area = room.area
915
916 -- update dimensions and position here because the bigmap might have changed them
917 windowinfo.window_left = WindowInfo(win, 1) or windowinfo.window_left
918 windowinfo.window_top = WindowInfo(win, 2) or windowinfo.window_top
919 config.WINDOW.width = WindowInfo(win, 3) or config.WINDOW.width
920 config.WINDOW.height = WindowInfo(win, 4) or config.WINDOW.height
921
922 WindowCreate (win,
923 windowinfo.window_left,
924 windowinfo.window_top,
925 config.WINDOW.width,
926 config.WINDOW.height,
927 windowinfo.window_mode, -- top right
928 windowinfo.window_flags,
929 Theme.PRIMARY_BODY)
930
931 -- Handle background texture.
932 if room.textimage ~= nil and config.USE_TEXTURES.enabled == true then
933 local iwidth = WindowImageInfo(win,room.textimage,2)
934 local iheight= WindowImageInfo(win,room.textimage,3)
935 local x = 0
936 local y = 0
937
938 while y < config.WINDOW.height do
939 x = 0
940 while x < config.WINDOW.width do
941 WindowDrawImage (win, room.textimage, x, y, 0, 0, 1) -- straight copy
942 x = x + iwidth
943 end
944 y = y + iheight
945 end
946 end
947
948 -- for zooming
949 WindowAddHotspot(win,
950 "zzz_zoom",
951 0, 0, 0, 0,
952 "", "", "", "", "mapper.MouseUp",
953 "", -- hint
954 miniwin.cursor_arrow, 0)
955
956 WindowScrollwheelHandler (win, "zzz_zoom", "mapper.zoom_map")
957
958 -- set up for initial room, in middle
959 -- set up for initial room, in middle
960 drawn, drawn_coords, rooms_to_be_drawn, speedwalks, plan_to_draw, area_exits = {}, {}, {}, {}, {}, {}
961 depth = 0
962
963 -- insert initial room
964 table.insert (rooms_to_be_drawn, add_another_room (uid, {}, config.WINDOW.width / 2, config.WINDOW.height / 2))
965
966 while #rooms_to_be_drawn > 0 and depth < config.SCAN.depth do
967 local old_generation = rooms_to_be_drawn
968 rooms_to_be_drawn = {} -- new generation
969 for i, part in ipairs (old_generation) do
970 draw_room (part.uid, part.path, part.x, part.y)
971 end -- for each existing room
972 depth = depth + 1
973 end -- while all rooms_to_be_drawn
974
975 for area, zone_exit in pairs (area_exits) do
976 draw_zone_exit (zone_exit)
977 end -- for
978
979 local room_name = room.name
980 local name_width = WindowTextWidth (win, FONT_ID, room_name)
981 local add_dots = false
982
983 -- truncate name if too long
984 local available_width = (config.WINDOW.width - 20 - WindowTextWidth (win, FONT_ID, "*?"))
985 while name_width > available_width do
986 room_name = room_name:sub(1, -3)
987 name_width = WindowTextWidth (win, FONT_ID, room_name .. "...")
988 add_dots = true
989 if room_name == "" then
990 break
991 end
992 end -- while
993
994 if add_dots then
995 room_name = room_name .. "..."
996 end -- if
997
998 Theme.DrawBorder(win)
999
1000 -- room name
1001 title_bottom = Theme.DrawTitleBar(win, FONT_ID, room_name)
1002
1003 if config.SHOW_ROOM_ID then
1004 Theme.DrawTextBox(win, FONT_ID,
1005 (config.WINDOW.width - WindowTextWidth (win, FONT_ID, "ID: "..uid)) / 2, -- left
1006 title_bottom, -- top
1007 "ID: "..uid, false, false)
1008 end
1009
1010 -- area name
1011
1012 local areaname = room.area
1013
1014 if areaname then
1015 Theme.DrawTextBox(win, FONT_ID,
1016 (config.WINDOW.width - WindowTextWidth (win, FONT_ID, areaname)) / 2, -- left
1017 config.WINDOW.height - 4 - font_height, -- top
1018 areaname:gsub("^%l", string.upper), false, false)
1019 end -- if area known
1020
1021 -- configure?
1022
1023 if draw_configure_box then
1024 draw_configuration ()
1025 else
1026 WindowShow(config_win, false)
1027 local x = 2
1028 local y = math.max(2, (title_bottom-font_height)/2)
1029 local text_width = Theme.DrawTextBox(win, FONT_ID,
1030 x, -- left
1031 y-2, -- top
1032 "*", false, false)
1033
1034 WindowAddHotspot(win, "<configure>",
1035 x-2, y-4, x+text_width, y + font_height, -- rectangle
1036 "", -- mouseover
1037 "", -- cancelmouseover
1038 "", -- mousedown
1039 "", -- cancelmousedown
1040 "mapper.mouseup_configure", -- mouseup
1041 "Click to configure map",
1042 miniwin.cursor_plus, 0)
1043 end -- if
1044
1045 if type (show_help) == "function" then
1046 local x = config.WINDOW.width - WindowTextWidth (win, FONT_ID, "?") - 6
1047 local y = math.max(2, (title_bottom-font_height)/2)
1048 local text_width = Theme.DrawTextBox(win, FONT_ID,
1049 x-1, -- left
1050 y-2, -- top
1051 "?", false, false)
1052
1053 WindowAddHotspot(win, "<help>",
1054 x-3, y-4, x+text_width+3, y + font_height, -- rectangle
1055 "", -- mouseover
1056 "", -- cancelmouseover
1057 "", -- mousedown
1058 "", -- cancelmousedown
1059 "mapper.show_help", -- mouseup
1060 "Click for help",
1061 miniwin.cursor_help, 0)
1062 end -- if
1063
1064 Theme.AddResizeTag(win, 1, nil, nil, "mapper.resize_mouse_down", "mapper.resize_move_callback", "mapper.resize_release_callback")
1065
1066 -- make sure window visible
1067 WindowShow (win, not window_hidden)
1068
1069 last_drawn = uid -- last room number we drew (for zooming)
1070
1071 local end_time = utils.timer ()
1072
1073 -- timing stuff
1074 if timing then
1075 local count= 0
1076 for k in pairs (drawn) do
1077 count = count + 1
1078 end
1079 print (string.format ("Time to draw %i rooms = %0.3f seconds, search depth = %i", count, end_time - start_time, depth))
1080
1081 total_times_drawn = total_times_drawn + 1
1082 total_time_taken = total_time_taken + end_time - start_time
1083
1084 print (string.format ("Total times map drawn = %i, average time to draw = %0.3f seconds",
1085 total_times_drawn,
1086 total_time_taken / total_times_drawn))
1087 end -- if
1088
1089 -- let them move it around
1090 movewindow.add_drag_handler (win, 0, 0, 0, title_bottom)
1091
1092 CallPlugin("abc1a0944ae4af7586ce88dc", "BufferedRepaint")
1093end -- draw
1094
1095local credits = {
1096 "MUSHclient mapper",
1097 string.format ("Version %0.1f", VERSION),
1098 "Made for Alter Aeon by Demon",
1099 "Based on work by Nick Gammon and Fiendish",
1100 "World: "..WorldName (),
1101 GetInfo (3),
1102}
1103
1104-- call once to initialize the mapper
1105function init (t)
1106
1107 -- make copy of colours, sizes etc.
1108
1109 config = t.config
1110 assert (type (config) == "table", "No 'config' table supplied to mapper.")
1111
1112 supplied_get_room = t.get_room
1113 assert (type (supplied_get_room) == "function", "No 'get_room' function supplied to mapper.")
1114
1115 show_help = t.show_help -- "help" function
1116 room_click = t.room_click -- RH mouse-click function
1117 room_mouseover = t.room_mouseover -- mouse-over function
1118 room_cancelmouseover = t.room_cancelmouseover -- cancel mouse-over function
1119 timing = t.timing -- true for timing info
1120 show_completed = t.show_completed -- true to show "Speedwalk completed." message
1121 show_other_areas = t.show_other_areas -- true to show other areas
1122 show_up_down = t.show_up_down -- true to show up or down
1123 show_area_exits = t.show_area_exits -- true to show area exits
1124 speedwalk_prefix = t.speedwalk_prefix -- how to speedwalk (prefix)
1125
1126 -- force some config defaults if not supplied
1127 for k, v in pairs (default_config) do
1128 config[k] = config[k] or v
1129 end -- for
1130
1131 win = GetPluginID () .. "_mapper"
1132 config_win = GetPluginID () .. "_z_config_win"
1133
1134 WindowCreate (win, 0, 0, 0, 0, 0, 0, 0)
1135 WindowCreate(config_win, 0, 0, 0, 0, 0, 0, 0)
1136
1137 -- add the fonts
1138 WindowFont (win, FONT_ID, config.FONT.name, config.FONT.size)
1139 WindowFont (win, FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
1140 WindowFont (config_win, CONFIG_FONT_ID, config.FONT.name, config.FONT.size)
1141 WindowFont (config_win, CONFIG_FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
1142
1143 -- see how high it is
1144 font_height = WindowFontInfo (win, FONT_ID, 1) -- height
1145
1146 -- find where window was last time
1147 windowinfo = movewindow.install (win, miniwin.pos_bottom_right, miniwin.create_absolute_location , true, {config_win}, {mouseup=MouseUp, mousedown=LeftClickOnly, dragmove=LeftClickOnly, dragrelease=LeftClickOnly}, {x=default_x, y=default_y})
1148
1149 -- calculate box sizes, arrows, connecting lines etc.
1150 build_room_info ()
1151
1152 WindowCreate (win,
1153 windowinfo.window_left,
1154 windowinfo.window_top,
1155 config.WINDOW.width,
1156 config.WINDOW.height,
1157 windowinfo.window_mode, -- top right
1158 windowinfo.window_flags,
1159 Theme.PRIMARY_BODY)
1160
1161 -- let them move it around
1162 movewindow.add_drag_handler (win, 0, 0, 0, 0)
1163
1164 local top = (config.WINDOW.height - #credits * font_height) /2
1165
1166 for _, v in ipairs (credits) do
1167 local width = WindowTextWidth (win, FONT_ID, v)
1168 local left = (config.WINDOW.width - width) / 2
1169 WindowText (win, FONT_ID, v, left, top, 0, 0, Theme.BODY_TEXT)
1170 top = top + font_height
1171 end -- for
1172
1173 Theme.DrawBorder(win)
1174 Theme.AddResizeTag(win, 1, nil, nil, "mapper.resize_mouse_down", "mapper.resize_move_callback", "mapper.resize_release_callback")
1175
1176 WindowShow (win, not window_hidden)
1177 WindowShow (config_win, false)
1178
1179end -- init
1180
1181function MouseUp(flags, hotspot_id, win)
1182 if bit.band (flags, miniwin.hotspot_got_rh_mouse) ~= 0 then
1183 right_click_menu()
1184 end
1185 return true
1186end
1187
1188function LeftClickOnly(flags, hotspot_id, win)
1189 if bit.band (flags, miniwin.hotspot_got_rh_mouse) ~= 0 then
1190 return true
1191 end
1192 return false
1193end
1194
1195function right_click_menu()
1196 menustring = "Bring To Front|Send To Back"
1197
1198 rc, a, b, c = CallPlugin("60840c9013c7cc57777ae0ac", "getCurrentState")
1199 if rc == 0 and a == true then
1200 if b == 1 then
1201 menustring = menustring.."|-|Show Continent Bigmap"
1202 elseif c == 1 then
1203 menustring = menustring.."|-|Merge Continent Bigmap Into GMCP Mapper"
1204 end
1205 end
1206
1207 result = WindowMenu (win,
1208 WindowInfo (win, 14), -- x position
1209 WindowInfo (win, 15), -- y position
1210 menustring) -- content
1211 if result == "Bring To Front" then
1212 CallPlugin("462b665ecb569efbf261422f","boostMe", win)
1213 elseif result == "Send To Back" then
1214 CallPlugin("462b665ecb569efbf261422f","dropMe", win)
1215 elseif result == "Show Continent Bigmap" then
1216 Execute("bigmap on")
1217 elseif result == "Merge Continent Bigmap Into GMCP Mapper" then
1218 Execute("bigmap merge")
1219 end
1220end
1221
1222function zoom_in ()
1223 if last_drawn and ROOM_SIZE < 40 then
1224 ROOM_SIZE = ROOM_SIZE + 2
1225 DISTANCE_TO_NEXT_ROOM = DISTANCE_TO_NEXT_ROOM + 2
1226 build_room_info ()
1227 draw (last_drawn)
1228 SaveState()
1229 end -- if
1230end -- zoom_in
1231
1232
1233function zoom_out ()
1234 if last_drawn and ROOM_SIZE > 4 then
1235 ROOM_SIZE = ROOM_SIZE - 2
1236 DISTANCE_TO_NEXT_ROOM = DISTANCE_TO_NEXT_ROOM - 2
1237 build_room_info ()
1238 draw (last_drawn)
1239 SaveState()
1240 end -- if
1241end -- zoom_out
1242
1243function mapprint (...)
1244 local old_note_colour = GetNoteColourFore ()
1245 SetNoteColourFore(MAPPER_NOTE_COLOUR.colour)
1246 print (...)
1247 SetNoteColourFore (old_note_colour)
1248end -- mapprint
1249
1250function maperror (...)
1251 local old_note_colour = GetNoteColourFore ()
1252 SetNoteColourFore(ColourNameToRGB "red")
1253 print (...)
1254 SetNoteColourFore (old_note_colour)
1255end -- maperror
1256
1257function show()
1258 WindowShow(win, true)
1259 hidden = false
1260end -- show
1261
1262function hide()
1263 WindowShow(win, false)
1264 hidden = true
1265end -- hide
1266
1267function save_state ()
1268 SetVariable("ROOM_SIZE", ROOM_SIZE)
1269 SetVariable("DISTANCE_TO_NEXT_ROOM", DISTANCE_TO_NEXT_ROOM)
1270 if WindowInfo(win,1) and WindowInfo(win,5) then
1271 movewindow.save_state (win)
1272 config.WINDOW.width = WindowInfo(win, 3)
1273 config.WINDOW.height = WindowInfo(win, 4)
1274 end
1275end -- save_state
1276
1277function hyperlinkGoto(uid)
1278 mapper.goto(uid)
1279 for i,v in ipairs(last_result_list) do
1280 if uid == v then
1281 next_result_index = i
1282 break
1283 end
1284 end
1285end
1286
1287require "serialize"
1288
1289
1290
1291function gotoNextResult(which)
1292 if tonumber(which) == nil then
1293 if next_result_index ~= nil then
1294 next_result_index = next_result_index+1
1295 if next_result_index <= #last_result_list then
1296 mapper.goto(last_result_list[next_result_index])
1297 return
1298 else
1299 next_result_index = nil
1300 end
1301 end
1302 ColourNote(RGBColourToName(MAPPER_NOTE_COLOUR.colour),"","NEXT ERROR: No more NEXT results left.")
1303 else
1304 next_result_index = tonumber(which)
1305 if (next_result_index > 0) and (next_result_index <= #last_result_list) then
1306 mapper.goto(last_result_list[next_result_index])
1307 return
1308 else
1309 ColourNote(RGBColourToName(MAPPER_NOTE_COLOUR.colour),"","NEXT ERROR: There is no NEXT result #"..next_result_index..".")
1310 next_result_index = nil
1311 end
1312 end
1313end
1314
1315function goto(uid)
1316 find (nil,
1317 {{uid=uid, reason=true}},
1318 0,
1319 false, -- show vnum?
1320 1, -- how many to expect
1321 true -- just walk there
1322 )
1323end
1324
1325-- generic room finder
1326-- dests is a list of room/reason pairs where reason is either true (meaning generic) or a string to find
1327-- show_uid is true if you want the room uid to be displayed
1328-- expected_count is the number we expect to find (eg. the number found on a database)
1329-- if 'walk' is true, we walk to the first match rather than displaying hyperlinks
1330-- if fcb is a function, it is called back after displaying each line
1331-- quick_list determines whether we pathfind every destination in advance to be able to sort by distance
1332function find (f, show_uid, expected_count, walk, fcb)
1333
1334 if not check_we_can_find () then
1335 return
1336 end -- if
1337
1338 if fcb then
1339 assert (type (fcb) == "function")
1340 end -- if
1341
1342 local start_time = utils.timer ()
1343 local paths, count, depth = find_paths (current_room, f)
1344 local end_time = utils.timer ()
1345
1346 local t = {}
1347 local found_count = 0
1348 for k in pairs (paths) do
1349 table.insert (t, k)
1350 found_count = found_count + 1
1351 end -- for
1352
1353 -- timing stuff
1354 if timing then
1355 print (string.format ("Time to search %i rooms = %0.3f seconds, search depth = %i",
1356 count, end_time - start_time, depth))
1357 end -- if
1358
1359 if found_count == 0 then
1360 mapprint ("No matches.")
1361 return
1362 end -- if
1363
1364 if found_count == 1 and walk then
1365 uid, item = next (paths, nil)
1366 mapprint ("Walking to:", rooms [uid].name)
1367 start_speedwalk (item.path)
1368 return
1369 end -- if walking wanted
1370
1371 -- sort so closest ones are first
1372 table.sort (t, function (a, b) return #paths [a].path < #paths [b].path end )
1373
1374 hyperlink_paths = {}
1375
1376 for _, uid in ipairs (t) do
1377 local room = rooms [uid] -- ought to exist or wouldn't be in table
1378
1379 assert (room, "Room " .. uid .. " is not in rooms table.")
1380
1381 if current_room == uid then
1382 mapprint (room.name, "is the room you are in")
1383 else
1384 local distance = #paths [uid].path .. " room"
1385 if #paths [uid].path > 1 then
1386 distance = distance .. "s"
1387 end -- if
1388 distance = distance .. " away"
1389
1390 local room_name = room.name
1391 if show_uid then
1392 room_name = room_name .. " (" .. uid .. ")"
1393 end -- if
1394
1395 -- in case the same UID shows up later, it is only valid from the same room
1396 local hash = utils.tohex (utils.md5 (tostring (current_room) .. "<-->" .. tostring (uid)))
1397
1398 Hyperlink ("!!" .. GetPluginID () .. ":mapper.do_hyperlink(" .. hash .. ")",
1399 room_name, "Click to speedwalk there (" .. distance .. ")", "", "", false)
1400 local info = ""
1401 if type (paths [uid].reason) == "string" and paths [uid].reason ~= "" then
1402 info = " [" .. paths [uid].reason .. "]"
1403 end -- if
1404 mapprint (" - " .. distance .. info) -- new line
1405
1406 -- callback to display extra stuff (like find context, room description)
1407 if fcb then
1408 fcb (uid)
1409 end -- if callback
1410 hyperlink_paths [hash] = paths [uid].path
1411 end -- if
1412 end -- for each room
1413
1414 if expected_count and found_count < expected_count then
1415 local diff = expected_count - found_count
1416 local were, matches = "were", "matches"
1417 if diff == 1 then
1418 were, matches = "was", "match"
1419 end -- if
1420 mapprint ("There", were, diff, matches,
1421 "which I could not find a path to within",
1422 config.SCAN.depth, "rooms.")
1423 end -- if
1424
1425end -- map_find_things
1426
1427-- build a speedwalk from a path into a string
1428
1429function build_speedwalk (path)
1430
1431 -- build speedwalk string (collect identical directions)
1432 local tspeed = {}
1433 for _, dir in ipairs (path) do
1434 local n = #tspeed
1435 if n == 0 then
1436 table.insert (tspeed, { dir = dir.dir, count = 1 })
1437 else
1438 if tspeed [n].dir == dir.dir then
1439 tspeed [n].count = tspeed [n].count + 1
1440 else
1441 table.insert (tspeed, { dir = dir.dir, count = 1 })
1442 end -- if different direction
1443 end -- if
1444 end -- for
1445
1446 if #tspeed == 0 then
1447 return
1448 end -- nowhere to go (current room?)
1449
1450 -- now build string like: 2n3e4(sw)
1451 local s = ""
1452
1453 for _, dir in ipairs (tspeed) do
1454 if dir.count > 1 then
1455 s = s .. dir.count
1456 end -- if
1457 if #dir.dir == 1 then
1458 s = s .. dir.dir
1459 else
1460 s = s .. "(" .. dir.dir .. ")"
1461 end -- if
1462 s = s .. " "
1463 end -- if
1464
1465 return s
1466
1467end -- build_speedwalk
1468
1469
1470
1471-- start a speedwalk to a path
1472
1473function start_speedwalk (path)
1474
1475 if not check_connected () then
1476 return
1477 end -- if
1478
1479 if current_speedwalk and #current_speedwalk > 0 then
1480 mapprint ("You are already speedwalking! (Ctrl + LH-click on any room to cancel)")
1481 return
1482 end -- if
1483
1484 current_speedwalk = path
1485
1486 if current_speedwalk then
1487 if #current_speedwalk > 0 then
1488 last_speedwalk_uid = current_speedwalk [#current_speedwalk].uid
1489
1490 -- fast speedwalk: just send # 4s 3e etc.
1491 if type (speedwalk_prefix) == "string" and speedwalk_prefix ~= "" then
1492 local s = speedwalk_prefix .. " " .. build_speedwalk (path)
1493 Execute (s)
1494 current_speedwalk = nil
1495 return
1496 end -- if
1497
1498 local dir = table.remove (current_speedwalk, 1)
1499 local room = get_room (dir.uid)
1500 walk_to_room_name = room.name
1501 SetStatus ("Walking " .. (expand_direction [dir.dir] or dir.dir) ..
1502 " to " .. walk_to_room_name ..
1503 ". Speedwalks to go: " .. #current_speedwalk + 1)
1504 Send (dir.dir)
1505 expected_room = dir.uid
1506 else
1507 cancel_speedwalk ()
1508 end -- if any left
1509 end -- if
1510
1511end -- start_speedwalk
1512
1513-- cancel the current speedwalk
1514
1515function cancel_speedwalk ()
1516 if current_speedwalk and #current_speedwalk > 0 then
1517 mapprint "Speedwalk cancelled."
1518 end -- if
1519 current_speedwalk = nil
1520 expected_room = nil
1521 hyperlink_paths = nil
1522 SetStatus ("Ready")
1523end -- cancel_speedwalk
1524
1525
1526-- ------------------------------------------------------------------
1527-- mouse-up handlers (need to be exposed)
1528-- these are for clicking on the map, or the configuration box
1529-- ------------------------------------------------------------------
1530
1531function mouseup_room (flags, hotspot_id)
1532 local uid = hotspot_id
1533
1534 if bit.band (flags, miniwin.hotspot_got_rh_mouse) ~= 0 then
1535
1536 -- RH click
1537
1538 if type (room_click) == "function" then
1539 room_click (uid, flags)
1540 end -- if
1541
1542 return
1543 end -- if RH click
1544
1545 -- here for LH click
1546
1547 -- Control key down?
1548 if bit.band (flags, miniwin.hotspot_got_control) ~= 0 then
1549 cancel_speedwalk ()
1550 return
1551 end -- if ctrl-LH click
1552
1553 start_speedwalk (speedwalks [uid])
1554
1555end -- mouseup_room
1556function mouseover_room (flags, hotspot_id)
1557 if type (room_mouseover) == "function" then
1558 room_mouseover (hotspot_id, flags) -- moused over
1559 end -- if
1560end -- mouseover_room
1561
1562function cancelmouseover_room (flags, hotspot_id)
1563 if type (room_cancelmouseover) == "function" then
1564 room_cancelmouseover (hotspot_id, flags) -- cancled mouse over
1565 end -- if
1566end -- cancelmouseover_room
1567
1568function mouseup_configure (flags, hotspot_id)
1569 draw_configure_box = true
1570 draw (current_room)
1571end -- mouseup_configure
1572
1573function mouseup_close_configure (flags, hotspot_id)
1574 draw_configure_box = false
1575 SaveState()
1576 draw (current_room)
1577end -- mouseup_player
1578
1579function mouseup_change_colour (flags, hotspot_id)
1580
1581 local which = string.match (hotspot_id, "^$colour:([%a%d_]+)$")
1582 if not which then
1583 return -- strange ...
1584 end -- not found
1585
1586 local newcolour = PickColour (config [which].colour)
1587
1588 if newcolour == -1 then
1589 return
1590 end -- if dismissed
1591
1592 config [which].colour = newcolour
1593
1594 draw (current_room)
1595end -- mouseup_change_colour
1596function mouseup_change_delay (flags, hotspot_id)
1597
1598 local delay = get_number_from_user ("Choose speedwalk delay time (0 to 10 seconds)", "Delay in seconds", config.DELAY.time, 0, 10, true)
1599
1600 if not delay then
1601 return
1602 end -- if dismissed
1603
1604 config.DELAY.time = delay
1605 draw (current_room)
1606end -- mouseup_change_delay
1607
1608function mouseup_change_font (flags, hotspot_id)
1609
1610 local newfont = utils.fontpicker (config.FONT.name, config.FONT.size, ROOM_NAME_TEXT.colour)
1611
1612 if not newfont then
1613 return
1614 end -- if dismissed
1615
1616 config.FONT.name = newfont.name
1617
1618 if newfont.size > 12 then
1619 utils.msgbox ("Maximum allowed font size is 12 points.", "Font too large", "ok", "!", 1)
1620 else
1621 config.FONT.size = newfont.size
1622 end -- if
1623
1624 ROOM_NAME_TEXT.colour = newfont.colour
1625
1626 -- reload new font
1627 WindowFont (win, FONT_ID, config.FONT.name, config.FONT.size)
1628 WindowFont (win, FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
1629 WindowFont (config_win, CONFIG_FONT_ID, config.FONT.name, config.FONT.size)
1630 WindowFont (config_win, CONFIG_FONT_ID_UL, config.FONT.name, config.FONT.size, false, false, true)
1631
1632 -- see how high it is
1633 font_height = WindowFontInfo (win, FONT_ID, 1) -- height
1634
1635 draw (current_room)
1636end -- mouseup_change_font
1637
1638function mouseup_change_depth (flags, hotspot_id)
1639
1640 local depth = get_number_from_user ("Choose scan depth (3 to 300 rooms)", "Depth", config.SCAN.depth, 3, 300)
1641
1642 if not depth then
1643 return
1644 end -- if dismissed
1645
1646 config.SCAN.depth = depth
1647 draw (current_room)
1648end -- mouseup_change_depth
1649
1650function mouseup_change_area_textures (flags, hotspot_id)
1651 if config.USE_TEXTURES.enabled == true then
1652 config.USE_TEXTURES.enabled = false
1653 else
1654 config.USE_TEXTURES.enabled = true
1655 end
1656 draw (current_room)
1657end -- mouseup_change_area_textures
1658
1659function mouseup_change_show_id (flags, hotspot_id)
1660 if config.SHOW_ROOM_ID == true then
1661 config.SHOW_ROOM_ID = false
1662 else
1663 config.SHOW_ROOM_ID = true
1664 end
1665 draw (current_room)
1666end -- mouseup_change_area_textures
1667
1668function mouseup_change_show_area_exits (flags, hotspot_id)
1669 if config.SHOW_AREA_EXITS == true then
1670 config.SHOW_AREA_EXITS = false
1671 else
1672 config.SHOW_AREA_EXITS = true
1673 end
1674 draw (current_room)
1675end -- mouseup_change_area_textures
1676
1677function zoom_map (flags, hotspot_id)
1678 if bit.band (flags, 0x100) ~= 0 then
1679 zoom_out ()
1680 else
1681 zoom_in ()
1682 end -- if
1683end -- zoom_map
1684
1685function resize_mouse_down(flags, hotspot_id)
1686 startx, starty = WindowInfo (win, 17), WindowInfo (win, 18)
1687end
1688
1689function resize_release_callback()
1690 config.WINDOW.width = WindowInfo(win, 3)
1691 config.WINDOW.height = WindowInfo(win, 4)
1692 draw(current_room)
1693end
1694
1695function resize_move_callback()
1696 if GetPluginVariable("c293f9e7f04dde889f65cb90", "lock_down_miniwindows") == "1" then
1697 return
1698 end
1699 local posx, posy = WindowInfo (win, 17), WindowInfo (win, 18)
1700
1701 local width = WindowInfo(win, 3) + posx - startx
1702 startx = posx
1703 if (50 > width) then
1704 width = 50
1705 startx = windowinfo.window_left + width
1706 elseif (windowinfo.window_left + width > GetInfo(281)) then
1707 width = GetInfo(281) - windowinfo.window_left
1708 startx = GetInfo(281)
1709 end
1710
1711 local height = WindowInfo(win, 4) + posy - starty
1712 starty = posy
1713 if (50 > height) then
1714 height = 50
1715 starty = windowinfo.window_top + height
1716 elseif (windowinfo.window_top + height > GetInfo(280)) then
1717 height = GetInfo(280) - windowinfo.window_top
1718 starty = GetInfo(280)
1719 end
1720
1721 WindowResize(win, width, height, BACKGROUND_COLOUR.colour)
1722 Theme.DrawBorder(win)
1723 Theme.AddResizeTag(win, 1, nil, nil, "mapper.resize_mouse_down", "mapper.resize_move_callback", "mapper.resize_release_callback")
1724
1725 WindowShow(win, true)
1726end