Skip to content

LDtk

LDtk projects as plain data, and LDtkLevel nodes built from them.

Files: ldtk.odin, ldtk_nodes.odin.

LDtk projects (https://ldtk.io, the level editor by Deepnight): reading .ldtk files, so levels drawn in LDtk play in the engine. The nodes that build them into a scene are in ldtk_nodes.odin.

LDtk_Load reads a project (JSON format 1.x; written against 1.5.3) into plain data:

  • Worlds and levels. Without LDtk’s “Multi-worlds” option a project has one world, made from the file’s root (its levels, worldLayout, grid size); with it, every world of worlds. Levels saved as separate files (“Save levels to separate files”: .ldtkl next to the project) are read too, so a level always has its layers.
  • Layers in LDtk’s order, top-most first (draw them back to front). Every kind keeps what a game needs: IntGrid values (row-major, 0 = empty), the tiles of Tiles layers and of auto-layers (an IntGrid layer with rules has both), entities. Offsets are the total ones (definition plus instance).
  • Entities: identifier, iid, position (px: where its pivot is, in the level), size, pivot, tags, tile, color and custom fields (LDtkValue: ints, floats, bools, strings, colors, enums as their value’s name, points, tiles, entity references, and arrays of those).
  • Definitions a game uses: tilesets (image path, grid, spacing, padding, custom data, enum tags), layer definitions (IntGrid value names and colors, parallax), entity definitions, enums.

Paths in the data (tileset.path, layer.tilesetPath, level.background.path) are the .ldtk file’s folder joined with LDtk’s relative path: pass them to the resource manager as they are. Everything a project holds lives in its own arena: LDtk_Destroy frees it all.

Entity definitions keep how LDtk draws them (render mode, tile render mode, nine-slice borders, opacities, their tile), and each entity points at its definition (LDtkEntity.def).

Ignored: auto-layer rules (LDtk resolves them into tiles when it saves), editor-only settings. LDtk’s internal icon atlas (embedAtlas: LdtkIcons) has no image file: entities using it draw their icons only when the project has its own copy beside the .ldtk (LDTK_ICONS_FILE).

Example

project, problem := e.LDtk_Load("assets/world.ldtk")
if project == nil do log.errorf("Can't read the level: %s", problem)
defer e.LDtk_Destroy(project)
level := e.LDtk_FindLevel(project, "Level_0")
walls := e.LDtk_FindLayer(level, "Collisions")
if e.LDtk_IntGridAt(walls, 3, 7) == 1 do fmt.println("a wall at cell 3, 7")

LDtk levels in scenes: an LDtkLevel node (scene files: <node kind="LDtkLevel" ldtk="..." level="..."/>) builds a level of an LDtk project (ldtk.odin) under itself when the scene is built. Its top-left corner is at the node’s position; the nodes it makes aren’t in the scene file, like a component’s.

LDtkLevel (LDtkLevelNode) the node in the scene file
Level_0 (LDtkLevelView) the level: its background (color, image); getBounds
Bg_textures (LDtkTileLayer) a layer's tiles, drawn in one batch; IntGrid cells
Collisions (LDtkTileLayer) without tiles are drawn in their value's color
Entities (Node) an entity layer: one node per entity, at its pivot
Player ...
Collision (StaticBody) IntGrid cells as rectangles (intGridCollision): solid and one-way
Spikes (Area) an area value's cells (areaValues), named after the value
  • Layers are made back to front, so LDtk’s top layer draws last (on top). Every node is on the LDtkLevel node’s render layer. Hidden layers (LDtk’s eye) are made hidden.

  • Entities, by their type’s rule (<entity name make component script noScript/>, LDtkEntityRule; none: Auto), become: Auto, in order of preference, an instance of the component the rule maps (<entity name="Player" component="components/player.component.xml"/>), what code registered for it (LDtk_RegisterEntity), else an LDtkEntityNode (draws the entity’s tile if it has one from an image tileset; outlined in the editor); or what make says: Component, Code (its rectangle, warned, if nothing registered it), Rectangle, or Skip (not made). A script is attached when what was made has none: the rule’s script, else (Auto) the one named like the entity (Player); noScript="true" attaches none. The entity’s fields set the script’s exported fields with the same name (case and underscores ignored): ints, floats, bools, strings, enums (by value name), colors, points (the pixel middle of their cell). Code finds the entity’s data with LDtk_EntityOf(node).

  • Collision: with intGridCollision naming an IntGrid layer, each value is a kind (LDtk_CollisionKind): one-way (oneWayValues), an area (areaValues), solid (in collisionValues, or every other non-empty value when that’s empty), or nothing. Solid and one-way cells become one StaticBody per level (one-way shapes have oneWay), each area value an Area named after the value, their rectangles merged (greedy: runs along rows, then down) so a wall is one shape, not a shape per cell. The body and areas get the node’s collisionLayer, collisionMask, friction and restitution.

  • allLevels builds every level of the level’s world at its place in it (Free and GridVania: worldX, worldY; linear layouts: one after the other), for worlds that scroll from level to level.

  • Projects are read once per game and kept (Game_LoadLDtk): every LDtkLevel node of a file shares one, and the data the nodes point at lives as long as the game (Game_ForgetLDtk drops it, for tools whose file changed).

  • Tilesets draw with nearest filtering (pixel art) unless smoothTiles.

  • Parallax (layers with a parallax factor), as LDtk does it: with the camera’s middle at c in the level, the layer’s points p show at c * factor + scale * p (scale = 1 - factor.x with parallax scaling; without it, minus half the layer’s size times the factor). Tile layers do it as they draw; entity layers move their group node in its update (a frame behind a camera that moves later). Not in tools (built without scripts): the level shows as it was made.

  • Entities draw as LDtk draws them: a tile by its definition’s tile render mode (Cover, FitInside, Repeat, Stretch, FullSizeCropped, FullSizeUncropped, NineSlice) at its tile opacity; in tools, without a tile, the definition’s shape (Rectangle, Ellipse, Cross; hollow, fill and line opacities). LDtk’s internal icons only with a copy of them (LDTK_ICONS_FILE).

  • Entity references: LDtk_EntityRefNode / LDtk_EntityRefNodes (an EntityRef field’s node), LDtk_FindEntityByIid.

  • Culling: tile layers draw only the tiles touching the screen (its corners taken back into the layer through the draw transform and the parallax).

  • Development runs reload saved LDtk files (LDtk_WatchFiles).

Example

// Given: self: ^e.Script
// A coin in LDtk, with a "value" Int field, gets the Coin script with value set
Coin :: struct {
using script: e.Script,
value: int `export:"default=1"`,
}
// Elsewhere: the level's data
level := cast(^e.LDtkLevelNode)e.Scene_FindNode(self.scene, "Level")
for mob in e.LDtk_FindEntityNodes(level, "Mob") do log.infof("a mob at %v", mob.position)
solid := e.LDtk_IntGridAtPosition(level, "Collisions", e.Node_GetWorldPosition(self.node)) != 0
door := e.LDtk_EntityRefNode(self.node, "door") // this entity's "door" field's entity
LDtkLayerType :: enum {
IntGrid,
Entities,
Tiles,
// An IntGrid-less auto-layer: tiles made by rules from another layer's IntGrid
AutoLayer,
}

A layer’s kind

enum. Source: ldtk.odin:49

LDtkWorldLayout :: enum {
// Anywhere (worldX, worldY)
Free,
// On a grid (worldX, worldY are multiples of the world grid)
GridVania,
// In a row, in the order of the levels array
LinearHorizontal,
// In a column, in the order of the levels array
LinearVertical,
}

How a world’s levels are placed

enum. Source: ldtk.odin:58

LDtkTilesetRect :: struct {
// The tileset's uid (LDtk_FindTileset)
tileset: int,
// The rectangle in the tileset's image, in pixels: top-left corner, width, height
x, y, w, h: int,
}

A rectangle of a tileset’s image

struct. Source: ldtk.odin:70

LDtkGridPoint :: distinct [2]int

A cell of a grid: a Point field’s value

distinct type. Source: ldtk.odin:78

LDtkEntityRef :: struct {
// The iids of the entity, and of its layer, level and world (`LDtk_FindEntityByIid`)
entityIid, layerIid, levelIid, worldIid: string,
}

Where an entity is: an EntityRef field’s value

struct. Source: ldtk.odin:81

LDtkValue :: union {
i64,
f64,
bool,
string,
sdl.Color,
LDtkGridPoint,
LDtkTilesetRect,
LDtkEntityRef,
[]LDtkValue,
}

A custom field’s value. nil when the field is null (no value). Enums are their value’s name. Arrays (Array<Int>…) are []LDtkValue.

union. Source: ldtk.odin:90

LDtkField :: struct {
// The field's name in LDtk
identifier: string,
// LDtk's type name: "Int", "Float", "Bool", "String", "Multilines", "Color", "Point",
// "FilePath", "Tile", "EntityRef", "LocalEnum.Item", "ExternEnum.X", "Array<Int>"...
type: string,
// Its value (nil when null; `LDtk_FieldValue` reads it as a type)
value: LDtkValue,
}

A custom field of a level or an entity

struct. Source: ldtk.odin:103

LDtkTile :: struct {
// Where it goes in the layer (its top-left corner, pixels; add the layer's offset)
px: [2]f32,
// Its top-left corner in the tileset image
src: [2]f32,
// Mirrored horizontally / vertically
flipX, flipY: bool,
// Opacity 0-1
alpha: f32,
// The tile's id in its tileset
id: int,
}

A tile of a Tiles layer or an auto-layer

struct. Source: ldtk.odin:114

LDtkEntity :: struct {
// Its definition's name ("Player")
identifier: string,
// Unique id (other entities' EntityRef fields point at it)
iid: string,
// Where its pivot is, in pixels from the level's top-left corner (without the layer offset)
px: [2]f32,
// Its grid cell
grid: [2]int,
// The pivot in its rectangle: 0-1 ({0.5, 1}: bottom middle)
pivot: [2]f32,
// Width and height in pixels
size: [2]f32,
// Its definition's tags
tags: []string,
// The tile LDtk shows it with (its definition's, or a field's); none for no tile
tile: Maybe(LDtkTilesetRect),
// The color LDtk shows it in (its smart color: a Color field's, else its definition's)
color: sdl.Color,
// Its custom fields' values
fields: []LDtkField,
// Its definition (render mode, tile mode...); nil if the project has none by its name
def: ^LDtkEntityDef,
}

An entity instance

struct. Source: ldtk.odin:128

LDtkLayer :: struct {
// The layer's name ("Collisions")
identifier: string,
// Unique id of this layer instance
iid: string,
// What it holds
type: LDtkLayerType,
// Cell size in pixels
gridSize: int,
// Width and height in cells
cells: [2]int,
// Total pixel offset (definition plus instance)
offset: [2]f32,
// Opacity 0-1
opacity: f32,
// Shown (LDtk's eye)
visible: bool,
// The tileset its tiles come from: its uid (-1 for none) and its image's path
tileset: int,
// The tileset image's path, joined to the project's folder ("" for none)
tilesetPath: string,
// IntGrid layers: cells.x * cells.y values, row by row; 0 is empty
intGrid: []i32,
// Tiles layers' tiles, and auto-layers' (IntGrid layers with rules too), back to front
tiles: []LDtkTile,
// Entities layers: its entities
entities: []LDtkEntity,
// From the layer definition: how much slower it scrolls (-1 to 1, 0 = none), and whether
// parallax also scales it
parallax: [2]f32,
// Parallax also scales the layer
parallaxScaling: bool,
}

A layer of a level

struct. Source: ldtk.odin:154

LDtkBackground :: struct {
// "" for none
path: string,
// Where the image's top-left corner goes in the level, in pixels
topLeft: [2]f32,
// Its scale ({1, 1}: its own size)
scale: [2]f32,
// The part of the image shown: x, y, width, height
crop: [4]f32,
}

A level’s background image, placed as LDtk shows it

struct. Source: ldtk.odin:189

LDtkNeighbour :: struct {
// The neighbour's iid (`LDtk_FindLevel`)
levelIid: string,
// The side: "n", "s", "e", "w", "ne"..., or "<", ">", "o"
dir: string,
}

A level touching this one in the world: its iid, and the side (n, s, e, w, ne…, or <, >, o for levels below, above, overlapping)

struct. Source: ldtk.odin:202

LDtkLevel :: struct {
// The level's name ("Level_0")
identifier: string,
// Unique id (what neighbours and entity references use)
iid: string,
// LDtk's numeric id
uid: int,
// Its top-left corner in the world (Free and GridVania layouts; -1 for linear ones)
world: [2]f32,
// Its depth in the world (levels stacked over each other)
worldDepth: int,
// Width and height in pixels
size: [2]f32,
// Its background color (the project's when it doesn't set one)
bgColor: sdl.Color,
// Its background image
background: LDtkBackground,
// The levels touching it
neighbours: []LDtkNeighbour,
// Its custom fields' values
fields: []LDtkField,
// Top-most first, as LDtk lists them
layers: []LDtkLayer,
}

A level

struct. Source: ldtk.odin:210

LDtkWorld :: struct {
// The world's name ("World" for projects without Multi-worlds)
identifier: string,
// Unique id
iid: string,
// How its levels are placed
layout: LDtkWorldLayout,
// GridVania's cell size
gridSize: [2]int,
// Its levels, in LDtk's order
levels: []LDtkLevel,
}

A world: its levels and how they’re laid out

struct. Source: ldtk.odin:236

LDtkTileset :: struct {
// Its id (what layers and tile rects point at: `LDtk_FindTileset`)
uid: int,
// Its name
identifier: string,
// The image, joined to the project's folder ("" for none, or LDtk's internal icons)
path: string,
// LDtk's own icon atlas (no image file the engine can load)
embedded: bool,
// Tile size in pixels
gridSize: int,
// Pixels between tiles
spacing: int,
// Pixels around the tiles, at the image's edges
padding: int,
// Image size in pixels, and in cells
size: [2]int,
// Image size in cells
cells: [2]int,
// Its tags
tags: []string,
// Text attached to tiles in LDtk ("custom data"), by tile id
customData: []LDtkTileData,
// Tiles tagged with an enum's values
enumTags: []LDtkEnumTag,
}

A tileset definition

struct. Source: ldtk.odin:257

LDtkTileData :: struct {
// The tile's id in the tileset
tileId: int,
// The text LDtk holds for it
data: string,
}

A tile’s custom data (a tileset’s)

struct. Source: ldtk.odin:285

LDtkEnumTag :: struct {
// The enum value's name
enumValue: string,
// The ids of the tiles tagged with it
tileIds: []int,
}

The tiles tagged with an enum value (a tileset’s)

struct. Source: ldtk.odin:293

LDtkIntGridValue :: struct {
// The value cells hold (1 and up)
value: int,
// "" when unnamed
identifier: string,
// The color LDtk shows its cells in
color: sdl.Color,
}

An IntGrid value’s name and color (a layer definition’s)

struct. Source: ldtk.odin:301

LDtkLayerDef :: struct {
// Its id
uid: int,
// The layer's name (layers of levels have the same: `LDtk_FindLayerDef`)
identifier: string,
// What its layers hold
type: LDtkLayerType,
// Cell size in pixels
gridSize: int,
// IntGrid layers: their values' names and colors
intGridValues: []LDtkIntGridValue,
// How much slower its layers scroll (-1 to 1, 0 = none)
parallax: [2]f32,
// Parallax also scales its layers
parallaxScaling: bool,
}

A layer definition

struct. Source: ldtk.odin:311

LDtkEntityRenderMode :: enum {
Rectangle,
Ellipse,
Tile,
Cross,
}

How LDtk draws an entity: its definition’s shape, or its tile

enum. Source: ldtk.odin:329

LDtkTileRenderMode :: enum {
// Scaled to cover it, keeping its shape, cropped around the pivot
Cover,
// Scaled to fit inside, keeping its shape, at the pivot
FitInside,
// Repeated at its size from the top-left corner
Repeat,
// Scaled to the rectangle
Stretch,
// At its size, cropped to the rectangle around the pivot
FullSizeCropped,
// At its size, at the pivot, not cropped
FullSizeUncropped,
// Corners at their size, edges and middle repeated (nineSlice borders)
NineSlice,
}

How an entity’s tile fills its rectangle (LDtk’s tileRenderMode)

enum. Source: ldtk.odin:337

LDtkEntityDef :: struct {
// Its id
uid: int,
// The entity's name ("Player")
identifier: string,
// Default width and height in pixels
size: [2]f32,
// The pivot in its rectangle: 0-1
pivot: [2]f32,
// The color LDtk draws it in
color: sdl.Color,
// Its tags
tags: []string,
// How LDtk draws it
renderMode: LDtkEntityRenderMode,
// How its tile fills its rectangle
tileRenderMode: LDtkTileRenderMode,
// Top, right, bottom, left (NineSlice)
nineSlice: [4]f32,
// Shapes: no fill; fill and line opacities. Tiles: opacity
hollow: bool,
// Opacities 0-1: the shape's fill (default 0.08) and line (default 1), and the tile (default 1)
fillOpacity, lineOpacity, tileOpacity: f32,
// The definition's tile (entities may show another, from a field)
tile: Maybe(LDtkTilesetRect),
}

An entity definition

struct. Source: ldtk.odin:355

LDtkEnumDef :: struct {
// Its id
uid: int,
// The enum's name
identifier: string,
// Its values' names, in order
values: []string,
}

An enum definition: its values, in order

struct. Source: ldtk.odin:383

LDtkProject :: struct {
// Holds everything below (`LDtk_Destroy` frees it)
arena: MemoryArena,
// The .ldtk file, and its folder (paths in the data are joined to it)
path, dir: string,
// The LDtk version that saved it ("1.5.3")
jsonVersion: string,
// The levels' default background color
bgColor: sdl.Color,
// At least one (projects without Multi-worlds have one, from the file's root)
worlds: []LDtkWorld,
// Tileset definitions
tilesets: []LDtkTileset,
// Layer definitions
layers: []LDtkLayerDef,
// Entity definitions
entities: []LDtkEntityDef,
// Enum definitions, external ones included
enums: []LDtkEnumDef,
}

An LDtk project. Made by LDtk_Load; everything in it is in its arena (LDtk_Destroy).

struct. Source: ldtk.odin:395

LDtkLevelSettings :: struct {
// The .ldtk file, relative to the project folder
file: string,
// The level's identifier (or iid); "" for the first level
level: string,
// Every level of the level's world, each at its place in the world
allLevels: bool,
// The IntGrid layer whose cells become collision ("" for none), and which values (none: every
// non-empty cell)
collisionLayer: string,
// `collisionValues="1 3"`: the IntGrid values that are solid (empty: every non-empty cell
// that isn't one-way or an area; "0" alone: none, 0 being LDtk's empty cell)
collisionValues: [dynamic]int,
// `oneWayValues="2"`: values that are one-way platforms (bodies pass up through them)
oneWayValues: [dynamic]int,
// `areaValues="4"`: values that become Areas (triggers: onCollisionEnter / Exit), one per value,
// named after the value in LDtk ("Spikes")
areaValues: [dynamic]int,
// Make nodes for entities
spawnEntities: bool,
// Draw the level's background color and image
background: bool,
// Linear filtering for tilesets (off: nearest, for pixel art)
smoothTiles: bool,
// What entities become and run, by entity identifier (`<entity>` elements; types without one
// follow the Auto rules)
entityComponents: [dynamic]LDtkEntityComponent,
}

What a scene file’s LDtkLevel node says (SceneNodeData.ldtk)

struct. Source: ldtk_nodes.odin:85

LDtkEntityComponent :: struct {
// The entity's identifier in LDtk ("Player")
entity: string,
// The component file, relative to the project folder (`make` Component; Auto with one too)
component: string,
// What its entities become (Auto: the component when set, else code, else the rectangle)
make: LDtkEntityMake,
// The script they run: "" for the one named like the entity (when what's made has none)
script: string,
// No script, not even the one named like the entity
noScript: bool,
}

What an entity type becomes and which script its entities run (&lt;entity name="Coin" component=".." make=".." script=".." noScript="true"/>, attributes only when not the default). The first files had only a component: that still means the component.

struct. Source: ldtk_nodes.odin:119

LDtkEntityRule :: LDtkEntityComponent

Another name for LDtkEntityComponent: what an entity type becomes and runs

type. Source: ldtk_nodes.odin:133

LDtkEntityMake :: enum {
// The mapped component, else what code registered (`LDtk_RegisterEntity`), else the rectangle
Auto,
// The rule's component
Component,
// What code registered (`LDtk_RegisterEntity`); the rectangle when nothing did
Code,
// Its rectangle (and tile), whatever is mapped or registered
Rectangle,
// Nothing: entities of the type aren't made
Skip,
}

What an entity type’s nodes are

enum. Source: ldtk_nodes.odin:136

LDtkCollisionKind :: enum {
None,
// Solid collision (a StaticBody's rectangles)
Solid,
// One-way platforms: solid from above only (`CollisionShape.oneWay`)
One_Way,
// An Area: reports bodies entering and leaving (triggers)
Area,
}

What an IntGrid value of the collision layer becomes

enum. Source: ldtk_nodes.odin:150

LDtkLevelNode :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// The project (the game's: Game_LoadLDtk), nil when it couldn't be read
project: ^LDtkProject,
// One per level built
views: [dynamic]^LDtkLevelView,
// Every entity's node, with its data
spawned: [dynamic]LDtkSpawned,
// Layers with a parallax factor move with the camera (not in tools that build without
// scripts, like the editor: the level shows as it was made)
parallax: bool,
// What it was built with (in its arena): for reloading when the file changes
settings: LDtkLevelSettings,
// The physics settings of the collision bodies it makes
collision: LDtkCollisionBody,
// An entity's node was made (when the level is built, and for entities a reload added)
onEntitySpawned: EventSubject `event:"An entity's node was made (built, or added by a hot reload; data: the node)" data:"node := cast(^e.Node)data.(rawptr)"`,
// Its LDtk file was saved and the level updated (hot reload, dev runs)
onReloaded: EventSubject `event:"Its LDtk file changed and the level was updated (dev runs: Play from the editor)"`,
}

