Generic Mapping Script

All and any discussion and development of the Mudlet Mapper.
Jor'Mox
Posts: 1146
Joined: Wed Apr 03, 2013 2:19 am

Re: Generic Mapping Script

Post by Jor'Mox »

Basic map stretching is definitely something I can add, but if you don't mind, I'd prefer to test out this version to see if it is working and fix any bugs before I add that part in. The greater the complexity, the harder debugging becomes, especially when doing it second hand. As is, this should place rooms in the way you would expect based on movement (though if you go through a special exit, the new room goes right on top of the previous room for now), and you should be able to "shift" a room, like so: shift n OR shift north
Code: [show] | [select all] lua
map = map or {}
map.room_info = map.room_info or {}
map.prev_info = map.prev_info or {}
map.aliases = map.aliases or {}

local defaults = {
    -- using Geyser to handle the mapper in this, since this is a totally new script
    mapper = {x = "-21%", y = 0, width = "20%", height = "30%"}
}

local terrain_types = {
    -- used to make rooms of different terrain types have different colors
    -- add a new entry for each terrain type, and set the color with RGB values
    -- each id value must be unique, terrain types not listed here will use mapper default color
    ["Inside"] = {id = 1, r = 255, g = 0, b = 0},
}

-- list of possible movement directions and appropriate coordinate changes
local move_vectors = {
    north = {0,1,0}, south = {0,-1,0}, east = {1,0,0}, west = {-1,0,0},
    northwest = {-1,1,0}, northeast = {1,1,0}, southwest = {-1,-1,0}, southeast = {1,-1,0},
    up = {0,0,1}, down = {0,0,-1}
}

-- used to convert short dirs for full dirs
local exits = {
    n = "north", s = "south", w = "west", e = "east",
    nw = "northwest", ne = "northeast", sw = "southwest", se = "southeast",
    u = "up", d = "down"
}

local function make_room()
    local info = map.room_info
    local coords = {0,0,0}
    addRoom(info.vnum)
    local areas = getAreaTable()
    local areaID = areas[info.area]
    if not areaID then
        areaID = addAreaName(info.area)
    else
        coords = {getRoomCoordinates(map.prev_info.vnum)}
        local shift = {0,0,0}
        for k,v in pairs(map.prev_info.exits) do
            if v == info.vnum and move_vectors[k] then
                shift = move_vectors[k]
                break
            end
        end
        for n = 1,3 do
            coords[n] = coords[1] + shift[n]
        end
        -- map stretching
        local overlap = getRoomsByPosition(areaID,coords[1],coords[2],coords[3])
        if not table.is_empty(overlap) then
            local rooms = getAreaRooms(areaID)
            local rcoords
            for _,id in ipairs(rooms) do
                rcoords = getRoomCoordinates(id)
                for n = 1,3 do
                    if shift[n] ~= 0 and (rcoords[n] - coords[n]) * shift[n] >= 0 then
                        rcoords[n] = rcoords[n] + shift[n]
                    end
                end
                setRoomCoords(id,rcoords[1],rcoords[2],rcoords[3])
            end
        end
    end
    setRoomArea(info.vnum, areaID)
    setRoomCoordinates(info.vnum, coords[1], coords[2], coords[3])
    if terrain_types[info.terrain] then
        setRoomEnv(info.vnum, terrain_types[info.terrain].id)
    end
    for dir, id in pairs(info.exits) do
        -- need to see how special exits are represented to handle those properly here
        if getRoomName(id) then
            setExit(info.vnum, id, dir)
        else
            setExitStub(info.vnum, dir, true)
        end
    end
end

local function shift_room(dir)
    local ID = map.room_info.vnum
    local x,y,z = getRoomCoords(ID)
    local x1,y1,z1 = move_vector[dir]
    x = x + x1
    y = y + y1
    z = z + z1
    setRoomCoordinates(ID,x,y,z)
    updateMap()
end

local function handle_move()
    if not getRoomName(map.room_info.vnum) then
        make_room()
    end
    centerview(map.room_info.vnum)
end

