Files
EVEngine/docs/dev/top-down-level-format.md
T

2.6 KiB

Top-down level documents

Level editing uses eve::level_editing::LevelDocument as its format-neutral working model. A document contains an ordered stack of tile and object layers. It supports orthogonal, isometric, staggered, and hexagonal maps; coordinates and object sizes are world pixels, while tile layers are dense row-major GID grids where 0 is empty.

Native JSON format

Native files use .level.json or .evelevel. Version 1 has this shape:

{
  "format": "eve.level",
  "version": 1,
  "orientation": "orthogonal",
  "width": 64,
  "height": 48,
  "tilewidth": 32,
  "tileheight": 32,
  "properties": { "music": "forest" },
  "layers": [
    {
      "id": "layer-1",
      "name": "Ground",
      "type": "tilelayer",
      "visible": true,
      "opacity": 1,
      "offsetx": 0,
      "offsety": 0,
      "properties": {},
      "width": 64,
      "height": 48,
      "data": [0, 0, 1]
    },
    {
      "id": "layer-2",
      "name": "Gameplay",
      "type": "objectgroup",
      "properties": {},
      "objects": [
        {
          "id": "object-3",
          "name": "Start",
          "type": "spawn",
          "x": 64,
          "y": 96,
          "width": 0,
          "height": 0,
          "rotation": 0,
          "visible": true,
          "properties": { "team": "player" }
        }
      ]
    }
  ]
}

properties are deliberately open string maps at document, layer, and object scope. Games can therefore define their own schema without forking the editor's core representation. A project should validate those values in its own importer or inspector plug-in.

Formats and extension points

LevelFormatRegistry ships with eve.level and tiled.json. Tiled JSON (.tmj/.json) is imported into the same document and can be exported again. Tile layers, object layers, visibility, opacity, offsets, object transforms, and custom properties are mapped directly.

To support another source, derive from LevelFormat, implement id, extensions, canRead, read, and write, then transfer ownership with registerFormat(std::unique_ptr<LevelFormat>). Replacing a registered ID intentionally overrides that format, allowing a game to customize even the built-in serializers. Editors should edit only the neutral document; format-specific logic belongs in these adapters.

LevelFormatRegistry formats;
formats.registerFormat(std::make_unique<MyWorldFormat>());
auto level = formats.load("world.custom");
formats.save("world.level.json", *level, "eve.level");

The separation keeps brush/history/UI code independent of storage and makes format conversion a normal load-edit-save workflow.