Displaying doors on Mapper, and drawing images for room (for shops, forges, etc)

Posted by Death on Wed 13 Mar 2019 10:46 AM — 50 posts, 181,566 views.

#0
Hey all,

I've scoured the internet for weeks looking for this, but I've not seen it anywhere.

I'm trying to add a RED line (or any color) on the room BORDER, but only on the side the door is on.

I get [Exits: down ] [Doors: north south west ] from my mud, so I can use this information to let the mapper know where the doors are.

The code involving mapper.lua for drawing the walls is kind of hard to understand.

Might I get a tip on how to do something like this?

All help would be appreciated.
Australia Forum Administrator #1
The code in mapper.lua around line 695 shows how to draw a line to indicate an exit up, down, in or out:


  -- show up and down in case we can't get a line in

  if room.exits.u then  -- line at top
    WindowLine (win, left, top, left + ROOM_SIZE, top, config.EXIT_COLOUR_UP_DOWN.colour, miniwin.pen_solid, 1)
  end -- if
  if room.exits.d then  -- line at bottom
    WindowLine (win, left, bottom, left + ROOM_SIZE, bottom, config.EXIT_COLOUR_UP_DOWN.colour, miniwin.pen_solid, 1)
  end -- if
  if room.exits ['in'] then  -- line at right
    WindowLine (win, left + ROOM_SIZE, top, left + ROOM_SIZE, bottom, config.EXIT_COLOUR_IN_OUT.colour, miniwin.pen_solid, 1)
  end -- if
  if room.exits.out then  -- line at left
    WindowLine (win, left, top, left, bottom, config.EXIT_COLOUR_IN_OUT.colour, miniwin.pen_solid , 1)
  end -- if



That basically shows the idea for drawing a line on top of the square that represents the room. You could copy and paste that, changing the "if" tests from whether there is an exit to whether there is a door, and perhaps change the colour and line thickness as desired.
#2
Thanks Nick, I'll be playing with it.

I'm under the impression that its possible to replace tiles (fillcolours) with actual images, as explained in

[link]http://gammon.com.au/mushclient/mw_images.htm[/link]

Something like placing something like this

[link]https://rlv.zcache.co.uk/pentacle_tile-r942424ed13c14d71a0988b61a926d94e_agtk1_8byvr_307.jpg[/link]

over a tile, to show that the room is a waypoint.

I've searched the internet for a long time, and can't find any examples. Is this shown somewhere, or can you help me figure out how it might happen?
Australia Forum Administrator #3
If you want to show a single image, the simpler thing would be to just draw the image with WindowDrawImage. Tiles are more for if you want repetition (eg. bricks).

You already know the coordinates so, to borrow from that page, it would be something like:



-- do this once near the start once the mapper window has been created
WindowLoadImage (win, "waypoint", "/some_folder/waypoint.bmp")


-- in the draw_room function:
if (some_condition)
  WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
#4
Hey Nick,

Just to be clear:

this is to draw that image as one of the squares that usually shows on the mapper, instead of a square filled with terrain color, right?


so something like


      if string.match (room.info, "waypoint") then
 WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill


would be right?
#5
Hey Nick,
[FIXED] ---- by putting this line here instead of at the beginning of the draw_room function (at the end)
    if string.match (room.info, "waypoint") then
     WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
	 end 
end -- draw_room


I've added the bold code in my mapper.lua as follows:

local function get_room (uid)
   local room = supplied_get_room (uid)
   room = room or { unknown = true }

   -- defaults in case they didn't supply them ...
   room.name = room.name or string.format ("Room %s", uid)
   room.name = mw.strip_colours (room.name)  -- no colour codes for now
   room.exits = room.exits or {}
   room.area = room.area or "<No area>"
   room.hovermessage = room.hovermessage or "<Unexplored room>"
   room.bordercolour = room.bordercolour or ROOM_COLOUR.colour
   room.borderpen = room.borderpen or 0 -- solid
   room.borderpenwidth = room.borderpenwidth or 1
   room.fillcolour = room.fillcolour or 0x000000
   room.fillbrush = room.fillbrush or 1 -- no fill
   room.texture = room.texture or nil -- no texture
   WindowLoadImage (win, "waypoint", "worlds\plugins\images\waypoint.bmp")
   room.textimage = nil

   if room.texture == nil or room.texture == "" then room.texture = "test5.png" end
   if textures[room.texture] then
      room.textimage = textures[room.texture] -- assign image
   else
      if textures[room.texture] ~= false then
         local dir = GetInfo(66)
         imgpath = dir .. "worlds\plugins\images\" ..room.texture
         if WindowLoadImage(win, room.texture, imgpath) ~= 0 then
            textures[room.texture] = false  -- just indicates not found
         else
            textures[room.texture] = room.texture -- imagename
            room.textimage = room.texture
         end
      end
   end

   return room

end -- get_room



and in my draw_room
local function draw_room (uid, path, x, y)


 if string.match (room.info, "waypoint") then
     WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
	 end

   local coords = string.format ("%i,%i", math.floor (x), math.floor (y))

   -- need this for the *current* room !!!
   drawn_coords [coords] = uid

   -- print ("drawing", uid, "at", coords)

   if drawn [uid] then
      return
   end -- done this one

   -- don't draw the same room more than once
   drawn [uid] = { coords = coords, path = path }

   local room = rooms [uid]

   -- not cached - get from caller
   if not room then
      room = get_room (uid)
      rooms [uid] = room
   end -- not in cache



I tried, and removed this code from my actual mapper plugin

      if string.match (room.info, "waypoint") then
 WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill

^^^ should I have done load?
All special rooms normally load via fillcolour as follows
      elseif string.match (room.info, "guild") then
         special_room = true
         room.fillcolour = mapper.GUILD_FILL_COLOUR.colour
         room.fillbrush = 0 -- solid


I've set the room.info to waypoint, and I'm getting this.