local function config()
    -- don't actually know how MSDP works... but this seems to be in line with the examples I have seen
    sendMSDP("REPORT", "ROOM")
    -- setting terrain colors
    for k,v in pairs(terrain_types) do
        setCustomEnvColor(v.id, v.r, v.g, v.b, 255)
    end
    -- making mapper window
    local info = defaults.mapper
    Geyser.Mapper:new({name = "myMap", x = info.x, y = info.y, width = info.width, height = info.height})
    -- clearing existing aliases if they exist
    for k,v in pairs(map.aliases) do
        killAlias(k)
    end
    -- making an alias to let the user shift a room around via command line
    table.insert(map.aliases,tempAlias([[^shift (\w+)$]],[[raiseEvent("shiftRoom",matches[2])]]))
end

function map.eventHandler(event,...)
     -- don't actually know how MSDP works... but this seems to be in line with the examples I have seen
    if event == "msdp.ROOM" then
        map.prev_info = map.room_info
        map.room_info = table.update({},msdp.Room)
        handle_move()
    elseif event == "shiftRoom" then
        local dir = exits[arg[1]] or arg[1]
        if not table.contains(exits, dir) then
            echo("Error: Invalid direction '" .. dir .. "'.")
        else
            shift_room(dir)
        end
    elseif event == "sysConnectionEvent" then
        config()
    end
end

registerAnonymousEventHandler("msdp.ROOM","map.eventHandler")
registerAnonymousEventHandler("shiftRoom","map.eventHandler")
registerAnonymousEventHandler("sysConnectionEvent", "map.eventHandler")
Edit: Oops. Was using the move vector to set coords, rather than properly adding them to the old coords. Fixed.
Edit2: Decided to toss in map stretching, and also fixed another bug I think I found.

Jor'Mox
Posts: 1146
Joined: Wed Apr 03, 2013 2:19 am

Re: Generic Mapping Script

Post by Jor'Mox »

Made some adjustments. See edits above. Also, tossed in map stretching anyway, after realizing it would be a bit easier for your script than it was for mine.

Ornir
Posts: 31
Joined: Thu Apr 07, 2011 8:15 am

Re: Generic Mapping Script

Post by Ornir »

Just to let you know, I have fixed some bugs and it seems to work really well! The only issue is that exits are not completed since the exit generation only works on new rooms.

I am going to make some changes tomorrow and once it is stable and appears to be working more or less ok for standard cases I will post up the code.

Jor'Mox
Posts: 1146
Joined: Wed Apr 03, 2013 2:19 am

Re: Generic Mapping Script

Post by Jor'Mox »

Hmm. That makes sense. I didn’t force exits to be two directional, and I obviously didn’t put anything in after a room was created. Should be a fairly easy thing though, just check for exit stubs when entering a room and try to connect any with the appropriate room, if it exists.

Jor'Mox
Posts: 1146
Joined: Wed Apr 03, 2013 2:19 am

Re: Generic Mapping Script

Post by Jor'Mox »

Put in something that should connect stubs in existing rooms when possible. Primarily in the handle_move function, but also adding the exitmap table at the top of the script to convert how stubs are represented into actual directions.
Code: [show] | [select all] lua
map = map or {}
map.room_info = map.room_info or {}
map.prev_info = map.prev_info or {}
map.aliases = map.aliases or {}

local defaults = {
    -- using Geyser to handle the mapper in this, since this is a totally new script
    mapper = {x = "-21%", y = 0, width = "20%", height = "30%"}
}

local terrain_types = {
    -- used to make rooms of different terrain types have different colors
    -- add a new entry for each terrain type, and set the color with RGB values
    -- each id value must be unique, terrain types not listed here will use mapper default color
    ["Inside"] = {id = 1, r = 255, g = 0, b = 0},
}

-- list of possible movement directions and appropriate coordinate changes
local move_vectors = {
    north = {0,1,0}, south = {0,-1,0}, east = {1,0,0}, west = {-1,0,0},
    northwest = {-1,1,0}, northeast = {1,1,0}, southwest = {-1,-1,0}, southeast = {1,-1,0},
    up = {0,0,1}, down = {0,0,-1}
}

