Skip to content

Map (tm.map) ​

The glue between a map provider resource and the UIs that show a world map. The pictures, calibration and style are defined once by a provider (a worldmap resource), and every resource with a map in its UI (the kit's KMap, a phone, dispatch) asks for them the same way. The UI side is in the interface kit (KMap).

SideCalls
Sharedtm.map.expandTiles(cfg)
Server (provider)tm.map.provide(fn), tm.map.setCalibration(cal, target?)
Client (UI host)tm.map.bind(options), tm.map.getConfig(), tm.map.getLook(), tm.map.setLook(look)

The contract between them has no resource names in it: the provider answers the server callback tm_lib:map:getConfig, and fires the client event tm_lib:map:calibrationUpdated when staff recalibrate.

Tile expander ​

tm.map.expandTiles(cfg) turns a tile config into the flat list of pictures every map draws. It has no natives, so it also runs in a plain Lua test. The result:

lua
{ width, height, size = width, list = { { url | fill, fade?, bleed?, x, y, w, h, minZoom? }, ... } }

Every rectangle is in atlas pixels (the space the calibration maps game coordinates into: px = x * sx + ox), so a picture can have any resolution of its own: it is stretched to its rectangle. A detailed island can sit next to a coarse overview. The list order is the draw order (later pictures are on top).

lua
tm.map.expandTiles({
    width = 8192, height = 8192,
    sets = {
        { fill = '#10304c' },                                              -- flat sea behind everything
        { url = 'https://example.com/tile%03d.png', grid = 4 },            -- 16 pictures, tile000 to tile015
        { url = 'https://example.com/isle%03d.png', x = 6000, y = 6000, w = 1000, h = 1000, cols = 2, rows = 2, first = 20, bleed = 2, minZoom = 1.5 },
    },
})
Set fieldMeaning
urlA picture address with one %d style number (%03d). The number is the picture's index. A set needs url or fill.
fillA flat color instead of pictures (no download).
fadeWith fill: 'down', 'up', 'left' or 'right', from clear to solid in that direction.
x, y, w, hThe set's rectangle in atlas pixels. Defaults: the whole atlas.
gridCut into grid x grid pictures. cols and rows set them separately. A count below 1 is 1.
firstAdded to each index in url (one naming scheme across several sets). Default 0.
skipArray of picture numbers to leave out, counted from 0 inside the set.
bleedAtlas pixels of the neighbors' picture each tile carries on its right and bottom edge, to hide seams. Ignored for fills.
minZoomOnly draw once the map is shown this large.

Each edge is rounded to a whole pixel once, so neighbors share their border exactly. The older shape { url, grid, size } (one square set) works too. Anything that is not a table gives the default 8192 pixel atlas with no pictures. A url that string.format rejects drops that set only.

Provider (server) ​

lua
tm.map.provide(function(source)
    return {
        calibration = { sx = 0.6583, sy = 0.6583, ox = 3756, oy = 5523 },
        tiles = tilesConfig,   -- a tile config (expanded for you) or an already expanded atlas
        style = 'night',       -- 'night' or 'original': the look for players who have not chosen one
    }
end)

tm.map.setCalibration({ sx = 0.66, sy = 0.66, ox = 3760, oy = 5520 })   -- everyone; pass a source for one player

setCalibration returns false and sends nothing when the calibration is not four numbers with positive scales.

UI host (client) ​

lua
tm.map.bind({
    commit = function(message, payload) SendNUIMessage({ action = message, data = payload }) end,
    calibration = 'setMapCalibration',   -- message name for { sx, sy, ox, oy } updates
    look = 'setMapLook',                 -- message name for { preset, sea, land, roads } updates
})

bind answers the UI's map:getConfig NUI callback with { calibration, tiles, style, look } (or false when no provider answers), and pushes later calibration and look changes through commit. Binding again replaces the options instead of stacking listeners.

The look ​

The look is { preset = 'original' | 'night' | 'custom', sea, land, roads } (colors are #rrggbb). It comes from, in order:

  1. The player's own look: tm.map.setLook(look). It is kept on LocalPlayer.state.tmMapLook (not replicated), so every resource sees it, and announced on the local event tm_lib:map:lookChanged. Saving it between sessions is up to the screen that edits it (a settings menu can keep it in tm.kvp and call setLook at start). setLook(nil) clears it. An invalid look is refused.
  2. The server default, from the tm_lib settings below, unless the preset is auto.
  3. Nothing: the UI then uses the provider's style.
Setting (tm_lib namespace)TypeDefault
map.presetselect: auto, original, night, customauto
map.seacolor#10304c
map.landcolor#1d2a3a
map.roadscolor#8fa8c6

Changing a setting or calling setLook reaches an open map at once through the look message. Clearing the player's look switches open maps to the server default when there is one.