Run-time error
Plugin: DemonMUSH_Mapper (called from world: Demonclient)
Immediate execution
...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:545: attempt to index global 'room' (a nil value)
stack traceback:
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:545: in function 'draw_room'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:980: in function 'draw'
        [string "Plugin: DemonMUSH_Mapper"]:240: in function 'process_exits'
        [string "Trigger: Exits_Line"]:1: in main chunk


I tried again, but this time I modified it by adding this part again
   if room.info and room.info ~= "" then
      if string.match (room.info, "shop") then
         room.fillcolour = mapper.SHOP_FILL_COLOUR.colour
   room.fillbrush = 8  -- medium pattern
 elseif string.match (room.info, "waypoint") then
     WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
      elseif string.match (room.info, "guild") then
         room.fillcolour = mapper.GUILD_FILL_COLOUR.colour
         room.fillbrush = 0 -- solid


and now I get
Run-time error
Plugin: DemonMUSH_Mapper (called from world: Demonclient)
Immediate execution
[string "Plugin: DemonMUSH_Mapper"]:323: bad argument #6 to 'WindowDrawImage' (number expected, got nil)
stack traceback:
        [C]: in function 'WindowDrawImage'
        [string "Plugin: DemonMUSH_Mapper"]:323: in function 'supplied_get_room'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:250: in function 'get_room'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:914: in function 'draw'
        [string "Plugin: DemonMUSH_Mapper"]:240: in function 'process_exits'
        [string "Trigger: Exits_Line"]:1: in main chunk

Not sure where I went wrong on it.
Amended on Thu 14 Mar 2019 07:50 AM by Death
#6
I've got the image drawing properly, but the borders for the room I'm in are RED, and they can still be seen only on some sides. I need them to show up on all 4 sides evenly, if possible, or not at all.

It can be seen here.

https://www.dropbox.com/s/2ixvprtvfnjcxmw/Screenshot%20%28201%29.png?dl=0


