feat: Basic mapper support.

- Automatically creates and maps rooms
- Loads default map, autosaves map periodically, and saves map on session end
- Aliases to find matching rooms, show speedwalk to room, and run speedwalk to room
- Basic map data structure
- Bootstrapping integration and loading of map data

Partially implements: https://todo.sr.ht/~fierre/termighty/1

Missing:

- Portal integration
- Suggestion of rooms/speedwalks for quests, campaigns
- Core map starter data
master
Fierre 4 years ago
parent 41b755336c
commit b95214444e

2
.gitignore vendored

@ -1,2 +1,4 @@
*.swp
logs/*.log
mapdata/autosave.map
mapdata/player.map

@ -21,9 +21,11 @@
#NOP Load script modules
#read {modules/action.tin};
#read {modules/map.tin};
#read {modules/gmcp/negotiate.tin};
#read {modules/gmcp/messaging.tin};
#read {modules/gmcp/char.tin};
#read {modules/gmcp/room.tin};
#read {modules/prompt.tin};
#read {modules/cron.tin};

@ -16,3 +16,7 @@
#list privateChannels {add} {ftalk};
#list privateChannels {add} {spouse};
#list privateChannels {add} {clantalk};
#NOP Map save/load paths;
#variable {mapAutosave} {mapdata/autosave.map};
#variable {playerMap} {mapdata/player.map};

@ -0,0 +1,255 @@
C 50000
V 2020
CAVOID <118>
CBACKGROUND
CBLOCK <218>
CEXITS <278>
CFOG <148>
CHIDE <168>
CINVISIBLE <208>
CPATHS <138>
CROOMS <178>
CSYMBOLS <128>
CUSER <258>
F 24588
G 0
I 0
L {ASCII NESW LINE} {NO EXITS} {x}
L {ASCII NESW LINE} {N} {o}
L {ASCII NESW LINE} { E} {o}
L {ASCII NESW LINE} {N E} {+}
L {ASCII NESW LINE} { S} {o}
L {ASCII NESW LINE} {N S} {|}
L {ASCII NESW LINE} { E S} {+}
L {ASCII NESW LINE} {N E S} {+}
L {ASCII NESW LINE} { W} {o}
L {ASCII NESW LINE} {N W} {+}
L {ASCII NESW LINE} { E W} {-}
L {ASCII NESW LINE} {N E W} {+}
L {ASCII NESW LINE} { S W} {+}
L {ASCII NESW LINE} {N S W} {+}
L {ASCII NESW LINE} { E S W} {+}
L {ASCII NESW LINE} {N E S W} {+}
L {ASCII NESW MISC} {USER} {x}
L {ASCII NESW MISC} {DIR UNKNOWN} {+}
L {ASCII NESW MISC} {N S VOID} {|}
L {ASCII NESW MISC} {E W VOID} {-}
L {ASCII NESW CURVED} {N E CURVED} {+}
L {ASCII NESW CURVED} {S E CURVED} {+}
L {ASCII NESW CURVED} {S W CURVED} {+}
L {ASCII NESW CURVED} {N W CURVED} {+}
L {ASCII NESW DIRS} {DIR N} {|}
L {ASCII NESW DIRS} {DIR NE} {/}
L {ASCII NESW DIRS} {DIR E} {-}
L {ASCII NESW DIRS} {DIR SE} {\\}
L {ASCII NESW DIRS} {DIR S} {|}
L {ASCII NESW DIRS} {DIR SW} {/}
L {ASCII NESW DIRS} {DIR W} {-}
L {ASCII NESW DIRS} {DIR NW} {\\}
L {NESW LINE} {NO EXITS} {\u2B51}
L {NESW LINE} {N} {\u2579}
L {NESW LINE} { E} {\u257A}
L {NESW LINE} {N E} {\u2517}
L {NESW LINE} { S} {\u257B}
L {NESW LINE} {N S} {\u2503}
L {NESW LINE} { E S} {\u250F}
L {NESW LINE} {N E S} {\u2523}
L {NESW LINE} { W} {\u2578}
L {NESW LINE} {N W} {\u251B}
L {NESW LINE} { E W} {\u2501}
L {NESW LINE} {N E W} {\u253B}
L {NESW LINE} { S W} {\u2513}
L {NESW LINE} {N S W} {\u252B}
L {NESW LINE} { E S W} {\u2533}
L {NESW LINE} {N E S W} {\u254B}
L {NESW MISC} {USER} {\u2B51}
L {NESW MISC} {DIR UNKNOWN} {\u2012}
L {NESW MISC} {N S VOID} {\u2507}
L {NESW MISC} {E W VOID} {\u254D}
L {NESW CURVED} {N E CURVED} {\u2570}
L {NESW CURVED} {S E CURVED} {\u256D}
L {NESW CURVED} {S W CURVED} {\u256E}
L {NESW CURVED} {N W CURVED} {\u256F}
L {NESW DIRS} {DIR N} {\u2191}
L {NESW DIRS} {DIR NE} {\u2B08}
L {NESW DIRS} {DIR E} {\u2B95}
L {NESW DIRS} {DIR SE} {\u2b0A}
L {NESW DIRS} {DIR S} {\u2193}
L {NESW DIRS} {DIR SW} {\u2B0B}
L {NESW DIRS} {DIR W} {\u2B05}
L {NESW DIRS} {DIR NW} {\u2B09}
L {MUDFONT NWS} {NO EXITS} {\u28CF}
L {MUDFONT NWS} {N} {\u28C7}
L {MUDFONT NWS} { NW} {\u28CE}
L {MUDFONT NWS} {N NW} {\u28C6}
L {MUDFONT NWS} { W} {\u28C9}
L {MUDFONT NWS} {N W} {\u28C1}
L {MUDFONT NWS} { NW W} {\u28C8}
L {MUDFONT NWS} {N NW W} {\u28C0}
L {MUDFONT NWS} { SW} {\u288F}
L {MUDFONT NWS} {N SW} {\u2887}
L {MUDFONT NWS} { NW SW} {\u288E}
L {MUDFONT NWS} {N NW SW} {\u2886}
L {MUDFONT NWS} { W SW} {\u2889}
L {MUDFONT NWS} {N W SW} {\u2881}
L {MUDFONT NWS} { NW W SW} {\u2888}
L {MUDFONT NWS} {N NW W SW} {\u2880}
L {MUDFONT NWS} { S} {\u284F}
L {MUDFONT NWS} {N S} {\u2847}
L {MUDFONT NWS} { NW S} {\u284E}
L {MUDFONT NWS} {N NW S} {\u2846}
L {MUDFONT NWS} { W S} {\u2849}
L {MUDFONT NWS} {N W S} {\u2841}
L {MUDFONT NWS} { NW W S} {\u2848}
L {MUDFONT NWS} {N NW W S} {\u2880}
L {MUDFONT NWS} { SW S} {\u280F}
L {MUDFONT NWS} {N SW S} {\u2807}
L {MUDFONT NWS} { NW SW S} {\u280E}
L {MUDFONT NWS} {N NW SW S} {\u2806}
L {MUDFONT NWS} { W SW S} {\u2809}
L {MUDFONT NWS} {N W SW S} {\u2801}
L {MUDFONT NWS} { NW W SW S} {\u2808}
L {MUDFONT NWS} {N NW W SW S} {\u2800}
L {MUDFONT NES} {NO EXITS} {\u28F9}
L {MUDFONT NES} {N} {\u28F8}
L {MUDFONT NES} { NE} {\u28F1}
L {MUDFONT NES} {N NE} {\u28F0}
L {MUDFONT NES} { W} {\u28C9}
L {MUDFONT NES} {N W} {\u28C8}
L {MUDFONT NES} { NE W} {\u28C2}
L {MUDFONT NES} {N NE W} {\u28C0}
L {MUDFONT NES} { SE} {\u2879}
L {MUDFONT NES} {N SE} {\u2878}
L {MUDFONT NES} { NE SE} {\u2871}
L {MUDFONT NES} {N NE SE} {\u2870}
L {MUDFONT NES} { W SE} {\u2849}
L {MUDFONT NES} {N W SE} {\u2848}
L {MUDFONT NES} { NE W SE} {\u2842}
L {MUDFONT NES} {N NE W SE} {\u2840}
L {MUDFONT NES} { S} {\u28B9}
L {MUDFONT NES} {N S} {\u28B8}
L {MUDFONT NES} { NE S} {\u28B1}
L {MUDFONT NES} {N NE S} {\u28B0}
L {MUDFONT NES} { W S} {\u2889}
L {MUDFONT NES} {N W S} {\u2888}
L {MUDFONT NES} { NE W S} {\u2882}
L {MUDFONT NES} {N NE W S} {\u2880}
L {MUDFONT NES} { SE S} {\u2839}
L {MUDFONT NES} {N SE S} {\u2838}
L {MUDFONT NES} { NE SE S} {\u2831}
L {MUDFONT NES} {N NE SE S} {\u2830}
L {MUDFONT NES} { W SE S} {\u2809}
L {MUDFONT NES} {N W SE S} {\u2808}
L {MUDFONT NES} { NE W SE S} {\u2802}
L {MUDFONT NES} {N NE W SE S} {\u2800}
L {MUDFONT VOID NWS} {NO EXITS} {\u2830}
L {MUDFONT VOID NWS} {N} {\u2838}
L {MUDFONT VOID NWS} { NW} {\u2831}
L {MUDFONT VOID NWS} {N NW} {\u2839}
L {MUDFONT VOID NWS} { W} {\u2836}
L {MUDFONT VOID NWS} {N W} {\u283E}
L {MUDFONT VOID NWS} { NW W} {\u2837}
L {MUDFONT VOID NWS} {N NW W} {\u283F}
L {MUDFONT VOID NWS} { SW} {\u2870}
L {MUDFONT VOID NWS} {N SW} {\u2878}
L {MUDFONT VOID NWS} { NW SW} {\u2871}
L {MUDFONT VOID NWS} {N NW SW} {\u2879}
L {MUDFONT VOID NWS} { W SW} {\u2876}
L {MUDFONT VOID NWS} {N W SW} {\u287E}
L {MUDFONT VOID NWS} { NW W SW} {\u2877}
L {MUDFONT VOID NWS} {N NW W SW} {\u287F}
L {MUDFONT VOID NWS} { S} {\u28B0}
L {MUDFONT VOID NWS} {N S} {\u28B8}
L {MUDFONT VOID NWS} { NW S} {\u28B1}
L {MUDFONT VOID NWS} {N NW S} {\u28B9}
L {MUDFONT VOID NWS} { W S} {\u28B6}
L {MUDFONT VOID NWS} {N W S} {\u28BE}
L {MUDFONT VOID NWS} { NW W S} {\u28B7}
L {MUDFONT VOID NWS} {N NW W S} {\u28BF}
L {MUDFONT VOID NWS} { SW S} {\u28F0}
L {MUDFONT VOID NWS} {N SW S} {\u28F8}
L {MUDFONT VOID NWS} { NW SW S} {\u28F1}
L {MUDFONT VOID NWS} {N NW SW S} {\u28F9}
L {MUDFONT VOID NWS} { W SW S} {\u28F6}
L {MUDFONT VOID NWS} {N W SW S} {\u28FE}
L {MUDFONT VOID NWS} { NW W SW S} {\u28F7}
L {MUDFONT VOID NWS} {N NW W SW S} {\u28FF}
L {MUDFONT VOID NES} {NO EXITS} {\u2806}
L {MUDFONT VOID NES} {N} {\u2807}
L {MUDFONT VOID NES} { NE} {\u280E}
L {MUDFONT VOID NES} {N NE} {\u280F}
L {MUDFONT VOID NES} { W} {\u2836}
L {MUDFONT VOID NES} {N W} {\u2837}
L {MUDFONT VOID NES} { NE W} {\u283E}
L {MUDFONT VOID NES} {N NE W} {\u283F}
L {MUDFONT VOID NES} { SE} {\u2886}
L {MUDFONT VOID NES} {N SE} {\u2887}
L {MUDFONT VOID NES} { NE SE} {\u288E}
L {MUDFONT VOID NES} {N NE SE} {\u288F}
L {MUDFONT VOID NES} { W SE} {\u28B6}
L {MUDFONT VOID NES} {N W SE} {\u28B7}
L {MUDFONT VOID NES} { NE W SE} {\u28BE}
L {MUDFONT VOID NES} {N NE W SE} {\u28BF}
L {MUDFONT VOID NES} { S} {\u2846}
L {MUDFONT VOID NES} {N S} {\u2847}
L {MUDFONT VOID NES} { NE S} {\u284E}
L {MUDFONT VOID NES} {N NE S} {\u284F}
L {MUDFONT VOID NES} { W S} {\u2876}
L {MUDFONT VOID NES} {N W S} {\u2877}
L {MUDFONT VOID NES} { NE W S} {\u287E}
L {MUDFONT VOID NES} {N NE W S} {\u287F}
L {MUDFONT VOID NES} { SE S} {\u28C6}
L {MUDFONT VOID NES} {N SE S} {\u28C7}
L {MUDFONT VOID NES} { NE SE S} {\u28CE}
L {MUDFONT VOID NES} {N NE SE S} {\u28CF}
L {MUDFONT VOID NES} { W SE S} {\u28F6}
L {MUDFONT VOID NES} {N W SE S} {\u28F7}
L {MUDFONT VOID NES} { NE W SE S} {\u28FE}
L {MUDFONT VOID NES} {N NE W SE S} {\u28FF}
L {MUDFONT CURVED} {N E} {\u2818}
L {MUDFONT CURVED} {S E} {\u28A0}
L {MUDFONT CURVED} {S W} {\u2844}
L {MUDFONT CURVED} {N W} {\u2803}
L {UNICODE GRAPHICS} {NO DIAGONAL} { }
L {UNICODE GRAPHICS} {SE} {\u2E0C}
L {UNICODE GRAPHICS} {NE} {\u2E1D}
L {UNICODE GRAPHICS} {SE NE} {>}
L {UNICODE GRAPHICS} {SW} {\u2E0D}
L {UNICODE GRAPHICS} {SE SW} {\u2E0C\u2E0D}
L {UNICODE GRAPHICS} {NE SW} {\uFF0F}
L {UNICODE GRAPHICS} {SE NE SW} {>\u2E0D}
L {UNICODE GRAPHICS} {NW} {\u2E1C}
L {UNICODE GRAPHICS} {SE NW} {\uFF3C}
L {UNICODE GRAPHICS} {NE NW} {\u2E1D\u2E1C}
L {UNICODE GRAPHICS} {SE NE NW} {>\u2E1C}
L {UNICODE GRAPHICS} {SW NW} {<}
L {UNICODE GRAPHICS} {SE SW NW} {\u2E0C<}
L {UNICODE GRAPHICS} {NE SW NW} {\u2E1D<}
L {UNICODE GRAPHICS} {SE NE SW NW} {><}
L {UNICODE GRAPHICS} {D} {-}
L {UNICODE GRAPHICS} {N} {\u2191}
L {UNICODE GRAPHICS} {S} {\u2193}
L {UNICODE GRAPHICS} {N S} {\u2502}
L {UNICODE GRAPHICS} {U} {+}
L {UNICODE GRAPHICS} {E} {\U01F806}
L {UNICODE GRAPHICS} {W} {\U01F804}
L {UNICODE GRAPHICS} {E W} {\u2500}
L {UNICODE GRAPHICS} {ROOM L} {[}
L {UNICODE GRAPHICS} {ROOM L CURVED} {(}
L {UNICODE GRAPHICS} {ROOM R} {]}
L {UNICODE GRAPHICS} {ROOM R CURVED} {)}
T {} { } {}
R { 1} {0} {} {} { } {} {} {} {} {} {1.000} {}

@ -64,3 +64,14 @@
#local {result} @checkBlessing{};
} {300};
};
#NOP ****f* modules.cron/Ticker mapAutosaver
#NOP NAME
#NOP Ticker mapAutosaver
#NOP SYNOPSIS
#NOP Periodically save the map to disk, automatically
#NOP -
#ticker {mapAutosaver} {
#local {result} @mapAutosave{};
} {$mapAutosaveInterval};

@ -24,4 +24,15 @@
#event {SESSION CONNECTED} {
#NOP Send channels over GMCP only;
#local {result} @sendGMCP{gmcpchannels on};
#local {result} @mapLoad{};
}
#NOP ****f* modules.gmcp.negotiate/Event SESSION DISCONNECTED
#NOP NAME
#NOP Event SESSION DISCONNECTED
#NOP SYNOPSIS
#NOP Perform any tasks for end-of-session.
#NOP -
#event {SESSION DISCONNECTED} {
#local {result} @mapSave{};
};

@ -0,0 +1,14 @@
#NOP ****f* modules.gcmp.room/Event IAC SB GMCP room.info
#NOP NAME
#NOP Event IAC SB GMCP room.info -- New room information received
#NOP SYNOPSIS
#NOP Process update and write to state variables
#NOP -
#event {IAC SB GMCP room.info IAC SE} {
#NOP Splat room.info into $room and pull out substructures exits and coords;
#variable {room} {%0};
#variable {exits} {$room[exits]};
#variable {coord} {$room[coord]};
#local {result} @mapRoom{};
}

@ -0,0 +1,187 @@
#variable {defaultMap} {mapdata/default.map};
#variable {mapAutosaveInterval} {300};
#NOP 178 - bold white skip background;
#variable {defaultColor} {<178>};
#NOP ****f* modules.map/Alias mfind
#NOP NAME
#NOP Alias mfind
#NOP SYNOPSIS
#NOP Find a path to a mapped room, based on exact name or room ID.
#NOP EXAMPLE
#NOP mfind 32474 -- find the Aardwolf Lottery room
#NOP mfind {The Aylorian Temple of Ivar} -- find a path to recall
#NOP SEE ALSO
#NOP modules.map/Alias msearch
#NOP modules.map/Alias mrun
#NOP -
#alias {mfind} {
#local {result} @mapFind{%1};
#showme {Speedwalk to %1: $result};
}
#NOP ****f* modules.map/Alias mrun
#NOP NAME
#NOP Alias mrun
#NOP SYNOPSIS
#NOP Find a path to a mapped room, and run it.
#NOP EXAMPLE
#NOP mrun 32474 -- run to the Aardwolf Lottery room
#NOP mrun {The Aylorian Temple of Ivar} -- run to recall
#NOP SEE ALSO
#NOP modules.map/Alias mfind
#NOP modules.map/Alias msearch
#NOP -
#alias {mrun} {
#local {result} @mapFind{%1};
#showme {Running to %1: $result};
#send {run $result};
};
#NOP ****f* modules.map/Function mapFind
#NOP NAME
#NOP Function mapFind
#NOP SYNOPSIS
#NOP Find the shortest path to a given room, zip it, and return the speedwalk.
#NOP EXAMPLE
#NOP #local {speedwalk} @mapFind{32474} -- find the path to Aardwolf lottery room
#NOP #local {speedwalk} @mapFind{The Aylorian Temple of Ivar} -- speedwalk to recall
#NOP -
#function {mapFind} {
#local {destination} {%1};
#map find $destination;
#path zip;
#path save forward {runPath};
#return {$runPath};
};
#NOP ****f* modules.map/Alias msearch
#NOP NAME
#NOP Alias msearch
#NOP SYNOPSIS
#NOP Find rooms with a partial match to the token, or room ID.
#NOP Use this to find the exact target (room ID) for mrun and mfind.
#NOP EXAMPLE
#NOP msearch 32474 -- find room info for ID 32474
#NOP msearch {The Aylorian Temple of Ivar} -- find room info for recall
#NOP msearch {ivar} -- find rooms with 'Ivar' in the name (case-insensitive)
#NOP SEE ALSO
#NOP modules.map/Alias mfind
#NOP modules.map/Alias mrun
#NOP -
#alias {msearch} {
#local {query} {%1};
#showme {Potential room matches for "$query":};
#map list {%i%*$query%*};
};
#NOP ****f* modules.map/Function mapRoom
#NOP NAME
#NOP Function mapRoom
#NOP SYNOPSIS
#NOP Process the current state of $room, $exits, and $coord from GMCP
#NOP SEE ALSO
#NOP modules.gmcp.room/Event IAC SB GMCP room.info
#NOP -
#function {mapRoom} {
#if {$room[num] == -1} {
#showme {Clan room};
}
{
#NOP Center map on the active room;
#map goto {$room[num]} {dig};
#NOP Get all map data that we have about this room;
#map get {all} {result};
#NOP Use the name set/unset to determine if we've seen this room before;
#if {"$result[name]" == ""} {
#map set {roomarea} {$room[zone]};
#map set {roomnote} {$room[environment]};
#map set {roomname} {$room[name]};
#map set {roomterrain} {$room[terrain]};
#map set {roomdesc} {$room[details]};
#map set {roomcolor} {$defaultColor};
};
#NOP Get the exits for this room;
#map get {roomexit} {exitsHere};
#foreach {$exits} {exit} {
#NOP & gets the variable index, non-existent nested vars report as 0;
#NOP See #help variable;
#if {&exitsHere[$exit] == 0} {
#NOP Get the map's stored exit roomvnum into $result;
#map get {roomvnum} {exitVnum} {$exits[$exit]};
#NOP Create an exit in the current room (set with goto above);
#map dig {$exit} {$exits[$exit]};
#NOP Set the exit room color as empty if it was new / unvisited;
#if {$exitVnum == 0} {
#map set {roomcolor} {} {$exits[$exit]};
};
};
};
};
#return {$room[num]};
};
#NOP ****f* modules.map/Function mapAutosave
#NOP NAME
#NOP Function mapAutosave
#NOP SYNOPSIS
#NOP Save the current map state to $mapAutosave
#NOP SEE ALSO
#NOP modules.cron/Ticker mapAutosaver
#NOP conf/options.tin
#NOP -
#function {mapAutosave} {
#showme {Autosaving current map...};
#map write {$mapAutosave};
#return {0};
};
#NOP ****f* modules.map/Function mapSave
#NOP NAME
#NOP Function mapSave
#NOP SYNOPSIS
#NOP Save the current map state to $playerMap
#NOP SEE ALSO
#NOP modules.gmcp.negotiate/Event SESSION DISCONNECTED
#NOP conf/options.tin
#NOP -
#function {mapSave} {
#showme {Saving map to $playerMap};
#map write {$playerMap};
#return {0};
};
#NOP ****f* modules.map/Function mapLoad
#NOP NAME
#NOP Function mapLoad
#NOP SYNOPSIS
#NOP Load the player map if possible, or the default map otherwise.
#NOP SEE ALSO
#NOP modules.gmcp.negotiate/Event SESSION CONNECTED
#NOP -
#function {mapLoad} {
#NOP Echo $? to force test to have console output to trap;
#script {mapExists} {test -w $playerMap; echo $?};
#NOP test -w(riteable) will return 0 if file exists and can be written;
#if {$mapExists[1] == 0} {
#map read {$playerMap};
}
{
#map read {$defaultMap};
};
#map flag vtmap;
#map flag static;
#map flag nofollow;
#map goto 1;
#return {0};
};
Loading…
Cancel
Save