-- used to convert short dirs for full dirs
local exits = {
    n = "north", s = "south", w = "west", e = "east",
    nw = "northwest", ne = "northeast", sw = "southwest", se = "southeast",
    u = "up", d = "down"
}

local exitmap = {[1] = "north",   [2] = "northeast",   [3] = "northwest",   [4] = "east",
  [5] = "west",   [6] = "south",   [7] = "southeast",   [8] = "southwest",
  [9] = "up",   [10] = "down",   [11] = "in",   [12] = "out",}

local function make_room()
    local info = map.room_info
    local coords = {0,0,0}
    addRoom(info.vnum)
    local areas = getAreaTable()
    local areaID = areas[info.area]
    if not areaID then
        areaID = addAreaName(info.area)
    else
        coords = {getRoomCoordinates(map.prev_info.vnum)}
        local shift = {0,0,0}
        for k,v in pairs(map.prev_info.exits) do
            if v == info.vnum and move_vectors[k] then
                shift = move_vectors[k]
                break
            end
        end
        for n = 1,3 do
            coords[n] = coords[1] + shift[n]
        end
        -- map stretching
        local overlap = getRoomsByPosition(areaID,coords[1],coords[2],coords[3])
        if not table.is_empty(overlap) then
            local rooms = getAreaRooms(areaID)
            local rcoords
            for _,id in ipairs(rooms) do
                rcoords = getRoomCoordinates(id)
                for n = 1,3 do
                    if shift[n] ~= 0 and (rcoords[n] - coords[n]) * shift[n] >= 0 then
                        rcoords[n] = rcoords[n] + shift[n]
                    end
                end
                setRoomCoords(id,rcoords[1],rcoords[2],rcoords[3])
            end
        end
    end
    setRoomArea(info.vnum, areaID)
    setRoomCoordinates(info.vnum, coords[1], coords[2], coords[3])
    if terrain_types[info.terrain] then
        setRoomEnv(info.vnum, terrain_types[info.terrain].id)
    end
    for dir, id in pairs(info.exits) do
        -- need to see how special exits are represented to handle those properly here
        if getRoomName(id) then
            setExit(info.vnum, id, dir)
        else
            setExitStub(info.vnum, dir, true)
        end
    end
end

local function shift_room(dir)
    local ID = map.room_info.vnum
    local x,y,z = getRoomCoords(ID)
    local x1,y1,z1 = move_vector[dir]
    x = x + x1
    y = y + y1
    z = z + z1
    setRoomCoordinates(ID,x,y,z)
    updateMap()
end

local function handle_move()
    local info = map.room_info
    if not getRoomName(info.vnum) then
        make_room()
    else
        local stubs = getExitStubs1(info.vnum)
        for _, n in ipairs(stubs) do
            local dir = exitmap[n]
            local id = info.exits[dir]
            -- need to see how special exits are represented to handle those properly here
            if getRoomName(id) then
                setExit(info.vnum, id, dir)
            end
        end
    end
    centerview(map.room_info.vnum)
end

local function config()
    -- don't actually know how MSDP works... but this seems to be in line with the examples I have seen
    sendMSDP("REPORT", "ROOM")
    -- setting terrain colors
    for k,v in pairs(terrain_types) do
        setCustomEnvColor(v.id, v.r, v.g, v.b, 255)
    end
    -- making mapper window
    local info = defaults.mapper
    Geyser.Mapper:new({name = "myMap", x = info.x, y = info.y, width = info.width, height = info.height})
    -- clearing existing aliases if they exist
    for k,v in pairs(map.aliases) do
        killAlias(k)
    end
    -- making an alias to let the user shift a room around via command line
    table.insert(map.aliases,tempAlias([[^shift (\w+)$]],[[raiseEvent("shiftRoom",matches[2])]]))
end

function map.eventHandler(event,...)
     -- don't actually know how MSDP works... but this seems to be in line with the examples I have seen
    if event == "msdp.ROOM" then
        map.prev_info = map.room_info
        map.room_info = table.update({},msdp.Room)
        handle_move()
    elseif event == "shiftRoom" then
        local dir = exits[arg[1]] or arg[1]
        if not table.contains(exits, dir) then
            echo("Error: Invalid direction '" .. dir .. "'.")
        else
            shift_room(dir)
        end
    elseif event == "sysConnectionEvent" then
        config()
    end
