Message
| One approach you can use is to utilize Lua's coroutine feature to pause the drawing at each level. This requires only fairly minor changes (in bold):
-- draw our map starting at room: uid
function real_draw (uid)
if not uid then
maperror "Cannot draw map right now, I don't know where you are - try: LOOK"
return
end -- if
if current_room and current_room ~= uid then
changed_room (uid)
end -- if
current_room = uid -- remember where we are
-- timing
local start_time = utils.timer ()
-- start with initial room
rooms = { [uid] = get_room (uid) }
-- lookup current room
local room = rooms [uid]
room = room or { name = "<Unknown room>", area = "<Unknown area>" }
last_visited [uid] = os.time ()
current_area = room.area
-- we are recreating the window so any mouse-over is not valid any more
if WindowInfo (win, 19) and WindowInfo (win, 19) ~= "" then
if type (room_cancelmouseover) == "function" then
room_cancelmouseover (WindowInfo (win, 19), 0) -- cancelled mouse over
end -- if
end -- have a hotspot
WindowDeleteAllHotspots (win)
WindowCreate (win,
windowinfo.window_left,
windowinfo.window_top,
config.WINDOW.width,
config.WINDOW.height,
windowinfo.window_mode, -- top right
windowinfo.window_flags,
config.BACKGROUND_COLOUR.colour)
-- let them move it around
movewindow.add_drag_handler (win, 0, 0, 0, font_height + 4)
-- for zooming
WindowAddHotspot(win,
"zzz_zoom",
0, 0, 0, 0,
"", "", "", "", "",
"", -- hint
miniwin.cursor_arrow, 0)
WindowScrollwheelHandler (win, "zzz_zoom", "mapper.zoom_map")
-- 3D box around whole thing
draw_3d_box (win, 0, 0, config.WINDOW.width, config.WINDOW.height)
-- make sure window visible
WindowShow (win, not hidden)
-- set up for initial room, in middle
drawn, drawn_coords, rooms_to_be_drawn, speedwalks, plan_to_draw, area_exits = {}, {}, {}, {}, {}, {}
depth = 0
-- insert initial room
table.insert (rooms_to_be_drawn, add_another_room (uid, {}, config.WINDOW.width / 2, config.WINDOW.height / 2))
while #rooms_to_be_drawn > 0 and depth < config.SCAN.depth do
local old_generation = rooms_to_be_drawn
rooms_to_be_drawn = {} -- new generation
for i, part in ipairs (old_generation) do
draw_room (part.uid, part.path, part.x, part.y)
end -- for each existing room
depth = depth + 1
Redraw ()
coroutine.yield () -- stop for a moment
end -- while all rooms_to_be_drawn
for area, zone_exit in pairs (area_exits) do
draw_zone_exit (zone_exit)
end -- for
local room_name = room.name
local name_width = WindowTextWidth (win, FONT_ID, room_name, true)
local add_dots = false
-- truncate name if too long
while name_width > (config.WINDOW.width - 10) do
-- get rid of last word
local s = string.match (" " .. room_name .. "...", "(%s%S*)$")
if not s or #s == 0 then break end
room_name = room_name:sub (1, - (#s - 2)) -- except the last 3 dots but add the space
name_width = WindowTextWidth (win, FONT_ID, room_name .. " ...", true)
add_dots = true
end -- while
if add_dots then
room_name = room_name .. " ..."
end -- if
-- room name
draw_text_box (win, FONT_ID,
(config.WINDOW.width - WindowTextWidth (win, FONT_ID, room_name, true)) / 2, -- left
3, -- top
room_name, true, -- what to draw, utf8
config.ROOM_NAME_TEXT.colour, -- text colour
config.ROOM_NAME_FILL.colour, -- fill colour
config.ROOM_NAME_BORDER.colour) -- border colour
-- area name
local areaname = room.area
if areaname then
draw_text_box (win, FONT_ID,
(config.WINDOW.width - WindowTextWidth (win, FONT_ID, areaname, true)) / 2, -- left
config.WINDOW.height - 6 - font_height, -- top
areaname, true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
end -- if area known
-- configure?
if draw_configure_box then
draw_configuration ()
else
local x = 5
local y = config.WINDOW.height - 6 - font_height
local width = draw_text_box (win, FONT_ID,
x, -- left
y, -- top (ie. at bottom)
"*", true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
WindowAddHotspot(win, "<configure>",
x, y, x + width, y + font_height, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"", -- mousedown
"", -- cancelmousedown
"mapper.mouseup_configure", -- mouseup
"Click to configure map",
miniwin.cursor_hand, 0) -- hand cursor
end -- if
if type (show_help) == "function" then
local x = config.WINDOW.width - WindowTextWidth (win, FONT_ID, "?", true) - 5
local y = config.WINDOW.height - 6 - font_height
local width = draw_text_box (win, FONT_ID,
x, -- left
y, -- top (ie. at bottom)
"?", true, -- what to draw, utf8
config.AREA_NAME_TEXT.colour, -- text colour
config.AREA_NAME_FILL.colour, -- fill colour
config.AREA_NAME_BORDER.colour) -- border colour
WindowAddHotspot(win, "<help>",
x, y, x + width, y + font_height, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"", -- mousedown
"", -- cancelmousedown
"mapper.show_help", -- mouseup
"Click for help",
miniwin.cursor_hand, 0) -- hand cursor
end -- if
-- 3D box around whole thing
draw_3d_box (win, 0, 0, config.WINDOW.width, config.WINDOW.height)
-- make sure window visible
WindowShow (win, not hidden)
last_drawn = uid -- last room number we drew (for zooming)
local end_time = utils.timer ()
-- timing stuff
if timing then
local count= 0
for k in pairs (drawn) do
count = count + 1
end
print (string.format ("Time to draw %i rooms = %0.3f seconds, search depth = %i", count, end_time - start_time, depth))
total_times_drawn = total_times_drawn + 1
total_time_taken = total_time_taken + end_time - start_time
print (string.format ("Total times map drawn = %i, average time to draw = %0.3f seconds",
total_times_drawn,
total_time_taken / total_times_drawn))
end -- if
thread = nil -- don't need to call coroutine any more
end -- real_draw
function draw (uid)
thread = coroutine.create (real_draw) -- thread will run function real_draw
assert (coroutine.resume (thread, uid)) -- start thread, passing argument uid
end -- draw
function keep_drawing ()
if thread then
coroutine.resume (thread)
end -- if
end -- keep_drawing
What I have basically done here is:
- Renamed the draw function as real_draw (so I can make it a coroutine)
- Added a coroutine.yield inside the loop which draws all the room levels
- Forced a screen refresh before yielding
- Made a new draw() function which makes a coroutine (using read_draw) and starts it
- Make a function designed to resume the drawing (coroutine.resume)
- When the drawing is done for the whole lot of rooms, set the thread variable to nil which will cause that thread to be garbage-collected eventually
Then in the main mapper plugin you need to call this resuming function periodically, by adding this to the end of the script in the plugin:
function OnPluginTick ()
mapper.keep_drawing ()
end -- OnPluginTick
What this effectively does is break up the drawing into one pass per level, then handing back control to the client (eg. to process further input). The OnPluginTick timer kicks in 25 times a second to keep drawing that map if required, so that it draws fairly quickly, but can be interrupted if you move. This GIF shows the idea (the pausing is fairly subtle):
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|