The LDtkLevel node: what it built, for code that looks at the level.

struct. Source: ldtk_nodes.odin:193

LDtkCollisionBody :: struct {
// The layers it's on and the layers it collides with (bits: layer n is bit n-1)
layer, mask: u64,
// Its surface: friction and bounciness
friction, restitution: f32,
}

The StaticBody an IntGrid layer becomes: the scene file node’s physics attributes

struct. Source: ldtk_nodes.odin:216

LDtkLevelView :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// The level's data (the project's)
level: ^LDtkLevel,
// Its background image (nil for none)
background: ^sdl.Texture,
// Draw the background color and image (the scene file's `background`)
drawBackground: bool,
}

A level, as built: draws its background

struct. Source: ldtk_nodes.odin:224

LDtkTileLayer :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// The layer's data (the project's)
data: ^LDtkLayer,
// The tileset's image (nil for an IntGrid layer drawn in colors)
texture: ^sdl.Texture,
// The tileset's tile size
tileSize: f32,
// IntGrid cells' colors, by value (layers without tiles)
colors: map[int]sdl.Color,
// Move with the camera by the layer's parallax factor
parallax: bool,
// SDL's software renderer: the whole layer drawn once into a texture of its size, then copied
// each frame (`ldtkTileLayerDraw`); nil until then, and again after a reload
cache: ^sdl.Texture,
// The renderer it was made with
cacheRenderer: ^sdl.Renderer,
}

