Automaps in a Glk extension

I was thinking about what it could be like to implement a Glk (GlkOte? GlkOte only?) extension that could support displaying a map provided by the interpreter, e.g. ADRIFT.

Automapping is a pretty weird topic with a lot of edge cases; I’m not surprised nobody’s tried to take it on before.

I started imagining a Glk extension shaped like this:

#define mapimage_SVG     1  /* UTF-8 SVG; root must define a viewBox or width/height */
#define mapimage_PNG     2
#define mapimage_JPEG    3

#define mapflag_None          0x00000000
#define mapflag_HasFocus      0x00000001  /* focus_* arguments are meaningful */
#define mapflag_SuggestShow   0x00000002  /* soft hint: prefer making the map visible */
#define mapflag_UserRequestedShow   0x00000004  /* the user just asked to display the map */

glui32 glk_map_present(
    glui32 format,                 /* mapimage_* */
    const unsigned char *data,     /* document bytes */
    glui32 len,
    glui32 flags,                  /* mapflag_* */
    glsi32 focusleft,
    glsi32 focustop,
    glui32 focuswidth,
    glui32 focusheight);

void glk_map_clear(void);

glk_map_present() would create or replaces the current “map document.”

  • Returns 1 on success, 0 on failure (unsupported format, corrupt data, refused document, etc.).
  • On return (success or failure), the library retains its own copy of the document; the program may free data immediately.
  • The runner should provide some UI for panning and zooming the map, as well as hiding/showing the map, but this isn’t required. (Maybe there’d be testable gestalts for those features…? That way the terp/author could say “if this Glk runner doesn’t support panning and zooming, don’t show the map at all”)
  • A successful glk_map_present does not require the map to become visible. If the player previously hid the map, it generally stays hidden, but the latent document updates.
    • mapflag_SuggestShow is a hint that the library may show it (e.g. first map of the session, or after a major update).
    • mapflag_UserRequestedShow says that the user just requested to show the map (we have to take the terp’s word for it, but perhaps the user just typed map or show map, or selected “Show Map” off of an in-game menu), so the runner should re-show the map after the user has hidden it.
  • If mapflag_HasFocus is set, the focus rectangle is the region the library should try to keep viewable after present (and typically after player “recenter” actions). If focus is omitted, the library may keep the current pan/zoom or fit the whole document — implementation-defined.
  • Just one map is allowed at a time. (Right??)

glk_map_clear() destroys the map document. The library should remove map chrome (or disable map UI). It is legal when no map exists.

Refocus without re-sending the document

void glk_map_set_focus(
    glsi32 focusleft,
    glsi32 focustop,
    glui32 focuswidth,
    glui32 focusheight);

void glk_map_clear_focus(void);

Present an image resource from a Blorb?

glui32 glk_map_present_image(
    glui32 image,       /* Blorb pict id, as in glk_image_draw */
    glui32 flags,
    glsi32 focusleft,
    glsi32 focustop,
    glui32 focuswidth,
    glui32 focusheight);

Blorbs don’t support SVG (yet, maybe ever). SVG is an enormously complicated standard, with zillions of optional features, some of which can do I/O. :grimacing: But for something like Counterfeit Monkey, where the map is a handrolled PNG, this feels like a nice convenience.

If the map can be a rasterized image resource, you’ll probably want to rasterize map overlays, too

“Map overlays” are layered images that you can stack on top of a map (and on top of each other).

SVGs don’t need a separate API for overlays (because you can just put the overlay content directly into the SVG; SVGs are just XML text and easy to merge).

But if you want to draw a “you are here” symbol on top of a rasterized map, or if you want to change the background color of one room in the map, you’ll probably need an API like this:

overlayid_t glk_map_overlay(
    glui32 image,       /* Blorb pict id, as in glk_image_draw */
    glsi32 left,
    glsi32 top,
    glui32 width,
    glui32 height,
    glui32 zindex);

glui32 glk_map_overlay_move(
    overlayid_t overlay,
    glsi32 left,
    glsi32 top,
    glui32 width,
    glui32 height,
    glui32 zindex);

glui32 glk_map_overlay_clear(overlayid_t overlay);

glui32 glk_map_overlay_clear_all(void);

The map itself would have zindex 0; overlays with higher zindex would render on top of (obscuring) overlays with lower zindex.

Out of scope?

  • Multiple maps
  • Clickable maps

What would the Glk runner do, actually?