end

registerAnonymousEventHandler("msdp.ROOM","map.eventHandler")
registerAnonymousEventHandler("shiftRoom","map.eventHandler")
registerAnonymousEventHandler("sysConnectionEvent", "map.eventHandler")

User avatar
SlySven
Posts: 1023
Joined: Mon Mar 04, 2013 3:40 pm
Location: Deepest Wiltshire, UK
Discord: SlySven#2703

Re: Generic Mapping Script

Post by SlySven »

@Ornir you say you are an implementer - will your users have access to the same data that you have - as that may have an impact on what the mapper code has to do to work for them - for instance are they going to get the room vnums or will they only get room names (and will those be unique or be unique if combined with a description text)? Are you planning to have a realistic room placement or will there be exits that would not be realisable in a real 3D space? These things can influence the design of a good mapper to help them find their way around your World!

Ornir
Posts: 31
Joined: Thu Apr 07, 2011 8:15 am

Re: Generic Mapping Script

Post by Ornir »

Jor'Mox - Thank you so very much for the help. Here is the script I currently have, it works (mostly) only the map stretching has some bugs.

SlySven - I am a huge believer in providing as much data to the player as I can, if it will improve their playing experience. Therefore I give VNUMS to everyone via MSDP. This mapper will be part of the client GUI package I am developing for Luminari MUD.
Code: [show] | [select all] lua
map = map or {}
map.room_info = map.room_info or {}
map.prev_info = map.prev_info or {}
map.aliases = map.aliases or {}
map.enabled = true;


local defaults = {
    -- using Geyser to handle the mapper in this, since this is a totally new script
    mapper = {x = "-21%", y = 0, width = "20%", height = "30%"}
}

local terrain_types = {
    -- used to make rooms of different terrain types have different colors
    -- add a new entry for each terrain type, and set the color with RGB values
    -- each id value must be unique, terrain types not listed here will use mapper default color
    ["Inside"]         	= {id = 1, r = 130, g = 130, b = 130},
		["City"]           	= {id = 2, r = 200, g = 200, b = 200},
		["Field"] 					= {id = 3, r = 0, g = 170, b = 0},
		["Forest"] 					= {id = 4, r = 0, g = 122, b = 0},
		["Hills"] 					= {id = 5, r = 122, g = 69, b = 0},
		["Low Mountains"] 	= {id = 6, r = 116, g = 116, b = 116},
		["Water (Swim)"] 		= {id = 7, r = 0, g = 0, b = 255},
		["Water (No Swim)"] = {id = 8, r = 0, g = 0, b = 130},
		["In Flight"] 			= {id = 9, r = 200, g = 200, b = 255},
		["Underwater"] 			= {id = 10, r = 43, g = 43, b = 124},
		["Zone Entrance"] 	= {id = 211, r = 255, g = 0, b = 0},
		["Road North-South"] = {id = 12, r = 119, g = 101, b = 86},
		["Road East-West"] 	= {id = 13, r = 119, g = 101, b = 86},
		["Road Intersection"] = {id = 14, r = 119, g = 101, b = 86},
		["Desert"] 					= {id = 15, r = 234, g = 219, b = 124},
		["Ocean"] 					= {id = 16, r = 0, g = 90, b = 90},
		["Marshland"] 			= {id = 17, r = 81, g = 47, b = 109},
		["High Mountains"] 	= {id = 18, r = 255, g = 255, b = 255},
		["Outer Planes"] 		= {id = 19, r = 168, g = 42, b = 138},
		["Underdark - Wild"] = {id = 20, r = 131, g = 110, b = 145},
		["Underdark - City"] = {id = 21, r = 183, g = 178, b = 186},
		["Underdark - Inside"] = {id = 22, r = 132, g = 132, b = 132},
		["Underdark - Water (Swim)"] = {id = 23, r = 70, g = 139, b = 175},
		["Underdark - Water (No Swim)"] = {id = 24, r = 34, g = 68, b = 86},
		["Underdark - In Flight"] = {id = 25, r = 158, g = 178, b = 188},
		["Lava"] 						= {id = 26, r = 255, g = 119, b = 0},
		["Dirt Road North-South"] = {id = 27, r = 142, g = 85, b = 0},
		["Dirt Road East-West"] = {id = 28, r = 142, g = 85, b = 0},
		["Dirt Road Intersection"] = {id = 29, r = 142, g = 85, b = 0},
		["Cave"] 						= {id = 30, r = 80, g = 80, b = 80},
		["Jungle"] 					= {id = 31, r = 21, g = 132, b = 101},
		["Tundra"] 					= {id = 32, r = 224, g = 224, b = 224},
		["Taiga"] 					= {id = 33, r = 103, g = 137, b = 104},
		["Beach"] 					= {id = 34, r = 239, g = 235, b = 0},
}