A layer’s tiles (or an IntGrid layer’s colored cells, when it has no tiles), drawn in one batch

struct. Source: ldtk_nodes.odin:236

LDtkEntityLayer :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// The layer's data (the project's)
data: ^LDtkLayer,
}

An entity layer’s group: moves with the camera when the layer has a parallax factor

struct. Source: ldtk_nodes.odin:257

LDtkEntityNode :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// The entity's data (the project's)
entity: ^LDtkEntity,
// Its tile's tileset image (nil: no tile, or one from LDtk's internal icons without a copy)
texture: ^sdl.Texture,
// Its tile's rectangle in the image
src: sdl.FRect,
// Draw LDtk's shape when it has no tile (tools that build without scripts: the editor)
outline: bool,
}

An entity that no component or spawner took: its rectangle, and its tile if it has one

struct. Source: ldtk_nodes.odin:265

LDtkSpawned :: struct {
// The node made for it (it leaves the list when the node is removed)
node: ^Node,
// The entity's data (the project's)
entity: ^LDtkEntity,
// The level it's in
level: ^LDtkLevel,
}

An entity’s node and data

struct. Source: ldtk_nodes.odin:279

LDtkEntitySpawner :: #type proc(scene: ^Scene, entity: ^LDtkEntity) -> ^Node

