Builder Manual Play
Builder Manual / Level Design

TOML / Level Design

Level Design

TOML schema, minimum level template, and authoring rules for worlds.

Level Design — TOML Reference


Super Mango levels are defined as TOML files inside the levels/ directory. The game engine loads them at startup via level_loader.c; the level editor reads and writes the same format. All positions are in logical pixels (400×300 coordinate space).

TOML rule: All scalar key-value pairs must appear before any [array tables] in the file. The parser (tomlc17) will fail silently or error if arrays precede scalars.


Quick Start

# Run a level file directly
make run-level LEVEL=levels/00_sandbox_01.toml

# Open a level in the visual editor
make run-editor
# Then File → Open inside the editor

Top-Level Scalars

Every level file begins with these key-value pairs. All are required unless marked optional.

name        = "Creator's Playground"
description = """
Optional multi-line description of the level.
"""
generated_by  = "Author Name"           # optional credit string
screen_count  = 4                       # world width = screen_count × 400 px
player_start_x = 79.0                   # player spawn x in logical pixels
player_start_y = 124.5                  # player spawn y in logical pixels
music_path    = "assets/sounds/levels/water.wav"
music_volume  = 13                      # SDL_mixer volume 0–128
floor_tile_path = "assets/sprites/levels/grass_tileset.png"
initial_hearts  = 3                     # starting hit points
initial_lives   = 3                     # starting lives
score_per_life  = 1000                  # score at which a bonus life is awarded
coin_score      = 100                   # points per coin collected
floor_gaps      = [0, 192, 560, 928]    # world-space x positions of sea gaps
FieldTypeDescription
screen_countintNumber of 400px-wide screens. 4 → world is 1600px wide.
player_start_x/yfloatSpawn position in logical pixels.
music_pathstringPath to WAV/OGG, relative to repo root.
music_volumeintSDL_mixer channel volume: 0 (silent) – 128 (full).
floor_tile_pathstringPNG used to tile the ground. Per-level theming.
initial_heartsintStarting hit points for the level.
initial_livesintStarting lives for the level.
score_per_lifeintScore threshold spacing for bonus lives.
coin_scoreintPoints awarded for each collected coin.
floor_gapsint arraySea gap x-positions. Blue/fire flames are placed manually; flame x values normally match these openings.

Rails

Rails define closed or open tracks that spike blocks and float platforms ride on.

[rails]
layout  = "RECT"    # "RECT" = rectangular loop, "HORIZ" = horizontal line
x       = 444       # top-left tile x in logical pixels
y       = 35        # top-left tile y in logical pixels
w       = 10        # width in tiles (for RECT)
h       = 6         # height in tiles (for RECT)
end_cap = 0         # 0 = open end (rider detaches), 1 = capped end (rider bounces)

Rail layouts:

layoutShapeTypical use
RECTClosed rectangular loopContinuous-circuit spike blocks / float platforms
HORIZOpen horizontal lineSpike block that bounces left–right; w = length in tiles

The end_cap flag only applies to open (HORIZ) rails. With end_cap = 1 the rider bounces back; with end_cap = 0 it falls off the far end as a projectile.


Platforms

Ground-level pillar columns. The player can land on the top surface only.

[platforms]
x           = 80.0   # left edge of the pillar in logical pixels
tile_height = 2      # pillar height in 48px tiles (1 = 48px, 2 = 96px, 3 = 144px)
tile_width  = 1      # pillar width in 48px tiles (usually 1)

Pillar top Y: FLOOR_Y − (tile_height × TILE_SIZE) = 252 − (tile_height × 48).

tile_heightTop surface YNotes
1204Short hop
2156Standard — medium bouncepads clear this
3108Tall — requires high bouncepad or triple-jump

Coins

[coins]
x = 46.0    # centre-ish x in logical pixels (render width = 16px)
y = 236.0   # top edge y in logical pixels

Each coin is worth coin_score points (default 100). Every score_per_life points grants a bonus life.


Stars

[star_yellows]
x = 272.0
y = 108.0

[star_greens]
x = 500.0
y = 80.0

[star_reds]
x = 800.0
y = 100.0

Each star variant restores 1 heart on pickup. All are 16×16 px display size.


Last Star