For comparison

  • Counterfeit Monkey: Managing its map has been a huge source of bugs over the years.
    • As of v11, Counterfeit Monkey starts by disabling its map, allowing users to reveal the map as a graphics window that occupies the side pane with map on. If you do that on mobile, you’re going to have a bad time, but you’ll probably be able to figure out how to map off to make it go away.
    • Alternately, if you just want to peek at the map, you can type map, which displays in the text buffer as a full-screen image.
    • Each room has its own pre-rendered map, with the “you are here” symbol burned into the image. When showing the map, the UI just shows the current room’s pre-rendered image. (This feels suboptimal. Separate map overlay layers would work much, much better.)
  • Hadean Lands: Custom UI everywhere for Hadean Lands. I don’t think any of the source of that is publicly available…?
    • On desktop OSes, it starts by opening multiple OS windows, one for the text buffer, one for the map, and one for the “journal” that includes rituals, formulas, and facts. The map is pre-rastered as a PNG, with labels as overlays that you can hide/show with a “Labels” button in the map menu, and an animated “you are here” symbol, a star. There’s a “Flash Star” button that enables/disables flashing.
    • On iOS, the game launches in a tabbed view, with these tabs: Game (the text buffer), Journal, Map, Help, Settings.
  • ADRIFT: (This is what originally got me thinking about this problem.) In ADRIFT, authors can declare a map of rooms and exits, each with visibilities. You can make the whole map visible at game start, or reveal the map room-by-room as you explore. From the user’s perspective, it feels like an “automap”, but it’s not trying to automatically parse the transcript; it’s revealing more and more of the author’s hand-coded map model.
    • ADRIFT also lets you click on rooms of the map; the game runs Dijkstra’s algorithm to find the shortest path from your location to the destination, and automatically emits “go north.” “go west.” etc. commands to get you there.
    • The official ADRIFT-5 runner is only available for Windows. ADRIFT’s runner has a fancy dockable UI. You can drag and drop the map window (or other “named windows”) to the top, bottom, right, and left of the screen; you can pop them out and turn them into floating windows that you can reposition wherever you like.


    • FrankenDrift is available on other platforms; it makes a separate OS window available for the map, which the user can position manually. (On macOS, the map window spawns underneath the text buffer, making it difficult to discover, but it is there.) FrankenDrift doesn’t attempt to render a map on Glk.

Terp-rendered SVG maps

Some interpreters support author-provided automaps, like ADRIFT.

In that case, the terp would render the map as an SVG string and present it via glk_map_present().

As the map updates (e.g. moving the “you are here” marker), the terp would call glk_map_present() on each turn, presenting (representing) the entire SVG string each time the map updates. (SVG strings are quite short.)

Color recommendation for terp-rendered SVG maps: Measure the Normal TextColor/BackColor with glk_style_measure, and use those colors in the generated SVG (for now?)

glk_style_measure can report on the foreground text color and background color of the “Normal” style for the transcript window. If the user has opted to override stylehints that the terp has provided, glk_style_measure will report that, giving you a reasonable background color and foreground color for rendering.

If glk_style_measure returns false, use a default color scheme. For example, RemGlk interpreters (including Parchment and Lectrote) don’t implement glk_style_measure at all, returning FALSE in all cases. (glk_style_measure would require an asynchronous remote procedure call, asyncifying the function.)

I note that stylehints have been kinda “deprecated” for years, partially replaced by the garglk_set_zcolors Glk extension, and hopefully soon to be replaced by the CSS Glk extension.

But, as I write this, there is no other API proposed to access the normal text colors, so glk_style_measure is all we have.

My guesses as to what the UI would look like