Makes a node for an entity: called with the entity’s data, it returns the node (the engine positions and names it, and adds it to the level). Registered with LDtk_RegisterEntity.

type. Source: ldtk_nodes.odin:292

LDtkPlacedLevel :: struct {
level: ^LDtkLevel,
position: [2]f32,
}

A level a level node builds, and where in the node it goes

struct. Source: ldtk_nodes.odin:648

LDtk_Load :: proc(path: string) -> (^LDtkProject, string)

Reads an LDtk project file (and its separate level files, if it has them).

Inputs

  • path: The .ldtk file (read through the platform layer: the data pack, the browser’s files)

Returns

  • The project (LDtk_Destroy frees it), or nil and what went wrong (temp allocated)

Example

project, problem := e.LDtk_Load("assets/world.ldtk")
if project == nil do log.errorf("LDtk: %s", problem)
defer e.LDtk_Destroy(project)

procedure. Source: ldtk.odin:430

LDtk_Parse :: proc(text, path: string) -> (^LDtkProject, string)

Parses an LDtk project’s JSON. Separate level files are read relative to path.

Inputs

  • text: The .ldtk file’s contents
  • path: Where it is (the folder paths in it are relative to)

Returns

  • The project (LDtk_Destroy frees it), or nil and what went wrong (temp allocated)

