T3cartographer
A library for the Tads3 programming language that creates an in-game graphical map from a tads3 objects and outputs it in either ascii, svg, graphviz dot or html.
Install / Use
/learn @toerob/T3cartographerREADME
Tads3 Cartographer library
version 0.7 - beta
fig.1
What is it
<img src="./examples/screenshots/screenshot11.png">The Tads3 Cartographer library is tool that generates a map from your Tads3 source code that can be used either as an aid when developing your game or as an in-game map for the player. It supports various output formats. Depending on your solution and framework it can be a map in svg, html, dot-graphics or plain old ascii.
At the bare minimum you just need to import it and supply a map command to get something to render on your screen or to a file in game directory.
In your Makefile add:
-lib ../cartographer
-source ../renderers/scalable-text-renderer
-source ../renderers/svg-renderer
In your source code, add somewhere:
DefineIAction(Map)
execAction(cmd) {
// Figure out the room's coordinates based on their direction between each other
local roomCrawler = new RoomCrawler();
local rooms = roomCrawler.crawl(gPlayerChar.location);
// (Optionally)
// Render and store an svg map to local file
local tilemap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY);
tilemap.populate(rooms);
local svgMapStr = tilemap.render();
saveStringToFile(svgMapStr, 'mapdata.svg');
"Map generated to file mapdata.svg in current folder\n";
// (Optionally)
// Render and display an in-game ascii map
local textMap = new ScalableTextTileMap(roomCrawler.maxX, roomCrawler.maxY);
textMap.populate(rooms);
"<<textMap.render()>>";
}
;
for a minimum example implementation. Then you can fine tweak the rest (per room via optional properties that overrides the defaults).
If you now compile your game and type "map" a file will be produced, like this:
<img src="./examples/screenshots/svgmap.svg">Fig.1
And in-game map like this: <img src="./examples/screenshots/screenshot1.png">
fig.2
(See example 1 for the code that produces the images above.)
The SVG-renderer (fig.1) can be suitable for a game running with the webgui while the HTML renderer might be more suitable for a game running locally with the HTML tads interpreter since it produces a table that could easily be rendered in QTads or similar HTML supportive interpreters. I would though recommend the WebUI instead since it has more advantages in rendering details.
If you want to stay true with the textformat (and perhaps get a mud revival feeling), you might want to use the two pure text-renderers which can output the map in text-format in any interpreter, including the text-only ones.
The two choices here are to either go with the ScalableTextTileMap that displays connections and doors, where the output looks like this:
ScalableTextTileMap
<img src="./examples/screenshots/screenshot2.png"> fig.3Or go with the compact looking TextTileMap looking like fig.4
In adv3Lite there's also an exemplae on how to implement the map together with a menu (similar to the help menu), using the banner api to display the map (see example1 in examples directory) and navigate it with the WASD keys. (see fig.2)
TextTileMap
Exits: north southeast in
============================================================
Th meadow
LR Th
Ha Ki Th
*Po* On Lo Lo
On
============================================================
Legend
Po = porch On = on the east side of
Lo = long road east end Th = the garden south
meadow = on the meadow Ha = hallway
Ki = kitchen LR = living room
La = landing Ba = bathroom
Be = bedroom
fig.4
HTML renderer:
The HTML maps looks like this:
<img src="./examples/screenshots/screenshot6.png">
fig.5
WebUI:
And the a very basic usage of the WebUI toghether with the svg-renderer, looks like this: <img src="./examples/screenshots/screenshot7.png">
fig.6
Basic instructions on how to use the library
Using the RoomCrawler class
Before any rooms can be displayed you need to "crawl" them, which essentially means that the rooms will be iterated recursively from any starting point (preferably the player's location), and each and every room connected will be given a (x, y, z) coordinate.
The coordinate system has its origin in 0,0 and grows (eastward) in x-axis and (southwise) in y-axis, e.g:
0 1 2 3
+--------> x
0 |
1 |
2 |
y
Not depicted here the z-axis goes up positive, and down negative
That means the coordinate {x:2,y:2,z:0} has its north neighbour room to in {x:2,y:1,z:0}, its southeast in {x:3,y:3,z:0} and its downward neighbour in {x:2,y:2,z:-1}.
The "crawling" is made with the following code:
local roomCrawler = new RoomCrawler();
local rooms = roomCrawler.crawl(gPlayerChar.location, true);
The first parameter sets the starting point to crawl from, and the second boolean type parameter is a flag to use if you only want visited rooms to be crawled through. It is optional, so you can equally well type:
local rooms = roomCrawler.crawl(gPlayerChar.location);
Select a renderer to output the map
Next up is to decide on a renderer. if you like to go with SVG, use SvgTileMap:
local svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY);
svgTileMap.populate(rooms);
saveStringToFile(svgTileMap.render(), 'map.svg');
This will render a complete svg formatted file (map.svg) in the folder of the compiled game that you can do whatever you want with. (Optionally you can print the svg directly in the game but that will require it running in webui for the svg elements to render correctly, see the examples for inspiration on how to do that.)
The two parameters where we use the values of roomCrawler.maxX, respectively roomCrawler.maxY is unit number that will be converted to pixels depending on width, height and padding settings in the SvgTileMap. The variables maxX and maxY here means to how many tiles x-wise and y-wise have been counted to during the crawling process. So when we create a tilemap we know how big that tilemap must be in order to make room for all rooms.)
Each tile's width and height have default values so you don't need to do anything to them but if you want to tweak them, that's easily done. You can either extend the constructor call with two additional parameters,
local svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY, 120, 80);
svgTileMap.populate(rooms);
or just set them directly afterwards:
local svgmap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY);
svgmap.tileWidth = 120
svgmap.tileHeight = 80
If you like to go with HTML, instead of SvgTileMap, use HtmlTileMap:
local htmlMap = new HtmlTileMap(roomCrawler.maxX,roomCrawler.maxY);
htmlMap.populate(rooms);
HtmlTileMap hasn't yet received as much love as SvgTileMap, so it's less tweakable, but it isn't easily done anyway considering Tads only supports HTML 3.2 tables without styling, (but at least the borders are hidden.)
The same could be said about the support for the graphviz dot, format which is acquired via the renderGraphvizDotMap function. It is still not fully functional, and very primitive done compared to the SvgTileMap.
local dotMap = renderGraphvizDotMap(rooms);
The ScalableTextTileMap and TextTileMap renderer will render a textbased map as seen in fig.3 and fig.4:
local map = new TextTileMap(roomCrawler.maxX,roomCrawler.maxY);
map.populate(rooms);
The TextTileMap is very simple and doesn't show any room connnections. It is more compact than the ScalableTextTileMap though.
local map = new ScalableTextTileMap(roomCrawler.maxX,roomCrawler.maxY);
map.populate(rooms);
The scalableTextTileMap is showing connections between each room and doors.
when you are done rendering with either choice of renderer, you can either display it directly to the screen or save it to a file for external viewing.
saveStringToFile(dotMap, 'mapdata.gv');
saveStringToFile(htmlMap, 'mapdata.html');
saveStringToFile(svgTileMap, 'svgmap.gv');
See example1 in examples for a complete example.
keeping the result of a crawl and using renderPredicates (SvgTileMap)
To be bit more efficient about the rendering of the rooms, it can be a good idea to crawl all the rooms first, store the result in a list, create a tilemap with that result and just re-render that map instead of redoing the whole procedure each time. However if the roomCrawler should visit all rooms no matter if they have been visited or not, then the tilemap will have to take over that responsibility.
SvgTileMap.renderPredicates therefore holds a list (empty as default) of predicates that can be set to condition what rooms (and its outgoing paths) should be rendered or not.
A convenient predicate to display only visited rooms is statically declared inside the SvgTileMap called "SvgTileMap.renderOnlyVisitedRooms" , which itself is equivalent to: "{room: libGlobal.playerChar.hasSeen(room)}"
To add it just type:
svgTileMap.renderPredicates = [SvgTileMap.renderOnlyVisitedRooms];
One solution to keep the crawling result could look like the following:
mapManager: object
roomCrawler = nil
rooms = nil
svgTileMap = nil
initMap() {
roomCrawler = new RoomCrawler();
rooms = roomCrawler.crawl(gPlayerChar.location);
svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY, 70, 50);
svgTileMap.renderPredicates = [SvgTileMap.renderOnlyVisitedRooms];
svgTileMap.populat
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。