I’m imagining that Glk runners that support mapping would have different UI for large/small screens (and that the runners would be the best decision makers for how/when to do that).

  • Parchment/webUI runners would probably display the map in a side pane with a close button if the window is “wide enough.” If the window is too narrow, the map would be hidden by default. If there’s an on-screen menu (a hamburger menu, or a toolbar), a “Show Map” button could appear there if the map isn’t visible. (And, I think webUI runners should have a menu, at least a hamburger menu, with buttons to save, restore, restart, adjust settings, and get help.)
  • Desktop runner apps (Gargoyle, Spatterlight, Lectrote) could open the map in an OS window, which the user could reposition. ADRIFT’s runner has a fancy dockable UI, which feels like overkill to me, but it works, and maybe that’s “best” for power users. IMO non-power users struggle to understand it. Grandpa’s Ranch (a beginner-friendly ADRIFT game intended for the Text Adventure Literacy Jam) had to include a bunch of in-game documentation trying to explain to newbies how ADRIFT’s layout system works, which suggests to me that ADRIFT’s layout system is the wrong approach, even for a desktop-only IF interpreter.
    • When closing the map, perhaps it would make sense to print a message explaining how to re-open the map, e.g. with a “View → Map” menu, or by typing a MAP command or something.
  • Mobile apps have been pretty creative about menus.
    • I mentioned Hadean Lands’s tabs; that looks pretty good.
    • A hamburger menu would work, too.
    • iOS Frotz has a little menu “book” in the prompt; you can tap on it to get a bunch of commands to auto-type. Adding “map” in there would be nice.
    • Check out Fabularium’s custom-keyboard menu. I notice the M key doesn’t have anything on it, which would be perfect for showing the map!
      image

What do you think?

Is this anything? If I filed PRs on Parchment, Gargoyle, Lectrote, etc. to support this, would you consider merging those PRs, or just close them without merging?

When I see “automap” I think of a system which will automatically draw a map for the player based on a game transcript, adjusting room connections and positions as it goes, based on what the player does.

This seems quite different - it’s really about a pan-and-zoom interface (like Google Maps)? It looks like you’re mostly envisioning a single base image with optionally some additional graphics drawn on top, though they would have to be manually positioned by the game author? If the author is still having to position image elements according to image offsets then I don’t think it really qualifies as an automap.

But a pan-and-zoom mode for a graphics window is an interesting idea in its own right! It would entail basically two things: the idea of a “canvas” size distinct from the actual pixel size of the graphics window (which many graphics extensions would want to use anyway), and a way to specify the original zoom/“focus”/crop of the canvas. If you could obtain the zoom level then you could also use different image sources to draw onto the canvas. More than 2 levels would probably be overkill, but an extension could handle it. Maybe an event to indicate a change in zoom/pan.

Glk libraries would need to support mouse or multi touch pan/zoom operations, and probably add zoom in/out buttons as well.

There’s definitely potential here, though the details will need ruminating over. As would author and player demand. In app maps are great, but simply providing a PDF map would end up better for a lot of both authors and players.

For Parchment something to keep in mind is whether the operations could be transmitted via JSON.

In ADRIFT, authors can declare a map of rooms and exits, each with visibilities. You can make the whole map visible at game start, or reveal the map room-by-room as you explore.

From the user’s perspective, that feels like an “automap,” and I’d call it an automap, but it’s not trying to automatically parse the transcript; it’s revealing more and more of the author’s hand-coded map model. (You can’t do that with PDF!)

Even if the map is hardcoded and pre-rastered, one of the biggest advantages of an in-game map is a “you are here” symbol, which PDFs also can’t provide. But also, having the runner provide a UI for the map potentially solves the problems that Counterfeit Monkey has been wrestling with.

the idea of a “canvas” size distinct from the actual pixel size of the graphics window

This is baked in to SVG.

you could also use different image sources to draw onto the canvas

That’s what I was calling “map overlays,” and I thought (hoped?) it’d be out of scope.

I updated my original post with screenshots of ADRIFT.

A few months ago, I ported the ADRIFT 5 automapper to Javascript for use in my port of SuperPAC (link in AIF forum).

I gave it a try.

  • By default, the map is in “float” mode, where it’s a draggable rectangle, meant to evoke an OS window.
  • There’s no way to pan the map, but the whole map seems to be only a handful of rooms wide/tall, so there’s no need to do so; it all fits on one screen.
  • You can drag the corner to resize it, which is the only way to zoom in (e.g. if the text is too small.)
  • You can click on rooms to autowalk there.
  • There’s an “Options” menu allowing you to switch the map to “dock” mode, which then occupies a fixed pane on the screen. You can set the map to “float”, “dock”, or “none” to hide the map; you can do the same thing with the “graphics” window (float/dock/none).
  • Both windows default to “none” on mobile; neither “float” nor “dock” mode work properly on iPhone Safari 26. You can’t drag the floating rectangle or resize it. In “dock” mode, the map occupies the full screen, and I can’t get access to the transcript.