procedure. Source: ldtk.odin:446

LDtk_Destroy :: proc(project: ^LDtkProject)

Frees a project and everything in it. Data taken from it (levels, entities, strings) is gone too.

Inputs

  • project: The project (nil does nothing)

procedure. Source: ldtk.odin:527

LDtk_FindLevel :: proc(project: ^LDtkProject, name: string) -> ^LDtkLevel

A level by identifier (“Level_0”) or iid, in any world.

Inputs

  • project: The project (nil gives nil)
  • name: The level’s identifier or iid

Returns

  • The level, or nil

procedure. Source: ldtk.odin:543

LDtk_FindLayer :: proc(level: ^LDtkLevel, identifier: string) -> ^LDtkLayer

A level’s layer by identifier.

Inputs

  • level: The level (nil gives nil)
  • identifier: The layer’s name (“Collisions”)

Returns

  • The layer, or nil

procedure. Source: ldtk.odin:561

LDtk_FindTileset :: proc(project: ^LDtkProject, uid: int) -> ^LDtkTileset

A tileset by uid (what LDtkLayer.tileset and LDtkTilesetRect.tileset hold).

Inputs

  • project: The project (nil gives nil)
  • uid: The tileset’s uid

Returns

  • The tileset, or nil

procedure. Source: ldtk.odin:577