-- list of possible movement directions and appropriate coordinate changes
local move_vectors = {
    north = {0,1,0}, south = {0,-1,0}, east = {1,0,0}, west = {-1,0,0},
    northwest = {-1,1,0}, northeast = {1,1,0}, southwest = {-1,-1,0}, southeast = {1,-1,0},
    up = {0,0,1}, down = {0,0,-1}
}

-- used to convert short dirs for full dirs
local exits = {
    n = "north", s = "south", w = "west", e = "east",
    nw = "northwest", ne = "northeast", sw = "southwest", se = "southeast",
    u = "up", d = "down"
}

local exitmap = {
    north = 1,      northeast = 2,      northwest = 3,      east = 4,
    west = 5,       south = 6,          southeast = 7,      southwest = 8,
    up = 9,         down = 10,          ["in"] = 11,        out = 12,
    [1] = "north",  [2] = "northeast",  [3] = "northwest",  [4] = "east",
    [5] = "west",   [6] = "south",      [7] = "southeast",  [8] = "southwest",
    [9] = "up",     [10] = "down",      [11] = "in",        [12] = "out",
}
	
for k, v in pairs(exitmap) do
	
end	

local function make_room()
    local info = map.room_info
    local coords = {0,0,0}
    addRoom(info.VNUM)
    local areas = getAreaTable()
    local areaID = areas[info.AREA]
    if not areaID then
        areaID = addAreaName(info.AREA)
    else
        coords = {getRoomCoordinates(map.prev_info.VNUM)}								
        local shift = {0,0,0}
        for k,v in pairs(map.prev_info.EXITS) do
            if v == info.VNUM and move_vectors[k] then
                shift = move_vectors[k]								
                break
            end
        end
				
        for n = 1,3 do
            coords[n] = coords[n] + shift[n]
        end
											
        -- map stretching
        local overlap = getRoomsByPosition(areaID,coords[1],coords[2],coords[3])				
				
        if not table.is_empty(overlap) then
            local rooms = getAreaRooms(areaID)
            local rcoords		
            for _,id in ipairs(rooms) do
                rcoords = getRoomCoordinates(id)
                for n = 1,3 do
                    if shift[n] ~= 0 and (rcoords[n] - coords[n]) * shift[n] >= 0 then
                        rcoords[n] = rcoords[n] + shift[n]
                    end
                end
                setRoomCoords(id,rcoords[1],rcoords[2],rcoords[3])
            end
        end
    end
		setRoomArea(info.VNUM, areaID)
    setRoomCoordinates(info.VNUM, coords[1], coords[2], coords[3])
    if terrain_types[info.TERRAIN] then
        setRoomEnv(info.VNUM, terrain_types[info.TERRAIN].id + 16)
    end
    for dir, id in pairs(info.EXITS) do
        -- need to see how special exits are represented to handle those properly here				
        if not setExit(info.VNUM, id, exitmap[dir]) then       
            setExitStub(info.VNUM, exitmap[dir], true)
        end
    end
end

local function shift_room(dir)
    local ID = map.room_info.VNUM
    local x,y,z = getRoomCoordinates(ID)
    local x1,y1,z1 = move_vectors[dir]
    x = x + x1
    y = y + y1
    z = z + z1
    setRoomCoordinates(ID,x,y,z)
    updateMap()