Overall, the moral I draw from this remains the same:

  • Drawing a map (eliminating the need for users to draw their own map) is useful.
    • “You are here” symbols provide value over static PDF maps.
    • Gradually revealing the map is also helpful to avoid spoilers.
    • Clicking on a map room to walk there (“autowalk”) is also useful, especially in large maps.
  • To make the software work properly, authors should provide their own map model (rather than having the runner try to parse the transcript).
  • If the map is in a fixed pane, users need the ability to turn it on and off.
  • A full-screen “map” mode is needed on mobile.
  • If there’s a full-screen map mode, the UI probably requires an on-screen menu.
    • On-screen menus are pretty great, even without maps. Save, Restore, Restart, Settings all fit well in an on-screen menu.
    • A “map” command can also work, but that would require users to know how to use it. (Maps are especially useful for newbies, who are especially unlikely to know/retain a big list of commands.)

So if I’m understanding right, this proposal lets a Glk application request a window (which will be positioned wherever the Glk library thinks best) that can be zoomed and panned by the user, and the Glk application can then draw its map into that (but needs to handle the zooming and panning itself)?

GUI highly resembles the ADRIFT setup because it’s a port of an ADRIFT 5 game. Making the map non-scrollable and non-zoomable was deliberate on my part; the map is small enough to fit all at once. (Except there’s a second floor to the map, and it shows the current floor.)

Also, it shows indicators for the various NPCs on the map (the little circles).

(It’s weird that Dan just put more effort into commenting on the automap than the various testers put into commenting on the game.)

That’s not the way I’d phrase it, if I understand my Glk terms correctly. (I might not understand my Glk terms correctly! I’m pretty new to Glk.)

In my terms:

  • Gargoyle.exe/Gargoyle.app is a “Glk application”. (Parchment is also a “Glk application.”)
  • A Glk application can embed one or more “embedded Glk interpreters”. A Glk application can itself be considered an “interpreter” (if it allows users to choose a game to play), but an “application” is the top-level interpreter.
  • When an embedded Glk interpreter uses the Glk API, it can be linked with any compatible Glk “library” as part of building the Glk application.
    • C-based Glk libraries (cheapglk, RemGlk) call the embedded interpreter’s glk_main() function. The embedded interpreter can then open one or more Glk “windows” (panes) with glk_window_open(), call glk_select(), and request input with glk_request_line_event(). (It decides what Glk APIs to call by interpreting the game file.)
    • GlkOte-based Glk libraries (including AsyncGlk) call an accept function on the window.Game object, which expects JSON responses (usually from RemGlk)
  • The embedded Glk interpreter interprets the game file and calls Glk API functions.

In my terms, I would say that the “embedded Glk interpreter” can provide a map image (which will be displayed/positioned wherever the “Glk application” thinks best) that can be zoomed and panned by the user. The “Glk application” would decide whether to open an OS window, or to use a window pane in the current OS window, or to just display the map full-screen, allowing the user to dismiss it.

To clear up other possible misunderstandings:

  • My proposal does not call for displaying the map in a “Glk window.” (Glk windows are normally not OS windows; in hindsight, I wish we’d called them “panes” instead.)
  • glk_map_present would return a success code, but not a window; glk_map_present would not have a “rock” (a reference/handle) that refers to the map.
  • If the Glk application decides to present the map in a docked side pane, it would resize the top-level Glk window (firing a Glk resize event), reserving space for the map in its own area outside the Glk window-arrangement system.
  • Thus, it would not be possible to render the map in a docked side pane underneath the status line, because the status line is a pane in the Glk window-arrangement system.
  • Neither would it be possible to sandwich the map between two Glk windows (panes).
Could a Glk map API use Glk windows (panes)? Mayyyybe, but I think it's a bad idea.
  • Glk currently doesn’t have a model of “floating windows,” windows that the user can drag around and reposition. All Glk windows are panes inside the game’s rectangle.
  • Neither does Glk have a mechanism for users to control which Glk windows are visible/closable. (If it did, there would presumably have to be some generic UI at the Glk application layer to re-show hidden/closed windows… I think it would be difficult to design an API like that.)
  • Glk window layout is under the control of the embedded interpreter, who usually leaves a lot of that under the control of the author, which means that the Glk application can’t implement “responsive design” all on its own, where the window layout changes when the layout gets narrow/wide.
    • Early versions of Counterfeit Monkey showed the map in a docked side pane (a Glk window) at all times, even on mobile. Glk applications couldn’t jump in and fix that; the fix had to be in Counterfeit Monkey. (And in all N applications that try to do this.)
    • City of Secrets still doesn’t play correctly on mobile, for reasons like this.

Giving the Glk application control over the map position seems like the right approach.