LDtk_FindLayerDef :: proc(project: ^LDtkProject, identifier: string) -> ^LDtkLayerDef

A layer definition by identifier.

Inputs

  • project: The project (nil gives nil)
  • identifier: The layer’s name

Returns

  • The definition, or nil

procedure. Source: ldtk.odin:593

LDtk_IntGridAt :: proc(layer: ^LDtkLayer, cx, cy: int) -> int

An IntGrid layer’s value at a cell: 0 when empty, outside the layer, or not an IntGrid layer.

Inputs

  • layer: The layer (nil gives 0)
  • cx: The cell’s column, from the left
  • cy: The cell’s row, from the top

Returns

  • The cell’s value

Example

// Given: level: ^e.LDtkLevel, x: f32, y: f32
walls := e.LDtk_FindLayer(level, "Collisions")
cell := [2]int{int(x) / walls.gridSize, int(y) / walls.gridSize}
solid := e.LDtk_IntGridAt(walls, cell.x, cell.y) != 0

procedure. Source: ldtk.odin:616

LDtk_FindField :: proc(fields: []LDtkField, identifier: string) -> ^LDtkField

A custom field by identifier (of a level or an entity).

Inputs

  • fields: The level’s or entity’s fields
  • identifier: The field’s name

Returns

  • The field, or nil

procedure. Source: ldtk.odin:633

LDtk_FieldValue :: proc(fields: []LDtkField, identifier: string, default: $T) -> T

A custom field’s value as a type, or the default when it’s missing, null or another type. Ints read as floats too, and as int (which reads Int fields).

Inputs

  • fields: The level’s or entity’s fields
  • identifier: The field’s name
  • default: What to return when the field has no value of type T (it also sets T)

Returns

  • The value, or default

Example

// Given: entity: ^e.LDtkEntity
hp := e.LDtk_FieldValue(entity.fields, "hp", i64(3))
name := e.LDtk_FieldValue(entity.fields, "name", "nobody")

procedure. Source: ldtk.odin:655

LDtk_ValueText :: proc(value: LDtkValue, gridSize: int) -> (string, bool)

A field’s value in the text form scene files and script fields use: “12”, “0.5”, “true”, a string, “#rrggbbaa”, a point as the pixel position of its cell’s middle (“x,y”, from gridSize). Arrays, tiles and entity references have none.

Inputs

  • value: The field’s value
  • gridSize: The layer’s cell size in pixels (for points)

Returns

  • The text (temp allocated), and whether the value has a text form

procedure. Source: ldtk.odin:683

LDtk_Dependencies :: proc(text: string) -> []string

The files a project needs besides the .ldtk itself: its separate level files, tileset images and level backgrounds, relative to the .ldtk’s folder (temp allocated). Exports copy them with it.

Inputs

  • text: The .ldtk file’s contents

Returns

  • The relative paths, each once (empty when the text isn’t valid JSON)