end

local function handle_move()
		if not getRoomName(map.room_info.VNUM) then
        make_room()
    else		
			if terrain_types[map.room_info.TERRAIN] then
        setRoomEnv(map.room_info.VNUM, terrain_types[map.room_info.TERRAIN].id + 16)
    	end 	  
      local stubs = getExitStubs1(map.room_info.VNUM)				
			if stubs then		
       	for _, n in ipairs(stubs) do
          local dir = exitmap[n]				
	        local id = map.room_info.EXITS[dir]
	        -- need to see how special exits are represented to handle those properly here
	        if getRoomName(id) then	        
					  setExit(map.room_info.VNUM, id, exitmap[dir])
	        end
  	    end
			end
    end
    centerview(map.room_info.VNUM)
end

local function config()        
    -- setting terrain colors
    for k,v in pairs(terrain_types) do
        setCustomEnvColor(v.id + 16, v.r, v.g, v.b, 255)
    end
    -- making mapper window
    local info = defaults.mapper
    Geyser.Mapper:new({name = "myMap", x = info.x, y = info.y, width = info.width, height = info.height})
    -- clearing existing aliases if they exist
    for k,v in pairs(map.aliases) do
        killAlias(k)
    end
    -- making an alias to let the user shift a room around via command line
    table.insert(map.aliases,tempAlias([[^shift (\w+)$]],[[raiseEvent("shiftRoom",matches[2])]]))
end

function map.eventHandler(event,...)     
    if event == "msdp.ROOM" then
        map.prev_info = map.room_info
        map.room_info = table.update({},msdp.ROOM)
        handle_move()
    elseif event == "shiftRoom" then
        local dir = exits[arg[1]] or arg[1]
        if not table.contains(exits, dir) then
            echo("Error: Invalid direction '" .. dir .. "'.")
        else
            shift_room(dir)
        end
    elseif event == "sysConnectionEvent" then
        config()	
    end
end

function map.onProtocolEnabled(_, protocol)
  if protocol == "MSDP" then
    print("MSDP enabled!")
		sendMSDP("REPORT", "ROOM")
		config()	
  end
end

registerAnonymousEventHandler("msdp.ROOM","map.eventHandler")
registerAnonymousEventHandler("shiftRoom","map.eventHandler")
registerAnonymousEventHandler("sysConnectionEvent", "map.eventHandler")
registerAnonymousEventHandler("sysProtocolEnabled", "map.onProtocolEnabled")

Jor'Mox
Posts: 1146
Joined: Wed Apr 03, 2013 2:19 am

Re: Generic Mapping Script

Post by Jor'Mox »

Awesome to hear that things are mostly working at this point. If you explain what is going on improperly with the map stretching, I can probably squash whatever bugs are involved. I'm also curious to see if/when you get around to trying to map some special exits (i.e. ones not in the exit map). There are functions available to make those work, but without seeing how those are represented in the MSDP data, I obviously couldn't make this code handle them properly. If you need some help when you get to that point, be sure to let me know.

As a side note, I definitely look forward to seeing this as a finished product. It could very easily be used as a template for ANY game in which VNUMs (or other unique room identifiers) are available to the users.

Ornir
Posts: 31
Joined: Thu Apr 07, 2011 8:15 am

Re: Generic Mapping Script

Post by Ornir »

So MSDP is pretty cool because you can define whatever variables you want. There is not a standard that I am aware of for reporting on doors and special exits, so this is something I will need to create. I will create a new thread to discuss this. I am not certain yet if providing data regarding portal destinations is something I want to do, but I am leaning towards yes.

I would love to make this a generic MSDP mapping script, or maintain a version that fills that gap before adding more game-specific features.

Jevak
Posts: 6
Joined: Mon Dec 25, 2017 5:55 am

Re: Generic Mapping Script

Post by Jevak »

Hello i'm looking for a mapping script and i have no scripting knowledge at all, i found your post and have no clue how to set it up, can you help? here a picture of the mapping format for the MUD i play.
Attachments
mapping example.png
mapping example.png (47.8 KiB) Viewed 6128 times

Post Reply