It seems like revealing part of the map bit by bit is an important aspect of the Adrift implementation, but it’s not part of this proposal?

It doesn’t need to be, because ADRIFT could call glk_map_present each time the player moves, and/or reveals a room. The interpreter would generate a new SVG each time; the runner application wouldn’t need to know anything about that.

I think the API is too simplistic, requiring sending a potentially large image whenever the map changes (which could be often). Plus the requirement for SVG would be too burdensome for some interpreters.

A better API, imho, would be to send multiple images to populate the map window with. These images would come from a blorb and use blorb ID numbers. The map would need a way to be cleared, so it can be rebuilt from scratch after restoring a savegame (etc).

The map window could be layered (each image having a Z component), which makes things like a “you are here” image easy to implement for the gamecode or interpreter.

Yeah, that’s what I called “map overlays.”

For rasterized (PNG/JPG) maps, I guess it makes sense that you’d want rasterized map overlays, as well. (It does suck that Counterfeit Monkey is embedding N images for N rooms, each with the “you are here” symbol burned in.)

I’ve added a “If the map can be a rasterized image resource, you’ll probably want to rasterize map overlays, too” section to my top post:

overlayid_t glk_map_overlay(
    glui32 image,       /* Blorb pict id, as in glk_image_draw */
    glsi32 left,
    glsi32 top,
    glui32 width,
    glui32 height,
    glui32 zindex);

glui32 glk_map_overlay_move(
    overlayid_t overlay,
    glsi32 left,
    glsi32 top,
    glui32 width,
    glui32 height,
    glui32 zindex);

glui32 glk_map_overlay_clear(overlayid_t overlay);

glui32 glk_map_overlay_clear_all(void);

But I think SVGs (or some vector-art standard like SVGs, e.g. PDFs) will be necessary for cases like ADRIFT, where the blorb doesn’t include any rasterized map images. Plus, vector art looks way better than rasterized images when zooming in, especially rasterized text.

SVGs are surprisingly easy to generate from scratch; you don’t even need an “SVG library” to generate SVGs. They’re just XML text, and they’re almost never “potentially large images.” (But the Glk runner applications would need to embed an SVG library to render SVG.)

SVGs are a potential security risk in browsers however. I haven’t looked into whether they can be safely sandboxed. If they can be then they’d be good to support generally, not just for this.

Dannii, in these days one must be very cautious on “potential security issues”, for many OT reasons.

Said that, I prefer an “advent calendar” approach, with the “visited” status being also the open/closed status of the calendar’s door. This allow an agnostic approach to the picture format of the complete map and its overlay.

Best regards from Italy,
dott. Piergiorgio.

Dannii, what exactly is the security risk? (Yes, the person who created the SVG can run Javascript in the context of the site, but the site creator can already send arbitrary Javascript in the context of that site.)

Andrewj: What do you mean by potentially large? The given images were under 8k, and size doesn’t matter because they’re being generated locally.

Right, but the person who created the SVG is not the site creator, right? They’re any rando on the internet who wrote an IF piece.

An interpreter like Parchment lets you play storyfiles from anywhere on the internet. So I need to be careful what is allowed.

Here’s a possible API for presenting a map model via Glk, instead of accepting user-provided SVGs.

Presenting the model

You’d call glk_map_present_model with an array of “nodes” and “links”.

/* mapflags for glk_map_present_model: */
#define mapflag_SuggestShow         0x00000002  /* soft hint: prefer showing the map */
#define mapflag_UserRequestedShow   0x00000004  /* user asked to show map; re-show if hidden */


/* Replace the current semantic map model.
   Returns 1 on success, 0 on failure (invalid parameters, OOM, unsupported).
   Library copies what it needs; pointers need only live for the call. */

glui32 glk_map_present_model(
    glui32 mapflags,
    const glk_mapnode_t *nodes,
    glui32 nnodes,
    const glk_maplink_t *links,
    glui32 nlinks
);

void glk_map_clear(void);

Nodes

typedef struct glk_mappoint_struct {
    glsi32 x, y, z;            /* abstract map units, not pixels */
} glk_mappoint_t;

/* nodeflags: */
#define mapnode_None    0
#define mapnode_Here    0x1    /* "you are here"; used on at most one node */
#define mapnode_Focus   0x2    /* include this room’s box in the camera focus region;
                                  runner unions all Focus boxes and keeps them viewable.
                                  Independent of Here; often Here|Focus on current room. */