procedure. Source: ldtk.odin:708

LDtkLevelSettings_Destroy :: proc(settings: ^LDtkLevelSettings, allocator := context.allocator)

Frees what LDtkLevelSettings own and zeroes them. SceneData_Destroy calls it for every node.

Inputs

  • settings: The settings
  • allocator: The allocator their strings and lists were made with

procedure. Source: ldtk_nodes.odin:173

LDtk_RegisterEntity :: proc "contextless" (identifier: string, spawn: LDtkEntitySpawner)

Registers what an entity becomes in code, for entities no component is mapped to. Callable from @(init) (contextless, like Script_Register). Registering an identifier again replaces the first.

Inputs

  • identifier: The entity’s identifier in LDtk (“Torch”)
  • spawn: Makes its node

Example

package scripts
import e "engine:."
@(init)
registerSpawners :: proc "contextless" () {
e.LDtk_RegisterEntity("Torch", proc(scene: ^e.Scene, entity: ^e.LDtkEntity) -> ^e.Node {
return e.CreateParticleEmitter(scene, e.DEFAULT_PARTICLE_SETTINGS)
})
}

procedure. Source: ldtk_nodes.odin:314

Game_LoadLDtk :: proc(game: ^Game, path: string, projectDir := "") -> ^LDtkProject

A project, read the first time it’s asked for and kept for the game’s life (nil, logged, when it can’t be read; asked again, it stays nil until Game_ForgetLDtk).

Inputs

  • game: The game that keeps it
  • path: The .ldtk file, relative to the project folder
  • projectDir: The project folder (“” for the game’s, Game.projectDir; tools that build scenes pass theirs to Scene_Build)

Returns

  • The project (the game’s: don’t free it), or nil

procedure. Source: ldtk_nodes.odin:379

Game_ForgetLDtk :: proc(game: ^Game, path: string = "")

Drops a project the game read, so it’s read again: after the file changed. Nodes still pointing into it must be gone first (rebuild their scenes).

Inputs

  • game: The game
  • path: The .ldtk file as given to Game_LoadLDtk (“” for all, the projects replaced by reloads too)

procedure. Source: ldtk_nodes.odin:402

LDtk_FindEntityNodes :: proc(root: ^LDtkLevelNode, identifier: string) -> []^Node

The nodes made for an entity identifier, in level and layer order (temp allocated).

Inputs

  • root: The LDtkLevel node (nil gives none)
  • identifier: The entity’s identifier (“Mob”)

Returns

  • The nodes

procedure. Source: ldtk_nodes.odin:433

LDtk_EntityOf :: proc(node: ^Node) -> ^LDtkEntity

The LDtk data of an entity’s node (or of a node under it), or nil when it isn’t one.

Inputs

  • node: The entity’s node, or a node under it

Returns

  • The entity’s data (the project’s), or nil

Example

// Given: self: ^e.Script
entity := e.LDtk_EntityOf(self.node)
speed := e.LDtk_FieldValue(entity.fields, "speed", f64(100))

procedure. Source: ldtk_nodes.odin:454

LDtk_IntGridAtPosition :: proc(root: ^LDtkLevelNode, layer: string, position: [2]f32) -> int

An IntGrid layer’s value under a world position: 0 when empty, outside the levels, or when no level has that layer.

Inputs

  • root: The LDtkLevel node (nil gives 0)
  • layer: The IntGrid layer’s identifier
  • position: The world position, in game coordinates

Returns

  • The cell’s value

Example

// Given: self: ^e.Script, level: ^e.LDtkLevelNode
onLadder := e.LDtk_IntGridAtPosition(level, "Collisions", e.Node_GetWorldPosition(self.node)) == 3

procedure. Source: ldtk_nodes.odin:481

LDtk_PlacedLevels :: proc(project: ^LDtkProject, settings: LDtkLevelSettings, allocator := context.allocator) -> ([]LDtkPlacedLevel, bool)

The levels a level node builds, where each goes in the node: the chosen one (settings.level, by identifier or iid; “” for the first) at the node’s origin, or with allLevels every level of its world, at its world position (laid end to end in linear worlds).

Inputs

  • project: The parsed project
  • settings: The level node’s settings
  • allocator: For the list

Returns

  • The levels, and false when the chosen level isn’t in the project

procedure. Source: ldtk_nodes.odin:666

LDtk_EntityComponent :: proc(settings: ^LDtkLevelSettings, identifier: string) -> string

How a level node builds an entity (spawnEntity), in order: the component mapped to its type (LDtkLevelSettings.entityComponents), else code registered for it (LDtk_RegisterEntity), else its rectangle (and tile); then, if what was built has no script, the script named like the type (Script_Find(identifier)). Its LDtk fields set the script fields whose names match (LDtk_FieldMatches). Editors describe it with these procedures.

Inputs

  • settings: The level node’s settings
  • identifier: The entity type (“Coin”)

Returns

  • The component its type is mapped to, or “” for none

procedure. Source: ldtk_nodes.odin:888

LDtk_EntityRule :: proc(settings: ^LDtkLevelSettings, identifier: string) -> LDtkEntityRule

An entity type’s rule: what its entities become and run (zero, Auto everything, when the node has none for it).

