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