Maps
Example: Crater Canyon uses almost every feature on this page. It has no water, because it is a desert built on Empty (the planet-less Void scene, or the flat Testzone in game versions without it; see base below). A map can also bring its own planets, sun and weather: see Planets.
How a mod map works
A map in Project Avion is a scene of the game that also carries the player spawner, water, networking and more. A mod cannot ship a scene without shipping code, so a mod map is built on top of one of the game's maps: pick a base, hide nothing or some of it, and add terrain, props, lights, sounds, effects and a different sky. Everything is data the game checks, which is also why a server can send your map to joining players safely.
"content": { "maps": ["content/maps/*.json"] },
"permissions": ["scenes"]Your map appears on the map selection screen. Players choose it like any other map.
Definition
{
"id": "lagoon",
"name": "Hidden Lagoon",
"subtitle": "Mod map · tropical",
"description": "A calm lagoon behind a reef.",
"players": "1 - 8 Players",
"color": "#2fb3a3",
"base": "Testzone",
"preview": "/preview.png",
"music": { "file": "/music/lagoon.ogg", "volume": 0.4 },
"water": { "enabled": true, "level": 2 },
"environment": {
"ambient": "#40606a",
"fog": { "color": "#9fd4d8", "density": 0.003 },
"sun": { "color": "#fff2d0", "intensity": 1.3, "pitch": 40, "yaw": 120 }
},
"spawns": [ { "position": [0, 4, 0], "heading": 90 } ],
"objects": [
{ "name": "Island", "model": "/models/island.glb", "position": [0, 0, 0], "collider": "mesh" },
{ "name": "Hut", "model": "/models/hut.obj", "position": [12, 2, -4], "rotation": [0, 35, 0], "scale": 1.5, "collider": "box" },
{ "name": "Lantern", "light": { "color": "#ffb060", "range": 12, "intensity": 2.5 }, "position": [12, 4.5, -4] },
{ "name": "Torch fire", "effect": "fire", "position": [10, 2.2, -2], "effectOptions": { "rate": 30, "size": 0.4 } },
{ "name": "Waves", "sound": "/sounds/waves.ogg", "volume": 0.7, "range": 80, "position": [-30, 1, 0] },
{ "name": "Marker", "shape": "cylinder", "position": [0, 1, 20], "scale": [0.4, 2, 0.4], "color": "#f59e0b" }
]
}| Field | Description |
|---|---|
id | The map's id. Its full id, used in scripts, is your.mod-id:id. |
name, subtitle, description, players, color | How the map card looks on the selection screen. |
base | The scene it builds on, case-insensitive: Earth, FlatPlanet, OceanPlanet (planets), Testzone (a flat proving ground), RacingMap, Empty (default) for a map that brings its own terrain, or Void (empty space with a sun, for maps with a planet system). Empty loads Void when the game has it, else the flat Testzone; a map with a "system" on Empty/Void loads Void, or FlatPlanet with its planet hidden in game versions without it (the log says so once). The retired names Islands/Space (→ Earth) and Airfields (→ Testzone) still load, with a note in the log. Any other name is reported and replaced by Empty. |
preview | A picture for the map card. |
music | A file, or { "file", "volume" }. Loops while the map is loaded. |
hideBase | Names of parts of the base map to hide. Only parts the game marks as hideable are affected. |
water | Whether the map has a sea: true, false, or { "enabled", "level" } with level the sea's height in metres (world Y). Leave it out to keep the base map's water. See Water. |
system | The map's own planets, sun and weather. See Planets. |
environment | ambient light colour, fog (color, density 0–0.2), sun (color, intensity 0–20, pitch and yaw in degrees). |
spawns | Up to 64 { "position", "heading" }. Players start at a random one. |
objects | Up to 4096 objects (below). |
Objects
Each object has a name and a position (three numbers, metres), an optional rotation (three angles in degrees) and a scale (a number or three numbers), and exactly one kind:
| Kind | Fields |
|---|---|
model | A .glb, .gltf, .obj or .fbx file, color tint, collider |
shape | cube, sphere, cylinder, capsule or plane, color, collider |
light | { "color", "range" (0.1–500), "intensity" (0–100) } point light |
sound | A .wav, .ogg or .mp3 that loops, with volume (0–1) and range (metres) |
effect | An effect preset with effectOptions |
collider is none (default), box or mesh (any capitalisation). Give walkable terrain "collider": "mesh".
Mistakes are reported
A map is checked when the mod loads. A value of the wrong kind, a number out of range, a malformed position or colour, or a shape that is not one of the five is an error that names the field (map "lagoon": objects[3].shape: "sphear" is not one of "cube", … (did you mean 'sphere'?)) and the map is not offered. An unknown field or an unknown collider kind is a warning in the mod's log (objects[2].collider: "convex" is not one of "none", "box", "mesh"; using "none"), and the map still loads.
Water
The sea is one flat, wavy surface at a single height that stretches to the horizon. Ships float on it, sails stop at it, and splashes ripple it. None of the game's maps has a flat sea: Earth and OceanPlanet have the planet's own spherical sea, and on planets (Earth, FlatPlanet, OceanPlanet, RacingMap) a flat sea cannot exist. Use water on a flat base (Testzone, Empty).
water | Result |
|---|---|
| left out | The base map's water (no flat sea on any of the game's maps) |
true | A sea at the game's usual height |
false | No flat sea |
{ "enabled": true, "level": 2 } | A sea at Y 2. Use it to flood a valley in your terrain, or to lower the sea around your island |
level may be between -100000 and 100000. Don't add water to a map built on a planet, because "down" there points at the planet's centre.
Coordinates
Positions are metres, Y up. Model files use the usual right-handed convention (Blender, glTF, OBJ, FBX), and the game is left-handed, so a model's Z is mirrored when it loads. Something at z = 10 in your modelling tool is at z = -10 in the map. Place objects by walking there in the game and reading your position (world.me() in the script editor's Console, or with /lua), or by mirroring the Z of positions you measured in your modelling tool.
Scripts on maps
Maps are data, and a script adds what changes over time:
-- "lagoon" is this mod's own map (its full id is mod.id .. ":lagoon"). onEnter also runs at once when the
-- mod starts while the map is already loaded (after /mods.reload, or joining late).
maps.onEnter("lagoon", startBirds)
maps.onLeave("lagoon", stopBirds)| Function | Description |
|---|---|
maps.current() | The map being played: a game map id like "Earth", or a mod map's full id |
maps.onEnter(id, fn) / maps.onLeave(id, fn) | fn runs on every arrival on / departure from the map (and onEnter at once if already there). id is a full id, a game map, or one of your own maps' short ids |
maps.list() | Every map, as { id, name, subtitle, mod } |
maps.spawnPoints() | The current mod map's spawns |
maps.load(id) / maps.tryLoad(id) | Server only, needs scenes: the map the host's next session starts on. The game does not change map in the middle of a session. tryLoad returns false, reason instead of raising when the game refuses. |
maps.water() | The sea now, as { enabled, level, script }. script is true while a script has set the water rather than the map |
maps.setWater(on) | Server only, needs world: adds (true) or removes (false) the sea for every player, or { enabled, level } to move it too. Players who join later get the same water. Lasts until the map changes |
maps.resetWater() | Server only, needs world: back to the water the map declares |
-- A tide: the sea rises and falls two metres every ten minutes.
local base = maps.water().level
timer.every(5, function()
local t = time.now() / 600 * 2 * math.pi
maps.setWater{ enabled = true, level = base + 2 * math.sin(t) }
end)Everything a map creates is removed when the players leave it. Objects your script created stay until you destroy them or the mod stops, so remove them in maps.onLeave.