Inputs

  • settings: The level node’s settings
  • identifier: The entity type

Returns

  • The rule (its strings are the settings’)

procedure. Source: ldtk_nodes.odin:905

LDtk_EntityMade :: proc(settings: ^LDtkLevelSettings, identifier: string) -> LDtkEntityMake

What an entity type’s nodes are, its rule resolved: Auto becomes the mapped component, else Code when code registered it (LDtk_HasSpawner: never in an editor), else Rectangle; a Component rule without a component is a Rectangle.

Inputs

  • settings: The level node’s settings
  • identifier: The entity type

Returns

  • Component, Code, Rectangle or Skip

procedure. Source: ldtk_nodes.odin:922

LDtk_EntityScript :: proc(settings: ^LDtkLevelSettings, identifier: string, builtHasScript: bool) -> (name: string, chosen: bool)

The script an entity type’s nodes run: the rule’s, else (Auto) the one named like the type, but only when what’s made has no script of its own (a component’s root may).

Inputs

  • settings: The level node’s settings
  • identifier: The entity type
  • builtHasScript: What’s made has a script already

Returns

  • The script’s name (“” for none) and whether the rule names it (false: named like the entity)

procedure. Source: ldtk_nodes.odin:949

LDtk_CollisionKind :: proc(settings: ^LDtkLevelSettings, value: int) -> LDtkCollisionKind

What an IntGrid value of the collision layer becomes: one-way or an area when listed so, solid when listed solid, or (no solid values listed) every other non-empty value.

Inputs

  • settings: The level node’s settings
  • value: The cell’s value

Returns

  • Its kind (None for 0)

procedure. Source: ldtk_nodes.odin:967

LDtk_HasSpawner :: proc(identifier: string) -> bool

Whether code registered a spawner for an entity type (LDtk_RegisterEntity): the game’s own build has them; an editor (without the game’s scripts) never does.

Inputs

  • identifier: The entity type

Returns

  • true when a spawner makes its nodes

procedure. Source: ldtk_nodes.odin:985

LDtk_IsCollisionValue :: proc(values: []int, value: int) -> bool

Whether an IntGrid value is solid for a level node’s collision: any value but 0 when the node lists none, else one it lists.

Inputs

  • values: The node’s collision values (LDtkLevelSettings.collisionValues)
  • value: The cell’s value

Returns

  • true when the cell becomes collision

procedure. Source: ldtk_nodes.odin:1000

LDtk_FieldMatches :: proc(scriptField, ldtkField: string) -> bool

Whether a script field takes an LDtk field’s value: their names match, ignoring case and underscores (“maxSpeed” matches LDtk’s “max_speed”, “MaxSpeed” and “Max_speed”).

Inputs

  • scriptField: The script field’s name
  • ldtkField: The LDtk field’s identifier

Returns

  • true when they match

procedure. Source: ldtk_nodes.odin:1018

LDtk_EntityRefNode :: proc(node: ^Node, field: string) -> ^Node

The node of the entity an EntityRef field points at (field on the entity whose node, or a node under it, is node), when that entity was built under the same LDtkLevel node; nil otherwise (no such field, null, or an entity in a level that wasn’t built).

Inputs

  • node: The entity’s node, or a node under it
  • field: The EntityRef field’s identifier

Returns

  • The referenced entity’s node, or nil

Example

// Given: self: ^e.Script
// A lever whose "door" field points at a door
door := e.LDtk_EntityRefNode(self.node, "door")
if door != nil do door.visible = false

procedure. Source: ldtk_nodes.odin:1509

LDtk_EntityRefNodes :: proc(node: ^Node, field: string) -> []^Node

The nodes an Array<EntityRef> field points at (temp allocated; entities that weren’t built are left out).

Inputs

  • node: The entity’s node, or a node under it
  • field: The field’s identifier

Returns

  • The nodes (none when the node isn’t an entity’s or the field isn’t an EntityRef array)

procedure. Source: ldtk_nodes.odin:1530

LDtk_FindEntityByIid :: proc(root: ^LDtkLevelNode, iid: string) -> ^Node

An entity’s node by its iid (what EntityRef fields hold).

Inputs

  • root: The LDtkLevel node (nil gives nil)
  • iid: The entity’s iid

Returns

  • The node, or nil when no entity built under root has that iid

procedure. Source: ldtk_nodes.odin:1556

LDTK_ICONS_FILE :: "finalbossblues-icons_full_16.png"

The image of LDtk’s internal icons (tilesets with embedAtlas: LdtkIcons): FinalBossBlues’ Icons pack (finalbossblues.itch.io/icons), which LDtk carries but which the engine can’t ship. Put your own copy beside the .ldtk file under this name and entities using it draw their icons.

constant. Source: ldtk.odin:254

DEFAULT_LDTK_LEVEL_SETTINGS :: LDtkLevelSettings{
spawnEntities = true,
background = true,
}

The settings an LDtkLevel node starts with

constant. Source: ldtk_nodes.odin:161

LDTK_WATCH_SECONDS :: 0.5

How often development runs look at the LDtk files (seconds)

constant. Source: ldtk_nodes.odin:364