(I know that image looks horrible, it's just a test image)

Any idea why this alignment issue with the border might be happening and how to fix it??

ALSO, when I zoom out, it nukes the mapper window.
FIXED the errors below by adding
   
         if room.info and room.info ~= "" then
	 if string.match (room.info, "waypoint") then
     WindowDrawImage (win, "waypoint", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
	 end
	 end


Run-time error
Plugin: DemonMUSH_Mapper (called from world: Demonclient)
Function/Sub: mapper.zoom_map called by Plugin DemonMUSH_Mapper
Reason: Executing plugin DemonMUSH_Mapper sub mapper.zoom_map
...\Jeffrey Johnston\Desktop\MUSHclient\lua\altermapper.lua:713: bad argument #1 to 'match' (string expected, got nil)
stack traceback:
        [C]: in function 'match'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:713: in function 'draw_room'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:982: in function 'draw'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:1250: in function 'zoom_out'
        ...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:1710: in function <...\Master Vivi\Desktop\MUSHclient\lua\altermapper.lua:1708>


I got it transparent now, but it won't center, and if I zoom out, it doesn't stay in the square.
On top of that, is there a way to make the image transparent and still show terrain colour under it?
Amended on Fri 15 Mar 2019 06:52 AM by Death
#7
To clear up this insane mess of a post...

Is there a way to use miniwin.image_stretch AND miniwin.image_transparent_copy at the same time when drawing an image? I want my image to be transparent, and stretch to my box, so I can still see what terrain is under the image (and so I don't get an ugly white background fill)

Also, the borders don't show any more when you draw an image. I would still like to see the borders.
Amended on Fri 15 Mar 2019 09:25 AM by Death
USA Global Moderator #8
Quote:
Is there a way to use miniwin.image_stretch AND miniwin.image_transparent_copy at the same time when drawing an image?


You can always stretch first to a temporary space and then copy that in with transparency.

Quote:
Also, the borders don't show any more when you draw an image. I would still like to see the borders.

I'm 99% sure without looking at your code that you are just drawing your image and your borders at the wrong coordinates. You have to be very careful about whether your coordinates are inclusive or exclusive when using the window drawing operations. Notice, for instance, that your diagonal lines do not exactly line up with the corners of your room boxes. Programatic pixel art requires exact precision.
Amended on Fri 15 Mar 2019 03:31 PM by Fiendish
#9
Fiendish said:

Quote:
Is there a way to use miniwin.image_stretch AND miniwin.image_transparent_copy at the same time when drawing an image?


You can always stretch first to a temporary space and then copy that in with transparency.
How might I go about doing that?


Quote:
Also, the borders don't show any more when you draw an image. I would still like to see the borders.

I'm 99% sure without looking at your code that you are just drawing your image and your borders at the wrong coordinates. You have to be very careful about whether your coordinates are inclusive or exclusive when using the window drawing operations. Notice, for instance, that your diagonal lines do not exactly line up with the corners of your room boxes. Programatic pixel art requires exact precision.

I've not changed where it draws, but that only happens when you're in the CURRENT_ROOM, because the border gets thicker. I'll try to make current room have a border on the outside to fix it
USA Global Moderator #10
Quote:
Quote:
You can always stretch first to a temporary space and then copy that in with transparency.

How might I go about doing that?

In the past I've done something like...

Use WindowCreate to make an invisible miniwindow that just acts as a temporary manipulation and storage buffer. Load your image into it with WindowLoadImage. Then use WindowResize to resize that miniwindow to be exactly the size you want the image to be. Then draw the image stretched into the miniwindow with WindowDrawImage. Then use WindowImageFromWindow to clone the contents of your invisible miniwindow into your main miniwindow. Then you can draw it semi-transparently using WindowDrawImageAlpha in the main miniwindow.

Quote:
I've not changed where it draws

But it appears that you have now drawn an image overhanging part of it.

Quote:
I'll try to make current room have a border on the outside to fix it

Or you could reduce the size of your waypoint image so that it doesn't overhang your border. Or you could draw the border after your waypoint image instead of before it. Though probably you should do both so that the symbol stays centered.

Also don't load your image every time you look up a room. That's wildly inefficient. You only need to load the image file once ever.
Amended on Sat 16 Mar 2019 03:00 AM by Fiendish
#11
That whole create a separate miniwindow thing sounds complicated, I have no idea how to do this

Quote:


Also don't load your image every time you look up a room. That's wildly inefficient. You only need to load the image file once ever.


I had this in my get_room function

local function get_room (uid)
   local room = supplied_get_room (uid)
   room = room or { unknown = true }
   -- defaults in case they didn't supply them ...
   room.name = room.name or string.format ("Room %s", uid)
   room.name = mw.strip_colours (room.name)  -- no colour codes for now
   room.exits = room.exits or {}
   room.area = room.area or "<No area>"
   room.hovermessage = room.hovermessage or "<Unexplored room>"
   room.bordercolour = room.bordercolour or ROOM_COLOUR.colour
   room.borderpen = room.borderpen or 0 -- solid
   room.borderpenwidth = room.borderpenwidth or 1
   room.fillcolour = room.fillcolour or 0x000000
   room.fillbrush = room.fillbrush or 1 -- no fill
   room.texture = room.texture or nil -- no texture
     WindowLoadImage (win, "waypoint", "worlds\plugins\images\waypoint.bmp")
	      WindowLoadImage (win, "ocean", "worlds\plugins\images\ocean.bmp")
		  	      WindowLoadImage (win, "city", "worlds\plugins\images\city.bmp")
				   	      WindowLoadImage (win, "beach", "worlds\plugins\images\beach.bmp")
						  				   	      WindowLoadImage (win, "lightforest", "worlds\plugins\images\lightforest.bmp")
										  WindowLoadImage (win, "rock", "worlds\plugins\images\rock.bmp")
										       WindowLoadImage (win, "bank", "worlds\plugins\images\bank.bmp")
											       WindowLoadImage (win, "field", "worlds\plugins\images\field.bmp")


   room.textimage = nil

   if room.texture == nil or room.texture == "" then room.texture = "test5.png" end
   if textures[room.texture] then
      room.textimage = textures[room.texture] -- assign image
   else
      if textures[room.texture] ~= false then
         local dir = GetInfo(66)
         imgpath = dir .. "worlds\plugins\images\" ..room.texture
         if WindowLoadImage(win, room.texture, imgpath) ~= 0 then
            textures[room.texture] = false  -- just indicates not found
         else
            textures[room.texture] = room.texture -- imagename
            room.textimage = room.texture
			
         end
      end
   end
   
   return room
   
end -- get_room
and now I've changed it to be in my function draw (uid) right under
WindowCreate (win,
      windowinfo.window_left,
      windowinfo.window_top,
      config.WINDOW.width,
      config.WINDOW.height,
      windowinfo.window_mode,   -- top right
      windowinfo.window_flags,
      Theme.PRIMARY_BODY)

This should be far more efficient, right?
Amended on Sat 16 Mar 2019 03:15 AM by Death
Australia Forum Administrator #12

I’m on holiday right now so it isn’t easy for me to respond, however Fiendish is giving you good advice.

Amended on Sat 16 Mar 2019 12:09 PM by Nick Gammon
USA Global Moderator #13
Quote:
This should be far more efficient, right?

Yes, that's perfect.

Quote:

That whole create a separate miniwindow thing sounds complicated, I have no idea how to do this

It is a bit complicated, but luckily for you I gave you the exact sequence of steps to do, so actually you do now know how to do it just by following the steps I wrote out. Write the code for my steps as they apply to your specific needs (your image file name and what size you want it). You can then paste it here if it doesn't work, and we will help figure out if you applied something incorrectly.

Most of the new code (from WindowCreate to WindowImageFromWindow) would basically be replacing your current call to WindowLoadImage. And then the last call to WindowDrawImageAlpha would replace your current WindowDrawImage call.
Amended on Sun 17 Mar 2019 11:19 PM by Fiendish
#14
Hey all,


I've not been able to do the resizing thing, for now, but I've done quite a lot of other things. Screenshots can be seen here,

https://www.dropbox.com/s/rfhhko1rqxqcny6/Screenshot%20%28264%29.png?dl=0


and fully zoomed out
https://www.dropbox.com/s/pyn33uuzyrlrues/Screenshot%20%28265%29.png?dl=0


a few more to see my work
https://www.dropbox.com/s/mkyq94dwkff47kc/Screenshot%20%28266%29.png?dl=0

https://www.dropbox.com/s/hgp1yoz81eks5n5/Screenshot%20%28268%29.png?dl=0

https://www.dropbox.com/s/fvgtswhaczthzpx/Screenshot%20%28269%29.png?dl=0

fully zoomed out
https://www.dropbox.com/s/o8mj91pqr6d46x5/Screenshot%20%28270%29.png?dl=0

pretty zoomed out
https://www.dropbox.com/s/yfp7ygtk5x063bq/Screenshot%20%28271%29.png?dl=0



I need help figuring out how to make the room borders really fat (like room.borderpenwidth = 3) when I walk into a special room, but staying normal otherwise.

It seems to not work properly when I use WindowCircleOp for current_room.

I'm currently using
					     if uid == current_room and special_room then
                                         room.bordercolour = mapper.OUR_ROOM_COLOUR.colour
                                         room.borderpenwidth = 3		 
			             elseif uid == current_room and not special_room then
                                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,OUR_ROOM_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-2,miniwin.brush_null)


but it seems to not work.
If I put

if uid == current_room then
room.bordercolour = mapper.OUR_ROOM_COLOUR.colour
room.borderpenwidth = 3	

in my actual mapper plugin, it works properly, but then my current_room border when I'm NOT in those special rooms is not how I want it (+2 away from all true borders) its just a tiny flimsy line on the exact border of the rooms.

Any idea how to fix this??
I've spent like 6 hours trying to figure out this simple thing tonight. I suck.

Also, according to the second image do you think this is too much for the client to handle? Sometimes when I zoom out my map all of the way it just instantly crashes the entire MushClient.
Amended on Sun 24 Mar 2019 10:37 AM by Death
USA Global Moderator #15
I only see you calling WindowCircleOp for regular rooms. Where do you call it for special rooms? If you could link to the current version of your file and say which line numbers you're looking at, it will help give better context.

Quote:
Also, according to the second image do you think this is too much for the client to handle?


It shouldn't be.


Quote:
Sometimes when I zoom out my map all of the way it just instantly crashes the entire MushClient.

That's definitely not supposed to happen! Can you make a self-contained demonstration of the crash for debugging?
Amended on Sun 24 Mar 2019 02:52 PM by Fiendish
#16
Hey fiendish, here is what I've got.

Drawing images starts at line 733, and special rooms start at line 777. I've made it pretty to look at, for easy reading.

Here is the lua file.

https://www.dropbox.com/s/w0rg2rn890f9p11/altermapper.lua?dl=0


You can see at the bottom (line 857) where I've tried to differentiate special rooms and non-special rooms.

Currently, I've got the special room tiles drawing OVER the existing room.fillcolour tiles. Might this cause an issue with memory, because its technically drawing double the rooms? I intend to remove the fillcolours asap and ONLY draw the tiles, but it's a whole to-do, so I haven't done it yet. I might still need it to draw those underlying rooms, so I can draw doors on the borders, but I might be able to use WindowCircleOp to draw a border outside of the rooms and show doors that way (though I think that would slow the HELL out of the client, because I tried it one time and it was nightmarish when I zoomed out)
Currently, I just photoshopped the room borders onto my tiles, LOL.

Also to note, my mapper database has 13,000 rooms in it so far, I'm not sure if this would contribute to the slowing down, since I can view most of them at once if I zoom out all of the way. There's HUGE lag if I try to walk somewhere while zoomed out, but there's really no need to walk places while zoomed out (except clicking on a room to walk there) so I can just refrain from that for now.



As for the crashing, I'll try to get a screen recording of it happening. When the MUSHCLIENT crashes, it's instant, it doesn't even give me any kind of failure message.
Australia Forum Administrator #17
Possibly you are overflowing the Lua stack. A sudden crash like that is probably somewhere in Lua.
#18
Nick Gammon said:

Possibly you are overflowing the Lua stack. A sudden crash like that is probably somewhere in Lua.



How can I fix this?

Do I have too much code in my lua file that needs to move to my mapper plugin instead?
Amended on Mon 25 Mar 2019 04:01 AM by Death
USA Global Moderator #19
Quote:
Currently, I've got the special room tiles drawing OVER the existing room.fillcolour tiles. Might this cause an issue with memory, because its technically drawing double the rooms?

Performance issue yes, memory issue no.

Quote:
my mapper database has 13,000 rooms in it so far, I'm not sure if this would contribute to the slowing down, since I can view most of them at once if I zoom out all of the way. There's HUGE lag if I try to walk somewhere while zoomed out

Yep. Rapidly drawing that many rooms will take some significant rework to how and when the rooms get drawn. The mapper code wasn't designed for that.

Quote:
Do I have too much code in my lua file that needs to move to my mapper plugin instead?

No. That's not what overflowing the stack means. Basically Nick is talking about programming an infinite loop of function calls. If a function calls itself, then calling it will result in it being called over and over and over again infinitely, each time adding another layer to the "call stack".

I'll take a look at your code tomorrow and see if anything stands out.
Amended on Mon 25 Mar 2019 04:34 AM by Fiendish
#20
Quote:
Yep. Rapidly drawing that many rooms will take some significant rework to how and when the rooms get drawn. The mapper code wasn't designed for that.

On Nick's help page, He says
Quote:
The intention of allowing sub-images was so that you could make up a map by having lots of small images (eg. houses, trees, rocks). Now instead of having to load hundreds of tiny image files into memory, which would doubtless take a while to open all those files, you open a single, larger, file in which the various sub-images are "tiled". For example, you might allocate a 32 x 32 pixel square for each image. Now by selecting the appropriate tile you can copy the sub-image from the main image.


Maybe this many rooms weren't intended though..


Would putting the images in an sql database help somehow?
I read something about that in the thread below.

I'd love to retain high speed on the client and not slow it down to hell.


Quote:
No. That's not what overflowing the stack means. Basically Nick is talking about programming an infinite loop of function calls. If a function calls itself, then calling it will result in it being called over and over and over again infinitely, each time adding another layer to the "call stack".

I'll take a look at your code tomorrow and see if anything stands out.

Thanks for teaching me about that. I don't see what I would've done that would cause a loop, but I appreciate the help SO greatly!
Would be lovely to know what causes the issue, so I can avoid it in the future.
Amended on Mon 25 Mar 2019 04:44 AM by Death
USA Global Moderator #21
Quote:
Would putting the images in an sql database help somehow?

Very unlikely, but I haven't looked at your code yet. Remember that part where I said "don't load your image every time you look up a room. That's wildly inefficient. You only need to load the image file once ever." and then you made a change to follow my advice? That should already solve any issues with image loading speed. The problem is the amount of time it takes to complete each room drawing operation and hotspot creation multiplied by trying do many thousands of them.

Quote:
I don't see what I would've done that would cause a loop

You may not have. Nick was just speculating about one kind of issue that could cause a crash like you're experiencing.

I'm going to bed now though and will be back around tomorrow to take a look at the code you posted.
Amended on Mon 25 Mar 2019 05:40 AM by Fiendish
USA Global Moderator #22
A passing thought on my way to work...

Quote:
The problem is the amount of time it takes to complete each room drawing operation and hotspot creation

What version of MUSHclient are you using by the way? 5.02 fixed a performance problem when adding thousands of hotspots. (I was doing the same huge map drawing that you're doing now)

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=13688

If you're not using it already, grab the 5.06 pre-release executable from https://github.com/nickgammon/mushclient/releases/download/latest_commit/MUSHclient.exe
Amended on Mon 25 Mar 2019 12:48 PM by Fiendish
#23
Fiendish said:

A passing thought on my way to work...

Quote:
The problem is the amount of time it takes to complete each room drawing operation and hotspot creation

What version of MUSHclient are you using by the way? 5.02 fixed a performance problem when adding thousands of hotspots. (I was doing the same huge map drawing that you're doing now)

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=13688

If you're not using it already, grab the 5.06 pre-release executable from https://github.com/nickgammon/mushclient/releases/download/latest_commit/MUSHclient.exe


Hey Fiendish, I'm using 5.06, thanks. :)
USA Global Moderator #24
First, not completely related to the problem you're having and yet at the same time a reasonable way to prevent similar problems from happening in the first place, a few pieces of advice on making it easier to follow what's happening in your code...

1. Don't use string.match in places where you can just use a double equal sign. == is far more idiomatic and probably more efficient. It will also save you the pain of accidentally stumbling face first into "patterns" ( https://www.lua.org/pil/20.2.html ).

2. Fix your whitespace and indentation levels. Consistent indentation is one of the most important guides your brain has for making sense of complex logic.

3. If you find yourself writing almost identical lines of code over and over and over again, there's almost certainly a better way that is more compact and less overwhelming to look at at. Lookup tables can be a great help with this. As an example, I would replace your DRAW MAP IMAGES section that looks like this...

if string.match (room.fillcolour, "9109504") then
    WindowDrawImage (win, "ocean", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
elseif string.match (room.fillcolour, "9465920") then
    WindowDrawImage (win, "town", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
elseif string.match (room.fillcolour, "61680") then
    WindowDrawImage (win, "stream", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill

-- et cetera

with something like this instead...

color_to_image = {
    ["9109504"] = "ocean",
    ["9465920"] = "town",
    ["61680"] = "stream",
    -- et cetera
}

WindowDrawImage(win, color_to_image[room.fillcolour], left, top, right, bottom, miniwin.image_stretch)

It accomplishes the same thing with less room for accidental copy/paste errors.


Now on to your border drawing problem...

Your lines 858 and 859 accomplish nothing. To discover why, search in your file for every instance of "room.bordercolour". It appears in 3 places. 2 of those places set it, and one of those places use it. Look at where it gets used relative to where it gets set. Is it before or after? Is there any way for the code to get from where you set it to where you use it without violating logic or going backwards in time?

IMO, any modifications to room color, brush, and pen should be happening before line 694 because that small section with the comments "-- room fill" and "-- room border" are where they get used.
Amended on Tue 26 Mar 2019 02:38 AM by Fiendish
Australia Forum Administrator #25
If you know you can definitely reproduce the crash, for example by zooming right out, then a helpful thing to do is to add some debugging prints.

Even something simple like printing which room number you are currently drawing could indicate where the problem lies. You may find that just before the crash you draw the same room number hundreds of times for example.

You may find that your output window fills up with a lot of spam messages but that doesn't really matter if this helps to solve the problem. You may need to increase the number of lines shown in the window, from the default to something like 500000.
#26
Fiendish said:

First, not completely related to the problem you're having and yet at the same time a reasonable way to prevent similar problems from happening in the first place, a few pieces of advice on making it easier to follow what's happening in your code...

1. Don't use string.match in places where you can just use a double equal sign. == is far more idiomatic and probably more efficient. It will also save you the pain of accidentally stumbling face first into "patterns" ( https://www.lua.org/pil/20.2.html ).

2. Fix your whitespace and indentation levels. Consistent indentation is one of the most important guides your brain has for making sense of complex logic.

3. If you find yourself writing almost identical lines of code over and over and over again, there's almost certainly a better way that is more compact and less overwhelming to look at at. Lookup tables can be a great help with this. As an example, I would replace your DRAW MAP IMAGES section that looks like this...

if string.match (room.fillcolour, "9109504") then
    WindowDrawImage (win, "ocean", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
elseif string.match (room.fillcolour, "9465920") then
    WindowDrawImage (win, "town", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
elseif string.match (room.fillcolour, "61680") then
    WindowDrawImage (win, "stream", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill

-- et cetera

with something like this instead...

color_to_image = {
    ["9109504"] = "ocean",
    ["9465920"] = "town",
    ["61680"] = "stream",
    -- et cetera
}

WindowDrawImage(win, color_to_image[room.fillcolour], left, top, right, bottom, miniwin.image_stretch)


I can't really do it like that, since each example is also doing its own unique WindowCircleOp, as shown
				         elseif string.match (room.info, "magetrainer") then
                                         special_room = true
		 	                             WindowDrawImage (win, "magetrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
				                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,MAGE_TRAINER_FILL_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)


but other than that, that code DOES look easier to read.

How might I go about including all of my windowcircleops in that same format?

On a side note, my mushclient just nuked itself (crashed) when I reinstalled my mapper.
I'll try to find out why.
Amended on Tue 26 Mar 2019 08:12 AM by Death
USA Global Moderator #27
Quote:
I can't really do it like that

You very clearly can.

Quote:
since each example is also doing its own unique WindowCircleOp

Your WindowCircleOps aren't "unique". They vary only by color. Make a table that links each room.info to a color _exactly_ like I did linking fillcolour to image name in my example.

Quote:
On a side note, my mushclient just nuked itself (crashed) when I reinstalled my mapper.
I'll try to find out why.

You gave us your lua file but not your plugin file. Perhaps if you linked to that as well (and also a link to the server for your game) we could help test the crash.
Amended on Tue 26 Mar 2019 03:17 PM by Fiendish
#28
Here we go:

https://pastebin.com/3cJpGc2r


This is my mapper plugin.

I was just thinking: I didn't make a way to only tag a room as one tile, and also if the room gets tagged more than once, it puts the same thing in (If I tag waypoint twice, it puts waypointwaypoint in my sql)

Might this cause problems? I can imagine it trying to draw a cleric and warrior tile over and over, but maybe it would just drop them on top of each other and I'd never know. I'd imagine that's how it would draw, so I doubt it.

For the WindowCircleOp thing, would it be something like
special_room_border = {
    ["magetrainer"] = "MAGE_COLOUR_FILL",
    ["clerictrainer"] = "CLERIC_COLOUR_FILL",
    ["necromancertrainer"] = "NECRO_COLOUR_FILL",
    -- et cetera
}
WindowCircleOp (win, special_room_border[room.bordercolour], left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,[room.bordercolour],
                                         room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)

or did I mess up the WindowCircleOp (second argument)? should I put it back to
(win, miniwin.circle_rectangle??




Also, I fixed the border stuff by putting
   -- SPECIAL ROOM COLOUR FILLS
special_room = false
                         if room.info and room.info ~= "" then
                         if string.match (room.info, "waypoint") then
                                         special_room = true
                         elseif string.match (room.info, "bank") then
                                         special_room = true
                        ETC ETC ETC ETC 



in my mapper plugin, and

				         elseif string.match (room.info, "magetrainer") then
                                         special_room = true
		 	                             WindowDrawImage (win, "magetrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
				                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,MAGE_TRAINER_FILL_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)
				         elseif string.match (room.info, "necromancertrainer") then
                                         special_room = true
		 	                             WindowDrawImage (win, "necromancertrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
				                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,NECRO_TRAINER_FILL_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)			
				         elseif string.match (room.info, "rangertrainer") then
                                         special_room = true
		 	                             WindowDrawImage (win, "rangertrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
				                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,RANGER_TRAINER_FILL_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)												 
                 ETC ETC ETC
										end
                                        end -- if
			             if uid == current_room and not special_room then
                                         WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
                                         right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,OUR_ROOM_COLOUR.colour,
                                         room.borderpen, room.borderpenwidth,-2,miniwin.brush_null)										
end
end
  
in my mapper.lua


I'll try to convert the system to the new way fiendish showed me today, if I can get the WindowCircleOp part right. Should be much more pretty looking.

The link for my mud is dentinmud.org, port 3000.
You won't be able to see my mapper in its full glory unless you have all of the files relevant to it. You might only need my images folder, or you can spoof one.
If you connect, type "set kxwt on", as you don't have my plugin that automatically does that: this will feed you room and walk info every time you look or walk.
Amended on Tue 26 Mar 2019 11:22 PM by Death
USA Global Moderator #29
Quote:
For the WindowCircleOp thing, would it be something like

That's now invalid code. Look carefully at what I did again.

I replaced

if thing == A1 then
   my_function(some args, B1, some more args)
elseif thing == A2 then
   my_function(some args, B2, some more args)
...

with

from_A_to_B = {
  [A1] = B1,
  [A2] = B2,
   ...
}

my_function(some args, from_A_to_B[thing], some more args)


Quote:
You won't be able to see my mapper in its full glory unless

Please provide an entire working directory then, since the whole point was to be able to help you debug a crash, and that's easier to do if we can actually run what you're running.
Amended on Wed 27 Mar 2019 07:12 PM by Fiendish
#30
Hey all,
here is the client folder. Just download and place on your desktop and you're all golden. You'll probably get a few errors about the location of demonclient.mcl, because as far as I can tell theres no way to set the load location vaguely, only specifically (user specific)

https://www.dropbox.com/sh/24lv7wl9i52vvmj/AADv7Z5a0D9RolpuTYUflvh4a?dl=0


You can just load demonclient.mcl, and you should be set. Look will get you started.
Please contact in game named "Demon" about getting you off the newbie islands, since I didn't map it and have no plans to map it. (There are too many instanced areas with variable vnums)

Alternatively, you can ask on the newbie or gossip channel for someone to help you get to Ralnoth, which is on the mainland.

Let me know if you have any questions, and thanks for helping me find this crash bug!
Australia Forum Administrator #31
This sort of thing:


elseif string.match (room.info, "magetrainer") then
  special_room = true
  WindowDrawImage (win, "magetrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
  WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
    right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,MAGE_TRAINER_FILL_COLOUR.colour,
    room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)
elseif string.match (room.info, "necromancertrainer") then
  special_room = true
  WindowDrawImage (win, "necromancertrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
  WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
    right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,NECRO_TRAINER_FILL_COLOUR.colour,
    room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)			
elseif string.match (room.info, "rangertrainer") then
  special_room = true
  WindowDrawImage (win, "rangertrainer", left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
  WindowCircleOp (win, miniwin.circle_rectangle, left-2-room.borderpenwidth, top-2-room.borderpenwidth,
    right+2+room.borderpenwidth, bottom+2+room.borderpenwidth,RANGER_TRAINER_FILL_COLOUR.colour,
    room.borderpen, room.borderpenwidth,-1,miniwin.brush_null)


Can be easily simplified to something like this:


special_room_info = {
  magetrainer         =  MAGE_TRAINER_FILL_COLOUR.colour,
  necromancertrainer  =  NECRO_TRAINER_FILL_COLOUR.colour,
  rangertrainer       =  RANGER_TRAINER_FILL_COLOUR.colour,
  -- and so on
  }

if special_room_info [room.info] then -- if a special room
  special_room = true
  WindowDrawImage (win, room.info, left, top, right, bottom, miniwin.image_stretch)  -- stretch to fill
  WindowCircleOp (win, miniwin.circle_rectangle, left - 2 - room.borderpenwidth, top - 2 - room.borderpenwidth,
                  right + 2 + room.borderpenwidth, bottom + 2 + room.borderpenwidth,
                  special_room_info [room.info],  -- colour from table
                  room.borderpen, room.borderpenwidth, -1, miniwin.brush_null)
end -- if special room



Your dozens of lines of code condense down into one simple table and a single couple of functions to do the drawing.

Try to make consistent indentation. Having indents for no reason is just confusing.
USA Global Moderator #32
Ooooooh, it's based on the Aardwolf package, I see. One bit of advice if you don't mind my saying so, almost black on black is a very difficult color scheme to use. The contrast is so low I can barely make out any of the details. Also, if you become wildly successful and make a million dollars from it, please think of the little guy (lol).

The first message I see when logging in is

Quote:

Run-time error
Plugin: DemonMUSH_Mapper (called from world: Demonclient)
Immediate execution
Z:\Users\fiendish\Downloads\DemonMUSH\lua\altermapper.lua:1236: attempt to index local 'areaname' (a number value)
stack traceback:
Z:\Users\ fiendish\Downloads\DemonMUSH\lua\altermapper.lua:1236: in function 'draw'
[string "Plugin: DemonMUSH_Mapper"]:311: in function 'process_exits'
[string "Trigger: Exits_Line"]:1: in main chunk



I also see a lot of messages like
Quote:
Speedwalk failed! Expected to be in '-666' but ended up in '50048'.

When clicking on rooms to move between them.

I don't see you online, so I'll try again later.
Amended on Thu 28 Mar 2019 03:46 PM by Fiendish
#33
Hey fiendish,


(client isn't for sale, lol, and I suck! Nobody is getting rich off of this xD)

Yeah, I'm still looking for that areaname error, it's causing issues for sure.

I haven't got a speedwalk failure in a very long time, so I'm not sure what the issue there is. -666 is an unexplored room, so I'm not sure how it would expect to be elsewhere while walking to an unexplored room.

What exactly were you doing? Maybe theres some other weird issue going on there. Did you place the folder on the desktop, or run as admin? Sometimes that causes weird issues if you don't do that.

I'm also not sure what you mean by black on black. If there are any rooms that are black, there's an issue with that room. (Sometimes room 0 on unholy has this issue because it's room 0) I'm still trying to find where thats happening, because I fixed it by making unknown rooms -666 (vnum -1 is also possible) and then the problem came back again.

I'd ignore the speedwalk via clicking thing, because I don't think that's an issue unless your "Unholy Temple of Dentin" turned black. Some code is causing vnum 0 to become nothing when I start the client, but not when I reinstall the mapper.

I'll try to catch you online. I'll be on for the next 8 hours or so probably.

If you see a black room, hover over it to get the vnum and do "mapper delete room <vnum>
In this case, mapper delete room 0 to delete unholy temple.
Amended on Thu 28 Mar 2019 11:22 PM by Death
USA Global Moderator #34
Quote:

I'm also not sure what you mean by black on black.

By almost black on black I mean this:
https://imgur.com/a/f7g3EOu
and this
https://imgur.com/a/YCMI9K5
#35
Fiendish said:

Quote:

I'm also not sure what you mean by black on black.

By almost black on black I mean this:
https://imgur.com/a/f7g3EOu
and this
https://imgur.com/a/YCMI9K5


Oh thanks for that.
I have a few different themes, I just prefer the darker one. My favorite is called "purple pony".

https://www.dropbox.com/s/6wcgp7bcobioiem/Screenshot%20%28294%29.png?dl=0
Amended on Fri 29 Mar 2019 01:54 AM by Death
Australia Forum Administrator #36
Master Vivi said:

-666 is an unexplored room ...


The Number of the Beast - very appropriate!
#37
Hey Nick,

I'm having huge issues with lag on my mapper because its drawing too many rooms when my map is zoomed out.


Fiendish explained it in a way that the mapper loads rooms in an outward spiral, and said there's a way to pause the loading process, but that he didn't remember how.


I don't want it to redraw the entire map every time I move, maybe only the closer rooms.

I'm sure this would help TONS with lag.

Any idea? Or maybe only redraw rooms in XX range.

We also couldn't find a repeatable crash bug, but I did get my client to crash 4 times by zooming all the way into the map, or all of the way out of the map, and reinstalling the mapper. I couldn't get it to happen repeatedly though.
Amended on Mon 01 Apr 2019 12:45 AM by Death
Australia Forum Administrator #38
You can configure the redraw range, see this video:

Another Aardwolf mapper example

Near the start you see me changing the depth (number of rooms from the start) to draw.




Also see this Aardwolf mapper example which shows something like 475 rooms being drawn at once without any slow down.
#39
Nick Gammon said:

You can configure the redraw range, see this video:

[url=https://vimeo.com/80318832]Another Aardwolf mapper example[/url]

Near the start you see me changing the depth (number of rooms from the start) to draw.

-----

Also see this [url=https://vimeo.com/80305761]Aardwolf mapper example[/url] which shows something like 475 rooms being drawn at once without any slow down.



Hey friend, don't forget I'm loading images, not just the rooms. It was completely fine without the images, which is why I'm looking for the fix. And this is drawing somewhere between 8000-13000 rooms at once. AARD is too small and separated for that to happen. Max capacity I'm looking to draw will be somewhere in the range of double the entire amount of rooms in aardwolf, but we're starting small here with around 13-15k rooms in the current map.

I also want to be able to view all rooms on the map at once if I zoom all of the way out.
Amended on Mon 01 Apr 2019 10:08 AM by Death
USA Global Moderator #40
I believe this requires batching the rooms to be drawn and deferring display of each next batch of rooms until a check can be performed to make sure that the player has not moved before continuing to draw.

Nick, I couldn't remember if you'd shown someone an example of doing this at some point. I know that the Aardwolf mapper doesn't currently do it because the areas are small enough that it never needed to (I just reduce the depth while running like shown in your video).

A while ago I was experimenting with adding something like this to the Aardwolf mapper[0] as part of a bigger set of changes (including drawing the entire world instead of just the current area) and just didn't finish implementing everything I wanted to and then I forgot about it for a while and would probably have to start over to be able to get something merged into my current code. But basically I did the above batched by search depth and also didn't draw any hotspots until the very end (on the premise that rooms in rapid flux don't need to be clickable).

[0] - See the "draw_next_batch_of_rooms" function in https://github.com/fiendish/aardwolfclientpackage/blob/905df5b79046be9351d8f2a300d7ba1c8a7b1d52/MUSHclient/lua/aardmapper.lua#L1024
Amended on Mon 01 Apr 2019 03:42 PM by Fiendish
Australia Forum Administrator #41
Master Vivi said:

I don't want it to redraw the entire map every time I move, maybe only the closer rooms.

...

And this is drawing somewhere between 8000-13000 rooms at once.


That's why I suggested dropping the redraw distance. Drawing 13000 images may well slow things down. I'm not sure why you are resistant to implementing this.

The mapper was not really intended to show thousands of rooms (hence the depth parameter) however I am pleased to hear that, without adding images, it can cope with 13000 rooms.

Even if drawing one room takes 100 µs (which is not very long), that will still be 1.3 seconds to draw 13000 rooms.

You must have a big screen if you can perceive the graphics of 13000 rooms shown all at once (as images). Perhaps change it to not draw images if you zoom right out.
#42
Hey Nick,

Zooming out really helps you understand the layout of the map, and actually when its zoomed out that's actually when the map is most enjoyable: it looks like an actual world. There are forests, rivers, etc etc. It looks GREAT!! Lowering the depth would just make it so that rooms will not be shown at all, lol.

There are no issues while zoomed in normally, and by default its quite zoomed in.

Again, when you want to see the entire world map, it's okay. The fix is just don't travel when you're zoomed all of the way out, which is fine, but I was looking for some optimization, not a band-aid fix.

I appreciate your help.

What I'm saying is that maybe it doesn't have to redraw every single room every single time when zoomed out. Maybe it can only redraw the nearest rooms, or something like that? That's what fiendish is talking about as well.

I'd appreciate any insight into this idea, otherwise I'll just deal with it.

I'll continue working on finding the crash bug as well.

Cheers.
Australia Forum Administrator #43
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):

Amended on Wed 03 Apr 2019 03:28 AM by Nick Gammon
Australia Forum Administrator #44
If you move the yield into the inner loop it becomes much more obvious, as it yields for each room:


 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)
      Redraw ()
      coroutine.yield ()  -- stop for a moment
    end -- for each existing room
    depth = depth + 1
  end -- while all rooms_to_be_drawn

#45
Hey Nick,


All I've gotta say is WOW, looks like a lot of work you helped me with.

Thank you!


I'm not to keen on the blinking aspect of it, but meh. My friend said there might be a good way to do this.
Let me know if this makes sense to you.


Quote:
Dentin techs: ok anyway regarding your mapper problem, you hold all the rooms in an array or linked list, and give each room structure a 32 or 64 bit integer called "last_seen_pass_index". When you want to do the mapping, you just index through the array, and when you find a room that doesn't have the current pass index, you set the pass index, map it, then map its sub rooms
Dentin techs: if it does have the current pass index, you just return and do nothing
Dentin techs: outside of the array of rooms, you have an index that's just a counter. Starts at zero. When you want to run the map, you increment the external counter, then run the map algorithm. When you want to run the map again, increment the external counter, and run the mapper again
Dentin techs: its self synchronizing and guarantees a total complexity order of N*M, where N is the room count and M is the average exit count, but its actually better than that because of both disconnected graphs and fast return from already mapped objects
Dentin techs: anyway, super simple, super fast, roughly linear order, no reason for anything to slow down unless you're just doing a shit job of it
Dentin techs: its a pretty standard algorithm for doing this sort of thing repetitively
Dentin techs: cuts out a lot of the initialization steps that this sorts of preloaded computations require, does minimal touch based on orthogonal spaces, etc






Does that sound like something that's hard to implement, makes sense, or might be better?

Go easy on me, I'm a newbie and I'm not a coding god like you guys so I don't understand stuff like this.
Amended on Fri 05 Apr 2019 12:28 AM by Death
Australia Forum Administrator #46
I would try my suggestion first and see what you think. What he describes would, I think, still involve pauses or there would be no point to doing it.
#47
Hey Nick,

I appreciate the effort greatly.

Is it possible to not make the refresh as obvious? (the apparent blink every time you move)


That seems to take away from the whole immersive experience imo.


I'll be working on making it work today and tomorrow and I'll get back to you when I break something. xD

I appreciate the daily response so very much.

Cheers
USA Global Moderator #48
Quote:
Is it possible to not make the refresh as obvious? (the apparent blink every time you move)

Obviously that would require either drawing the border details first instead of last or re-drawing them after every update.
Amended on Fri 05 Apr 2019 05:50 PM by Fiendish
Australia Forum Administrator #49

The border details aren’t drawn after each update. The pause is inside an inner loop which draws the rooms. What you need to do is copy/paste the border stuff to be before the drawing loop. I didn’t want to do that in my example because it would have been a lot of changes which would obscure the important part.

What it used to do is:

  • Make the miniwindow
  • Populate it with all of the rooms (which were visible and inside the depth range)
  • Draw the border (in case one of the room square overlapped the border)
  • Draw the area name, help button and so on

Now this sequence didn’t matter if it all got shown at once, and it meant that I didn’t accidentally have glitches at the border.

However with the new method the border was copy/pasted to above the room drawing (so it doesn’t flicker) and also done again afterwards in case the rooms overlap it. You could copy/paste (or cut/paste) the other stuff like the help button, area name, room name, etc. to be before the rooms are drawn. That should reduce the flickering.

That should help. However you can’t really have both:

  • Completely flicker-free
  • Stop and check if there is more input from the MUD

I don’t think any other optimizations will work. Since the issue applies when you are moving, you can’t just cache existing drawing, as the map can be drawn completely differently after moving a single room, depending on how the “room walker” finds rooms.