typedef struct glk_mapnode_struct {
    glui32 id;                 /* opaque room rock (terp-chosen; session-stable is enough) */
    const char *label;         /* UTF-8 display name; required (empty OK); copied on present */
    glui32 nodeflags;          /* mapnode_* */
    glk_mappoint_t origin;     /* top-left of room box */
    glui32 width, height;      /* box size in abstract map units */
} glk_mapnode_t;

Links

/* Directions */
#define mapdir_North       1
#define mapdir_NorthEast   2
#define mapdir_East        3
#define mapdir_SouthEast   4
#define mapdir_South       5
#define mapdir_SouthWest   6
#define mapdir_West        7
#define mapdir_NorthWest   8
#define mapdir_Up          9
#define mapdir_Down       10
#define mapdir_In         11
#define mapdir_Out        12

/* Ports: A link starts at one port and optionally ends at another */
typedef struct glk_mapport_struct {
    glui32 nodeid;             /* matches glk_mapnode_t.id */
    glui32 dir;                /* mapdir_* */
} glk_mapport_t;

/* null port: arrow pointing into empty space */
#define glk_mapport_null ((glk_mapport_t){ 0, 0 }) 

/* line style */
#define maplink_Solid    0
#define maplink_Dotted   1   /* for restricted connections */

typedef struct glk_maplink_struct {
    glk_mapport_t from;        /* leave this room in this direction; from.nodeid must be in nodes[] */
    glk_mapport_t to;          /* arrive in this room from this direction (or glk_mapport_null) */
    glui32 style;              /* maplink_Solid or maplink_Dotted */
    glui32 via_count;          /* bend points */
    const glk_mappoint_t *via; /* via_count points, or NULL if via_count == 0 */
} glk_maplink_t;

Example

  • The player is in the Laboratory, west of the Hall, with a dotted-line link between them
  • There’s a Laboratory exit to the south to a room that the player hasn’t seen yet, so it doesn’t appear in the node list; its to is null. It would be rendered as a line pointing south from the Laboratory into empty space.

glk_map_present_model(
    mapflag_SuggestShow,
    (glk_mapnode_t[]){
        {
            .id = 1001, .label = "Laboratory",
            .nodeflags = mapnode_Here | mapnode_Focus,
            .origin = { 0, 0, 0 }, .width = 6, .height = 4
        },
        {
            .id = 1002, .label = "Hall",
            .nodeflags = mapnode_None,
            .origin = { 8, 0, 0 }, .width = 6, .height = 4
        },
    }, 2,
    (glk_maplink_t[]){
        {
            .from = { 1001, mapdir_West },
            .to = { 1002, mapdir_East },
            .style = maplink_Dotted,
            .via_count = 1, .via = (glk_mappoint_t[]){ { -2, 2, 0 } }
        },
        {
            .from = { 1001, mapdir_South },
            .to = glk_mapport_null,
            .style = maplink_Solid,
            .via_count = 0, .via = NULL
        },
    }, 2
);

Responsibilities of the Glk runner application

The Glk runner would have to convert the model into an image. (I recommend generating an SVG!)

  • As in my earlier proposal, the map would appear outside the top-level Glk window (i.e. not in a “Glk window” pane at all). If the Glk application decides to show the map in a window pane, the top-level Glk window would be resized (firing resize events).
  • The Glk application controls how to draw the rooms (room label fonts, room shapes [rectangle? rounded rectangle?], colors) and what the “you are here” indicator is (a symbol? a modified background color?)
  • Allow the user to pan/zoom the map
  • When the Glk application presents the map, zoom/pan to fit the selected “focus” nodes in the map

But what if the game wants to control more stuff?

  • Room shapes: octagons, triangles, ovals, rounding corners, etc.
  • Room border styles (dotted, wavy, colors, no border)
  • Line thickness
  • Fonts
  • Putting more text in the room (subtitles, object lists)
  • “You are here” symbol appearance (icon vs. color)
  • Link port labels (“trapdoor”)
  • Other overlays:
    • @ShardWorkX wanted to put little dots representing various NPCs on the map
    • Having several room colors, to depict different types of rooms (does the game decide? What about if the user prefers a dark mode UI vs. light mode?)
    • Putting rooms into containers (with background colors, titles)
    • Arbitrary non-room text on the map

Every individual thing on this list would require more API. :grimacing:

WDYT? Is this better?

Personally I think that proposed API is too specific to be feasible.