[last_star]
x = 1492.0
y = 100.0
next_phase = "levels/01_lugio_01.toml"  # optional level loaded after completion

Single-instance. Triggers the level-complete event when collected. Displayed at 24×24 px. next_phase is serialized inside [last_star] because phase progression is tied to collecting the end-of-level star.

Collecting the last star snapshots elapsed time and coin totals, then shows the level-completion summary. If next_phase is set, pressing Enter, Space, or controller Start after the summary loads the next TOML level; otherwise confirmation exits the run. Esc or controller Back exits the overlay/run without advancing.


Enemies

Spiders

Ground patrol enemy. Walks back and forth between patrol_x0 and patrol_x1.

[spiders]
x          = 600.0   # starting x in logical pixels
vx         = 50.0    # initial horizontal speed (px/s); sign sets direction
patrol_x0  = 592.0   # left patrol boundary
patrol_x1  = 750.0   # right patrol boundary
frame_index = 0      # starting animation frame (0–2)

Jumping Spiders

Variant that leaps across sea gaps. Same fields as spider.

[jumping_spiders]
x          = 130.0
vx         = 55.0
patrol_x0  = 46.0
patrol_x1  = 310.0

Birds

Slow sine-wave sky patrol. base_y is the vertical centre of the wave.

[birds]
x          = 100.0
base_y     = 60.0    # vertical centre of the sine wave in logical pixels
vx         = 45.0    # horizontal speed (px/s)
patrol_x0  = 0.0
patrol_x1  = 700.0
frame_index = 0

Faster Birds

Same schema as [birds]. Higher vx for more aggressive patrol.

[faster_birds]
x          = 600.0
base_y     = 50.0
vx         = -80.0
patrol_x0  = 300.0
patrol_x1  = 1100.0
frame_index = 0

Fish

Water-lane patrol with random upward jumps.

[fish]
x          = 700.0
vx         = 70.0
patrol_x0  = 500.0
patrol_x1  = 950.0

Faster Fish

Same schema as [fish]. Higher vx.

[faster_fish]
x          = 1100.0
vx         = 120.0
patrol_x0  = 900.0
patrol_x1  = 1400.0

Hazards

Axe Traps

Swinging or spinning axe mounted at the top of a platform pillar.

[axe_traps]
pillar_x = 256.0    # x of the platform column the axe is mounted on
y        = 0.0      # pivot y (0 = top of pillar; engine computes exact Y from pillar)
mode     = "PENDULUM"  # "PENDULUM" = sinusoidal ±60° swing | "SPIN" = full 360°
modeBehaviourPeriod
PENDULUMSwings −60° to +60° and back2 seconds per cycle
SPINContinuous clockwise rotation180°/s → one full rotation per 2 s

Circular Saws

Fast horizontal patrol with constant spin. Does not use a rail.

[circular_saws]
x          = 1350.0
y          = 0.0      # engine snaps to floor level
patrol_x0  = 1350.0
patrol_x1  = 1446.0
direction  = 1        # 1 = starts moving right, -1 = starts moving left

Patrol speed: 180 px/s. Spin speed: 720°/s. Pushes player on contact (220 px/s + −150 vy).

Spike Rows

Static strip of 16×16 spike tiles placed on the ground floor.

[spike_rows]
x     = 780.0   # left edge of the strip in logical pixels
count = 4       # number of 16×16 tiles in the row

Spike Platforms

Elevated spike hazard surface.

[spike_platforms]
x          = 370.0
y          = 200.0   # top edge in logical pixels
tile_count = 3       # number of tiles wide

Spike Blocks

Rail-riding hazard. References a rail by index (0-based order of [rails] in the file).

[spike_blocks]
rail_index = 0      # which rail to ride (0 = first [rails] entry)
t_offset   = 0.0    # starting position on the rail (0.0 = first tile)
speed      = 1.5    # traversal speed in tiles per second

Blue Flames

Erupts from a manually placed floor-gap position. x normally matches a floor_gaps entry so the flame rises from the opening. Blue and fire flame placements have separate capacities: MAX_BLUE_FLAMES and MAX_FIRE_FLAMES.

[[blue_flames]]
x = 192.0   # world-space x of the sea gap centre
[[fire_flames]]
x = 560.0   # same mechanics, fire-colored sprite

Eruption cycle: waiting (1.5 s) → rising (−550 px/s launch) → flipping (180° over 0.12 s at apex) → falling → repeat.


Surfaces

Float Platforms

Hovering surfaces with three behaviour modes.

[float_platforms]
mode       = "STATIC"   # "STATIC" | "CRUMBLE" | "RAIL"
x          = 172.0
y          = 200.0
tile_count = 4          # platform width in 16px pieces
rail_index = 0          # only used for RAIL mode
t_offset   = 0.0        # rail starting position (RAIL mode)
speed      = 0.0        # rail traversal speed in tiles/s (RAIL mode)
modeBehaviour
STATICHovers at fixed position forever
CRUMBLEFalls after player stands on it for 0.75 s
RAILTravels along the referenced rail path

Bridges

Tiled crumble walkway. Bricks fall when the player walks across.

[bridges]
x           = 1350.0
y           = 172.0
brick_count = 8       # number of 16×16 brick tiles

Bouncepads

Spring pads that launch the player vertically. Three size tiers.

[bouncepads_small]
x         = 734.0
launch_vy = -380.0    # upward impulse in px/s (negative = up)
pad_type  = "GREEN"

[bouncepads_medium]
x         = 310.0
launch_vy = -536.2
pad_type  = "WOOD"

[bouncepads_high]
x         = 1420.0
launch_vy = -700.0
pad_type  = "RED"
Arraypad_typeDefault launch_vyClears
bouncepads_smallGREEN−380.01-tile pillars
bouncepads_mediumWOOD−536.252-tile pillars
bouncepads_highRED−700.03-tile pillars

Climbable Surfaces

Vines, ladders, and ropes are placed as vertical stacks of 16px tiles.

[vines]
x          = 88.0
y          = 172.0   # top tile y in logical pixels
tile_count = 2       # height in 16px tiles

[ladders]
x          = 1552.0
y          = 0.0
tile_count = 30

[ropes]
x          = 460.0
y          = 172.0
tile_count = 1

The player can climb all three by pressing Up/Down while overlapping the surface. Vines and ropes require the player to jump into them; ladders are entered by pressing Up at the base.


Background & Foreground Layers

[[background_layers]]
path  = "assets/sprites/backgrounds/sky_blue.png"
speed = 0.0    # parallax scroll factor: 0.0 = static, 1.0 = locks to camera

[[background_layers]]
path  = "assets/sprites/backgrounds/glacial_mountains.png"
speed = 0.2

[[foreground_layers]]
path  = "assets/sprites/foregrounds/water.png"
speed = 0.0

[[fog_layers]]
path = "assets/sprites/foregrounds/fog_1.png"

[[fog_layers]]
path = "assets/sprites/foregrounds/fog_2.png"

Background layers are drawn in array order (first = furthest back). Up to 8 background layers are supported. Speed 0.0 tiles the image but does not scroll; speed 1.0 would scroll at the same rate as the camera (appears fixed in world space). Most parallax layers use 0.10.5. foreground_layers select the water/lava foreground strip texture, while fog_layers configure semi-transparent atmospheric overlays loaded by the fog system.

Available background images in assets/sprites/backgrounds/:

FileSuggested speed
sky_blue.png0.0
sky_fire.png0.0
clouds_bg.png0.1
glacial_mountains.png0.2
volcanic_mountains.png0.2
forest_leafs.png0.3
clouds_mg_1/2/3.png0.3–0.5
clouds_lonely.png0.4
castle_pillars.png0.5

Minimum Valid Level File

name            = "My Level"
screen_count    = 2
player_start_x  = 40.0
player_start_y  = 200.0
music_path      = "assets/sounds/levels/water.wav"
music_volume    = 15
floor_tile_path = "assets/sprites/levels/grass_tileset.png"
initial_hearts  = 3
initial_lives   = 3
score_per_life  = 1000
coin_score      = 100
floor_gaps      = []

[[background_layers]]
path  = "assets/sprites/backgrounds/sky_blue.png"
speed = 0.0

[last_star]
x = 760.0
y = 200.0

See levels/00_sandbox_01.toml for a full-featured reference level using every entity type.