commit 63c5ed9baf445f86b6b60de050ca403caf9d5639 Author: ludic-docs-bot Date: Thu Sep 17 22:10:03 2026 +0000 docs: regenerate site from f094acfdb5a6bd487154d7cbdd8aaae13ca6b02d diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/angle-add.html b/angle-add.html new file mode 100644 index 00000000..f5e337dc --- /dev/null +++ b/angle-add.html @@ -0,0 +1,32 @@ + + + + + +Angle.add — Ludic + + + + + + +
+
API Reference › Angle › Angle.add
+
+ method +

Angle.add

+
+ Angle.add(a, b) -> fixed +

Adds two angles and wraps the result back into range.

+ +

Example

program Demo {
+  handler Step phase Update {
+    facing = Angle.add(facing, turnRate)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-cos.html b/angle-cos.html new file mode 100644 index 00000000..573c5e03 --- /dev/null +++ b/angle-cos.html @@ -0,0 +1,32 @@ + + + + + +Angle.cos — Ludic + + + + + + +
+
API Reference › Angle › Angle.cos
+
+ method +

Angle.cos

+
+ Angle.cos(a) -> fixed +

Returns the cosine of the angle, using the deterministic fixed-point trig.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let dx = Angle.cos(facing)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-diff.html b/angle-diff.html new file mode 100644 index 00000000..e47343e7 --- /dev/null +++ b/angle-diff.html @@ -0,0 +1,32 @@ + + + + + +Angle.diff — Ludic + + + + + + +
+
API Reference › Angle › Angle.diff
+
+ method +

Angle.diff

+
+ Angle.diff(a, b) -> fixed +

Returns the shortest signed rotation from a to b, in [-pi, pi) — positive to turn one way, negative the other.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let steer = Angle.diff(facing, target)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-diff_degrees.html b/angle-diff_degrees.html new file mode 100644 index 00000000..bc39fc88 --- /dev/null +++ b/angle-diff_degrees.html @@ -0,0 +1,29 @@ + + + + + +Angle.diff_degrees — Ludic + + + + + + +
+
API Reference › Angle › Angle.diff_degrees
+
+ method +

Angle.diff_degrees

+
+ Angle.diff_degrees(a: int, b: int) -> int +

b - a wrapped into -180 .. 180, for integer-degree headings such as TopDown.aim_angle and IVec2.heading.

+ +

Example

# doc-check: skip — illustrative
+if abs(Angle.diff_degrees(a: aim, b: IVec2.heading(hero, foe))) <= 50 { hit() }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-from_degrees.html b/angle-from_degrees.html new file mode 100644 index 00000000..2ba6e7d8 --- /dev/null +++ b/angle-from_degrees.html @@ -0,0 +1,32 @@ + + + + + +Angle.from_degrees — Ludic + + + + + + +
+
API Reference › Angle › Angle.from_degrees
+
+ method +

Angle.from_degrees

+
+ Angle.from_degrees(d) -> fixed +

Converts degrees to radians and wraps the result into [-pi, pi). The natural way to author a heading a designer types in degrees.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let facing = Angle.from_degrees(90.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-lerp.html b/angle-lerp.html new file mode 100644 index 00000000..128ba119 --- /dev/null +++ b/angle-lerp.html @@ -0,0 +1,32 @@ + + + + + +Angle.lerp — Ludic + + + + + + +
+
API Reference › Angle › Angle.lerp
+
+ method +

Angle.lerp

+
+ Angle.lerp(a, b, t) -> fixed +

Interpolates from a toward b along the shortest arc (t is a fixed 0..1), so a turn never spins the long way around.

+ +

Example

program Demo {
+  handler Step phase Update {
+    facing = Angle.lerp(facing, target, 0.1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-sin.html b/angle-sin.html new file mode 100644 index 00000000..75406eb5 --- /dev/null +++ b/angle-sin.html @@ -0,0 +1,32 @@ + + + + + +Angle.sin — Ludic + + + + + + +
+
API Reference › Angle › Angle.sin
+
+ method +

Angle.sin

+
+ Angle.sin(a) -> fixed +

Returns the sine of the angle, using the deterministic fixed-point trig.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let dy = Angle.sin(facing)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-to_degrees.html b/angle-to_degrees.html new file mode 100644 index 00000000..c9b1ae25 --- /dev/null +++ b/angle-to_degrees.html @@ -0,0 +1,32 @@ + + + + + +Angle.to_degrees — Ludic + + + + + + +
+
API Reference › Angle › Angle.to_degrees
+
+ method +

Angle.to_degrees

+
+ Angle.to_degrees(a) -> fixed +

Converts a radian angle back to degrees — handy for display or debugging.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let deg = Angle.to_degrees(facing)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/angle-wrap.html b/angle-wrap.html new file mode 100644 index 00000000..b6d8268e --- /dev/null +++ b/angle-wrap.html @@ -0,0 +1,32 @@ + + + + + +Angle.wrap — Ludic + + + + + + +
+
API Reference › Angle › Angle.wrap
+
+ method +

Angle.wrap

+
+ Angle.wrap(a) -> fixed +

Normalizes any radian value into [-pi, pi), so accumulated rotation never runs away.

+ +

Example

program Demo {
+  handler Step phase Update {
+    heading = Angle.wrap(heading + spin)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-cell_x.html b/anim-cell_x.html new file mode 100644 index 00000000..22b16a8c --- /dev/null +++ b/anim-cell_x.html @@ -0,0 +1,36 @@ + + + + + +Anim.cell_x — Ludic + + + + + + +
+
API Reference › Anim › Anim.cell_x
+
+ method +

Anim.cell_x

+
+ Anim.cell_x(frame, cols, cell_w) -> int +

Turns a frame index into the left pixel of its cell on a spritesheet laid out as a grid of cols columns: (frame mod cols) * cell_w. Combine with Anim.cell_y to get the top-left source coordinate to blit from.

+

Parameters

framethe frame index (e.g. from Anim.frame)
colscolumns in the sheet
cell_wcell width in pixels
+

Example

program Demo {
+  handler Run phase Update {
+    let f  = Anim.frame(fixed(1), 12, 8)
+    let sx = Anim.cell_x(f, 4, 16)
+    let sy = Anim.cell_y(f, 4, 16)
+    print(sx)
+    print(sy)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-cell_y.html b/anim-cell_y.html new file mode 100644 index 00000000..d408d3ed --- /dev/null +++ b/anim-cell_y.html @@ -0,0 +1,34 @@ + + + + + +Anim.cell_y — Ludic + + + + + + +
+
API Reference › Anim › Anim.cell_y
+
+ method +

Anim.cell_y

+
+ Anim.cell_y(frame, cols, cell_h) -> int +

Turns a frame index into the top pixel of its cell on a spritesheet laid out as a grid of cols columns: (frame / cols) * cell_h. Pair with Anim.cell_x for the full source rectangle of the frame.

+

Parameters

framethe frame index (e.g. from Anim.frame)
colscolumns in the sheet
cell_hcell height in pixels
+

Example

program Demo {
+  handler Run phase Update {
+    let f  = Anim.frame(fixed(1), 12, 8)
+    let sy = Anim.cell_y(f, 4, 16)
+    print(sy)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-clip.html b/anim-clip.html new file mode 100644 index 00000000..1adfbe2a --- /dev/null +++ b/anim-clip.html @@ -0,0 +1,39 @@ + + + + + +Anim.clip — Ludic + + + + + + +
+
API Reference › Anim › Anim.clip
+
+ method +

Anim.clip

+
+ Anim.clip(name, frames, fps, mode) +

Registers (or updates) a spritesheet clip under a name, so gameplay refers to a motion by name — Anim.play(entity, "run") — instead of repeating its frame count and rate everywhere. The clip stores frames (cell count), fps (rate) and mode (0 loop, 1 once, 2 ping-pong). Names compare by identity (a string literal interns to one pointer), and the registry holds up to 32 clips. Registering the same name again overwrites it.

+

Parameters

namethe clip name (a string)
framesnumber of cells in the clip
fpsplayback rate in frames per second
mode0 loop, 1 once (clamp on last), 2 ping-pong
+

Example

program Demo {
+  property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0 }
+  model Hero { SpriteAnim }
+  entry {
+    Anim.clip("idle", 2, 4, 0)          # a slow 2-frame idle loop
+    Anim.clip("run",  6, 12, 0)         # a 6-frame run
+    Anim.clip("hit",  3, 15, 1)         # a one-shot hit reaction
+    spawn Hero { SpriteAnim { } }
+    Anim.play(World.query_next(World.prop_id("SpriteAnim"), 0), "run")
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-duration.html b/anim-duration.html new file mode 100644 index 00000000..7a6d8348 --- /dev/null +++ b/anim-duration.html @@ -0,0 +1,33 @@ + + + + + +Anim.duration — Ludic + + + + + + +
+
API Reference › Anim › Anim.duration
+
+ method +

Anim.duration

+
+ Anim.duration(fps, count) -> fixed +

Returns the length of one cycle of a clip in seconds as a fixed: count / fps. Handy to schedule the next event, size a progress bar, or line a tween up with an animation.

+

Parameters

fpsframes per second
countnumber of frames in the clip
+

Example

program Demo {
+  handler Run phase Update {
+    let secs = Anim.duration(12, 4)   # 4 frames / 12 fps = 0.333s
+    print(secs)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-finished.html b/anim-finished.html new file mode 100644 index 00000000..68a153e0 --- /dev/null +++ b/anim-finished.html @@ -0,0 +1,35 @@ + + + + + +Anim.finished — Ludic + + + + + + +
+
API Reference › Anim › Anim.finished
+
+ method +

Anim.finished

+
+ Anim.finished(timer, fps, count) -> bool +

Returns true once floor(timer * fps) >= count — i.e. a one-shot clip driven by Anim.once has played its final frame. Use it to despawn an effect, fire a follow-up, or switch back to an idle clip.

+

Parameters

timerelapsed seconds (a fixed)
fpsframes per second
countnumber of frames in the clip
+

Example

program Demo {
+  property Blast { timer: fixed = 0.0 }
+  model Boom { Blast }
+  handler Run phase Update {
+    Blast.timer = Blast.timer + Time.delta()
+    if Anim.finished(Blast.timer, 15, 6) { despawn (self()) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-fired.html b/anim-fired.html new file mode 100644 index 00000000..a8b9d3da --- /dev/null +++ b/anim-fired.html @@ -0,0 +1,35 @@ + + + + + +Anim.fired — Ludic + + + + + + +
+
API Reference › Anim › Anim.fired
+
+ method +

Anim.fired

+
+ Anim.fired(entity) -> bool +

Returns whether an entity's SpriteAnim clip landed on the frame armed by Anim.on_frame this tick — the read side of a frame event. It is true only on the single tick the clip first reaches that cell, so a game can poll it each frame and turn it into a gameplay action (emit an event, spawn a hitbox, play a footstep). Returns false if the entity has no SpriteAnim or no event_fired field.

+

Parameters

entitythe entity carrying SpriteAnim
+

Example

program Demo {
+  property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0, event_frame: int = 0, event_fired: int = 0 }
+  model Hero { SpriteAnim }
+  handler Step phase Update {
+    let e = World.query_next(World.prop_id("SpriteAnim"), 0)
+    if Anim.fired(e) { print(42) }      # react: footstep, hitbox, event…
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-frame.html b/anim-frame.html new file mode 100644 index 00000000..c725f0a3 --- /dev/null +++ b/anim-frame.html @@ -0,0 +1,36 @@ + + + + + +Anim.frame — Ludic + + + + + + +
+
API Reference › Anim › Anim.frame
+
+ method +

Anim.frame

+
+ Anim.frame(timer, fps, count) -> int +

Returns the current frame of a looping clip: floor(timer * fps) reduced modulo count. timer is elapsed seconds as a fixed, fps the clip's frames per second, and count the number of frames. The clip wraps forever — frame count-1 is followed by frame 0.

+

Parameters

timerelapsed seconds (a fixed)
fpsframes per second
countnumber of frames in the clip
+

Example

program Demo {
+  property Sprite { timer: fixed = 0.0 }
+  model Hero { Sprite }
+  handler Run phase Update {
+    Sprite.timer = Sprite.timer + Time.delta()
+    let cell = Anim.frame(Sprite.timer, 12, 4)   # 4-frame run at 12 fps
+    print(cell)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-on_frame.html b/anim-on_frame.html new file mode 100644 index 00000000..1eb85f0e --- /dev/null +++ b/anim-on_frame.html @@ -0,0 +1,39 @@ + + + + + +Anim.on_frame — Ludic + + + + + + +
+
API Reference › Anim › Anim.on_frame
+
+ method +

Anim.on_frame

+
+ Anim.on_frame(entity, frame) +

Arms a frame event on an entity's SpriteAnim: the engine sets the component's event_fired flag on the tick the clip first lands on frame — a footstep on the contact cell, a hitbox going live mid-swing. Gameplay reads the flag with Anim.fired in its own handler and reacts (emit its own event, spawn, play a sound). The engine detects the boundary; the game owns the reaction, so it stays inside the deterministic, no-runtime-dispatch event model. Requires the component to carry event_frame and event_fired fields.

+

Parameters

entitythe entity carrying SpriteAnim
framethe cell index that should fire the event
+

Example

program Demo {
+  property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0, event_frame: int = 0, event_fired: int = 0 }
+  model Hero { SpriteAnim }
+  entry {
+    spawn Hero { SpriteAnim { fps: 12, frames: 6, mode: 0 } }
+    let e = World.query_next(World.prop_id("SpriteAnim"), 0)
+    Anim.on_frame(e, 3)                 # fire when the run clip hits its contact frame
+    tick_fixed()
+    if Anim.fired(e) { print(1) }
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-once.html b/anim-once.html new file mode 100644 index 00000000..44ea44e9 --- /dev/null +++ b/anim-once.html @@ -0,0 +1,36 @@ + + + + + +Anim.once — Ludic + + + + + + +
+
API Reference › Anim › Anim.once
+
+ method +

Anim.once

+
+ Anim.once(timer, fps, count) -> int +

Like Anim.frame but for a one-shot clip: the frame is min(floor(timer * fps), count - 1), so once it reaches the last frame it stays there instead of wrapping. Use it for a play-once animation like an explosion or a door opening; pair it with Anim.finished to know when it is done.

+

Parameters

timerelapsed seconds (a fixed)
fpsframes per second
countnumber of frames in the clip
+

Example

program Demo {
+  property Blast { timer: fixed = 0.0 }
+  model Boom { Blast }
+  handler Run phase Update {
+    Blast.timer = Blast.timer + Time.delta()
+    let cell = Anim.once(Blast.timer, 15, 6)
+    print(cell)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-pingpong.html b/anim-pingpong.html new file mode 100644 index 00000000..e77e8385 --- /dev/null +++ b/anim-pingpong.html @@ -0,0 +1,36 @@ + + + + + +Anim.pingpong — Ludic + + + + + + +
+
API Reference › Anim › Anim.pingpong
+
+ method +

Anim.pingpong

+
+ Anim.pingpong(timer, fps, count) -> int +

Returns a frame index that plays forward to count-1, then back to 0, then forward again — a triangle wave over the frames. Ideal for a two-way idle bob or a breathing/pulsing loop where a plain wrap would snap.

+

Parameters

timerelapsed seconds (a fixed)
fpsframes per second
countnumber of frames in the clip
+

Example

program Demo {
+  property Idle { timer: fixed = 0.0 }
+  model Fish { Idle }
+  handler Run phase Update {
+    Idle.timer = Idle.timer + Time.delta()
+    let cell = Anim.pingpong(Idle.timer, 8, 4)   # 0 1 2 3 2 1 0 1 ...
+    print(cell)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/anim-play.html b/anim-play.html new file mode 100644 index 00000000..913252b1 --- /dev/null +++ b/anim-play.html @@ -0,0 +1,39 @@ + + + + + +Anim.play — Ludic + + + + + + +
+
API Reference › Anim › Anim.play
+
+ method +

Anim.play

+
+ Anim.play(entity, clip) | Anim.play(entity, fps, frames, mode) +

Starts a spritesheet clip on an entity's SpriteAnim component and rewinds it to its first cell, so gameplay swaps or replays an animation with one call instead of setting five fields by hand. Two forms: Anim.play(entity, "run") plays a named clip registered with Anim.clip; Anim.play(entity, fps, frames, mode) sets the clip directly (mode 0 loop, 1 once, 2 ping-pong). A no-op if the entity has no SpriteAnim. The engine advances the clip from there each tick.

+

Parameters

entitythe entity carrying SpriteAnim
clipa registered clip name (2-argument form), **or**
+

Example

program Demo {
+  property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0 }
+  model Hero { SpriteAnim }
+  entry {
+    Anim.clip("run", 6, 12, 0)
+    spawn Hero { SpriteAnim { } }
+    let e = World.query_next(World.prop_id("SpriteAnim"), 0)
+    Anim.play(e, "run")                 # by name
+    Anim.play(e, 8, 4, 1)               # or directly: 4 frames @ 8fps, once
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-clearcolor.html b/annot-clearcolor.html new file mode 100644 index 00000000..d90610bf --- /dev/null +++ b/annot-clearcolor.html @@ -0,0 +1,35 @@ + + + + + +"@ClearColor" — Ludic + + + + + + +
+
API Reference › Annotations › "@ClearColor"
+
+ annotation +

"@ClearColor"

+
+ "@ClearColor(0xRRGGBB) handler Draw phase Render { … }" +

@ClearColor(N) declares the framebuffer clear colour (a 0x00RRGGBB integer) so the engine owns the per-frame clear and present: at the top of the Render phase it clears the framebuffer to N, and after all Render handlers run it presents the frame — so a game's Render handler carries no Screen.clear / Screen.show boilerplate, and the colour is configured declaratively rather than written in the handler body. It is opt-in: a program with no @ClearColor is byte-for-byte identical (it clears and presents itself, or the light system owns the present). Place it on the program's Render handler (or any declaration — it is program-wide). The argument is any constant expression — a hex literal, a Color.* name, or a const from your palette.

+ +

Example

program ClearColorDemo {
+  property Tag { n: int = 0 }
+  model P { Tag }
+  @ClearColor(0x102030)
+  handler Draw phase Render {
+    Screen.fill_rectangle(10, 10, 4, 4, 0xffffff)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-computed.html b/annot-computed.html new file mode 100644 index 00000000..c0d9c0a7 --- /dev/null +++ b/annot-computed.html @@ -0,0 +1,46 @@ + + + + + +@Computed — Ludic + + + + + + +
+
API Reference › Annotations › @Computed
+
+ annotation +

@Computed

+
+ @Computed on a property field +

@Computed marks a property field as derived: instead of occupying a slot in the world, its expression is expanded inline at every site that reads it, so it is recomputed on demand and never takes storage. Use it for values that are a pure function of other fields — a total, a midpoint, a scaled amount — where storing them would risk going stale. Because a computed field has no backing storage it cannot be assigned to, and it is not something you replicate: it costs nothing on the wire and is simply recomputed on each peer. Reach for it when a value should always be consistent with its inputs and you would otherwise have to remember to recompute it by hand.

+ +

Example

program DerivedStats {
+  property Health {
+    current: int = 100,
+    maximum: int = 100,
+    @Computed missing: int = maximum - current    # never stored; expanded where read
+  }
+  model Player { Health }
+
+  handler ReportDamage phase Update {
+    for (Health) in query [Health] {
+      Health.current = 70
+      print(Health.missing)                        # 30 — recomputed from current and maximum
+    }
+  }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Health { current: 100, maximum: 100 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-enginesystem.html b/annot-enginesystem.html new file mode 100644 index 00000000..8ee7eb00 --- /dev/null +++ b/annot-enginesystem.html @@ -0,0 +1,45 @@ + + + + + +@EngineSystem — Ludic + + + + + + +
+
API Reference › Annotations › @EngineSystem
+
+ annotation +

@EngineSystem

+
+ "@EngineSystem(Component, Phase) function esys_name() { … }" +

@EngineSystem(Component, Phase) marks a function as an engine-owned system: the compiler adds it to the engine-system registry, and the frame loop calls it every Phase — after the game's own handlers — whenever the named Component is present in the program. It is the package-declarable form of the built-in engine systems (the ones that advance SpriteAnim/Motion/Light2D), so a third-party controller ships a system the same way the standard library does, with no compiler edit. The function reads and writes components by name through the reflection ABI, so it stays layout-independent and additive: a build that never declares the component compiles byte-for-byte the same. Phases are Input, FixedUpdate, Update, LateUpdate and Render.

+ +

Example

program EngineSystemDemo {
+  property Score { value: int = 0 }
+  model Player { Score }
+
+  @EngineSystem(Score, Update) function esys_score() -> void {   # runs every Update
+    let P = world_prop_id("Score")
+    let e = world_query_next(P, 0)
+    if e >= 0 { world_set(e, P, 0, world_get(e, P, 0) + 1) }     # advance by name
+  }
+
+  @OnSpawn(Player) handler Init { }
+  handler Boot phase Start { let e = world_spawn(world_model_id("Player")) }
+  handler Run phase Update {
+    let P = world_prop_id("Score")
+    print(world_get(world_query_next(P, 0), P, 0))               # grows each frame
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-export.html b/annot-export.html new file mode 100644 index 00000000..ed840888 --- /dev/null +++ b/annot-export.html @@ -0,0 +1,40 @@ + + + + + +@export — Ludic + + + + + + +
+
API Reference › Annotations › @export
+
+ annotation +

@export

+
+ @export function name(…) -> R +

@export makes a function visible outside the module as a plain native symbol, so a host program or another linked object can call it by name. Without it, functions are internal to the compiled unit; with it, the emitted symbol is externally linkable, which is how Ludic hands entry points to a runtime seam or a foreign caller. It is the outbound counterpart to extern function, which pulls a foreign symbol in. Keep exported signatures to POD scalars and pointers, since they cross a native ABI boundary where Ludic's richer types do not apply.

+ +

Example

program ScoreModule {
+  var running_total: int = 0
+
+  @export function add_points(amount: int) -> int {      # callable from a native host
+    running_total = running_total + amount
+    return running_total
+  }
+
+  entry {
+    print(add_points(10))                           # 10
+    print(add_points(5))                            # 15
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-handles.html b/annot-handles.html new file mode 100644 index 00000000..4b908c17 --- /dev/null +++ b/annot-handles.html @@ -0,0 +1,45 @@ + + + + + +@Handles — Ludic + + + + + + +
+
API Reference › Annotations › @Handles
+
+ annotation +

@Handles

+
+ @Handles(Event) handler Name { … } +

@Handles is a declarative annotation that names the event or subsystem a handler (or a whole program) is responsible for. The parser accepts and records it as metadata for analysis and tooling — it does not by itself wire up dispatch, so it documents intent rather than driving execution. When you want a handler to actually run in response to an event, use @On(Event), which registers the listener; @Handles is the companion label that makes ownership of an event legible across a modular codebase. Keep the named symbol a real event or system so the annotation stays meaningful.

+ +

Example

program CombatModule {
+  property Health { current: int = 100 }
+  model Enemy { Health }
+
+  event DamageDealt { amount: int = 0 }
+
+  @Handles(DamageDealt)                              # documents this handler's responsibility
+  @On(DamageDealt) handler ApplyDamage {             # @On does the actual dispatch wiring
+    for (Health) in query [Health, {Enemy}] {
+      Health.current = Health.current - amount
+    }
+  }
+
+  entry {
+    spawn Enemy { Health { current: 100 } }
+    emit DamageDealt(amount: 25)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-namespace.html b/annot-namespace.html new file mode 100644 index 00000000..58e18074 --- /dev/null +++ b/annot-namespace.html @@ -0,0 +1,36 @@ + + + + + +@Namespace — Ludic + + + + + + +
+
API Reference › Annotations › @Namespace
+
+ annotation +

@Namespace

+
+ "@Namespace(Name) function name_method(…) -> T { … }" +

@Namespace(Name) registers Name as a namespace a package provides, so a caller can write Name.method(…) and the compiler dispatches it to the bare function name_method(…) (the namespace lowercased, an underscore, then the method). It is the package-declarable form of the built-in Foo.* stdlib namespaces (Regex.*, Grid.*, …): a third-party package ships the same call shape without editing the compiler. The alias goes through the same generic call path the core namespaces use and preserves the target function's return type; it only applies after every built-in namespace, so it never shadows a core one. Mark each function that backs a method (the registration is idempotent — one mark is enough to open the namespace, but marking each provider documents the surface).

+ +

Example

program NamespaceDemo {
+  @Namespace(Coach) function coach_bonus() -> int { return 99 }
+  @Namespace(Coach) function coach_double(n: int) -> int { return n + n }
+
+  entry {
+    print(Coach.bonus())        # 99  — dispatches to coach_bonus
+    print(Coach.double(21))     # 42  — dispatches to coach_double
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-on.html b/annot-on.html new file mode 100644 index 00000000..35262ce0 --- /dev/null +++ b/annot-on.html @@ -0,0 +1,43 @@ + + + + + +@On — Ludic + + + + + + +
+ +
+ annotation +

@On

+
+ @On(Event) handler Name { … } +

@On(Event) turns a handler into a listener for a named event: whenever any code runs emit Event(...), this handler's body runs with the event's fields bound by name. It is how systems talk to each other without referencing one another directly — the emitter never knows who is listening, so gameplay, UI and mods can all react to the same moment independently. Several handlers may listen to one event; they run in declaration order. On a cancellable event, a listener may cancel to veto it, and the emitter sees that outcome. Related lifecycle hooks such as @OnSpawn, @OnEnable and @OnDespawn are specialized event listeners the compiler wires up for you.

+ +

Example

program Events {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Player { Health }
+
+  event Damaged { amount: int = 0 }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Health { current: 100, maximum: 100 } }
+    emit Damaged(amount: 25)
+  }
+
+  @On(Damaged)
+  handler ReportDamage {
+    print(amount)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-onattach.html b/annot-onattach.html new file mode 100644 index 00000000..68adabed --- /dev/null +++ b/annot-onattach.html @@ -0,0 +1,45 @@ + + + + + +@OnAttach — Ludic + + + + + + +
+
API Reference › Annotations › @OnAttach
+
+ annotation +

@OnAttach

+
+ @OnAttach(Property) handler Name { … } +

@OnAttach(Property) fires when the named property is structurally added to a live model instance with attach P on e — the property's fields are seeded first, then the hook runs in the context of that instance so it can read and adjust them. This is the structural birth of a property, the counterpart to @OnDetach; it differs from @OnEnable, which only un-pauses a property that already exists. Use it to react when a capability appears on an instance at runtime — a shield goes up, a status effect lands. The property name is a checked reference. Add @Public to also emit a prop_<Property>_attach event.

+ +

Example

program AttachHook {
+  property Tag    { value: int = 0 }
+  property Shield { amount: int = 0 }
+  model Unit { Tag }
+
+  @OnAttach(Shield) handler RaiseShield {            # fires when Shield is attached
+    print(Shield.amount + 10)                        # 15 — reads the seeded amount
+  }
+
+  handler Seed phase Start { spawn Unit { Tag { value: 1 } } }
+
+  handler AddShield phase Render {
+    for (unit) in query [Unit] {
+      attach Shield on self() { amount: 5 }          # triggers @OnAttach
+    }
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-ondespawn.html b/annot-ondespawn.html new file mode 100644 index 00000000..a40fdd43 --- /dev/null +++ b/annot-ondespawn.html @@ -0,0 +1,46 @@ + + + + + +@OnDespawn — Ludic + + + + + + +
+
API Reference › Annotations › @OnDespawn
+
+ annotation +

@OnDespawn

+
+ @OnDespawn(Model, reason: r) handler Name { … } +

@OnDespawn(Model) is the teardown counterpart to @OnSpawn: it fires once for each instance of the named model as it is removed, whether by an in-world despawn or at program shutdown, and its body still reads the instance's outgoing field values before they are gone. With the optional reason: r binding it becomes reason-carrying teardown — r is bound to an EndReason the compiler passes at each teardown site (an in-world despawn passes EndReason.Despawned, program exit passes EndReason.Quit) so one hook can branch on why the instance is ending, the way Unreal's EndPlay(reason) does. Every still-live instance's hook fires at quit, which makes "no silent deaths" real: drop loot on a real death but skip it when the app is simply closing. Add @Public to also emit a model_<Model>_despawn event.

+ +

Example

program DespawnHook {
+  property Health { current: int = 0 }
+  property Loot   { gold: int = 0 }
+  model Enemy { Health, Loot }
+
+  @OnDespawn(Enemy, reason: teardown_reason) handler DropLoot {
+    match teardown_reason {
+      EndReason.Quit => { }                            # app closing — do not drop loot
+      _              => { print(Loot.gold) }          # died in-world — award the gold
+    }
+  }
+
+  handler SpawnWave phase Start { spawn Enemy { Health { current: 10 }, Loot { gold: 25 } } }
+
+  handler KillOne phase Render {
+    for (Health) in query [Health, {Enemy}] { despawn self() }   # prints 25
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-ondetach.html b/annot-ondetach.html new file mode 100644 index 00000000..5c6c5469 --- /dev/null +++ b/annot-ondetach.html @@ -0,0 +1,48 @@ + + + + + +@OnDetach — Ludic + + + + + + +
+
API Reference › Annotations › @OnDetach
+
+ annotation +

@OnDetach

+
+ @OnDetach(Property) handler Name { … } +

@OnDetach(Property) is the teardown counterpart to @OnAttach: it fires when the named property is structurally removed from a live instance with detach P on e. The hook runs before the property's presence flag clears, so its body can still read the outgoing field values one last time — useful for releasing whatever the property was tracking or logging its final state. It is distinct from @OnDisable, which pauses a property while keeping its data; detach actually destroys the property's presence on the instance. Add @Public to also emit a prop_<Property>_detach event.

+ +

Example

program DetachHook {
+  property Tag    { value: int = 0 }
+  property Shield { amount: int = 0 }
+  model Unit { Tag }
+
+  @OnDetach(Shield) handler DropShield {             # fires as Shield is removed
+    print(Shield.amount + 20)                        # 25 — reads the outgoing amount
+  }
+
+  handler Seed phase Start {
+    spawn Unit { Tag { value: 1 } }
+    for (unit) in query [Unit] { attach Shield on self() { amount: 5 } }
+  }
+
+  handler RemoveShield phase Render {
+    for (shield) in query [Shield] {
+      detach Shield on self()                         # triggers @OnDetach
+    }
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-ondisable.html b/annot-ondisable.html new file mode 100644 index 00000000..ec863511 --- /dev/null +++ b/annot-ondisable.html @@ -0,0 +1,45 @@ + + + + + +@OnDisable — Ludic + + + + + + +
+
API Reference › Annotations › @OnDisable
+
+ annotation +

@OnDisable

+
+ @OnDisable(Property) handler Name { … } +

@OnDisable(Property) fires when a present property is paused with disable P on e. Disabling keeps the property's data intact and simply stops it from participating — the logical "off" switch — so this hook is the moment a capability goes dormant, not the moment it is destroyed (that is @OnDetach). The body runs in the instance's context and can read the fields before they go quiet, which is handy for cleaning up an effect the property was driving. It pairs with @OnEnable for capabilities that toggle on and off repeatedly. Add @Public to also emit a prop_<Property>_disable event.

+ +

Example

program DisableHook {
+  property Tag    { value: int = 0 }
+  property Shield { amount: int = 0 }
+  model Unit { Tag, Shield }
+
+  @OnDisable(Shield) handler ShieldDown {            # fires when Shield is disabled
+    print(Shield.amount)                             # 5 — data is retained, just paused
+  }
+
+  handler Seed phase Start { spawn Unit { Tag { value: 1 }, Shield { amount: 5 } } }
+
+  handler Deactivate phase Render {
+    for (unit) in query [Unit] {
+      disable Shield on self()                        # triggers @OnDisable
+    }
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-onenable.html b/annot-onenable.html new file mode 100644 index 00000000..95a248b7 --- /dev/null +++ b/annot-onenable.html @@ -0,0 +1,46 @@ + + + + + +@OnEnable — Ludic + + + + + + +
+
API Reference › Annotations › @OnEnable
+
+ annotation +

@OnEnable

+
+ @OnEnable(Property) handler Name { … } +

@OnEnable(Property) fires when a property that is present but disabled is turned back on with enable P on e. Unlike @OnAttach, the property's data was never destroyed — enable/disable only pause and resume a property that already exists, keeping its fields intact — so this hook is about a capability becoming active again rather than being created. The body runs in the instance's context and can read the retained field values. Use the enable/disable pair for a capability that toggles repeatedly (a power-up, a stun) where re-seeding on every toggle would be wrong. Add @Public to also emit a prop_<Property>_enable event.

+ +

Example

program EnableHook {
+  property Tag    { value: int = 0 }
+  property Shield { amount: int = 0 }
+  model Unit { Tag, Shield }
+
+  @OnEnable(Shield) handler ShieldBackOnline {       # fires when Shield is re-enabled
+    print(Shield.amount)                             # 5 — the retained amount, not re-seeded
+  }
+
+  handler Seed phase Start {
+    spawn Unit { Tag { value: 1 }, Shield { amount: 5 } }
+    for (unit) in query [Unit] { disable Shield on self() }
+  }
+
+  handler Reactivate phase Render {
+    for (unit) in query [Unit] { enable Shield on self() }         # triggers @OnEnable
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-onquit.html b/annot-onquit.html new file mode 100644 index 00000000..9f2cc59b --- /dev/null +++ b/annot-onquit.html @@ -0,0 +1,39 @@ + + + + + +@OnQuit — Ludic + + + + + + +
+
API Reference › Annotations › @OnQuit
+
+ annotation +

@OnQuit

+
+ @OnQuit handler Name { … } +

@OnQuit pins a handler to the program's shutdown moment: it runs exactly once as the program exits, after the final frame. Use it for teardown that must happen once — flushing a save, printing a final tally, releasing a host resource. In the lifecycle timeline it runs after every still-live instance's @OnDespawn hook has fired, so the world has already been torn down by the time @OnQuit sees it. It is the boot-time counterpart of @OnStart; add @Public to also emit a program_quit event other modules can hook.

+ +

Example

program QuitHook {
+  property Score { total: int = 0 }
+  model Scoreboard { Score }
+
+  @OnQuit handler ReportFinalScore {                 # runs once at shutdown
+    for (Score) in query [Score] { print(Score.total) }
+  }
+
+  handler Seed phase Start { spawn Scoreboard { Score { total: 42 } } }
+
+  handler Finish phase Render { quit() }             # triggers shutdown -> prints 42
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-onspawn.html b/annot-onspawn.html new file mode 100644 index 00000000..e5feef25 --- /dev/null +++ b/annot-onspawn.html @@ -0,0 +1,40 @@ + + + + + +@OnSpawn — Ludic + + + + + + +
+
API Reference › Annotations › @OnSpawn
+
+ annotation +

@OnSpawn

+
+ @OnSpawn(Model) handler Name { … } +

@OnSpawn(Model) turns a handler into a birth hook: it fires once for each newly spawned instance of the named model, right after its properties are seeded. The body runs in the context of that fresh instance, so it can read and adjust the instance's fields by property name to finish initialization — seed derived state, register the instance, play a spawn effect. The model name is a checked reference, so a typo is a compile error rather than a silently dead hook. Its teardown counterpart is @OnDespawn; add @Public to also emit a model_<Model>_spawn event other modules can listen for.

+ +

Example

program SpawnHook {
+  property Health { current: int = 0, maximum: int = 100 }
+  model Enemy { Health }
+
+  @OnSpawn(Enemy) handler InitEnemy {                # fires per new Enemy instance
+    Health.current = Health.maximum                  # start every enemy at full health
+    print(Health.current)                            # 100
+  }
+
+  handler SpawnWave phase Start {
+    spawn Enemy { Health { current: 0, maximum: 100 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-onstart.html b/annot-onstart.html new file mode 100644 index 00000000..87d744c0 --- /dev/null +++ b/annot-onstart.html @@ -0,0 +1,40 @@ + + + + + +@OnStart — Ludic + + + + + + +
+
API Reference › Annotations › @OnStart
+
+ annotation +

@OnStart

+
+ @OnStart handler Name { … } +

@OnStart pins a handler to the program's boot moment: it runs exactly once when the program starts, before the per-frame phases begin, instead of being placed in a recurring phase. Use it for one-time setup — spawning the initial world, seeding program vars, loading a scene. It overrides the handler's phase, so you write @OnStart handler Name { … } rather than handler Name phase Start and get the same once-at-boot placement through the annotation channel. Its shutdown counterpart is @OnQuit; add @Public to also emit a program_start event other modules can hook.

+ +

Example

program BootHook {
+  property Health { current: int = 0, maximum: int = 100 }
+  model Player { Health }
+
+  var elapsed_frames: int = 0
+
+  @OnStart handler CreateWorld {                     # runs once at boot
+    spawn Player { Health { current: 100, maximum: 100 } }
+    elapsed_frames = 0
+  }
+
+  handler CountFrames phase Update { elapsed_frames = elapsed_frames + 1 }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-owned.html b/annot-owned.html new file mode 100644 index 00000000..6cb4b3fc --- /dev/null +++ b/annot-owned.html @@ -0,0 +1,41 @@ + + + + + +@Owned — Ludic + + + + + + +
+
API Reference › Annotations › @Owned
+
+ annotation +

@Owned

+
+ @Owned model Name { … } +

@Owned declares that every instance of a model carries a network owner — it adds an owner slot to the model (the runtime's @L_owner array), which the builtins owner, set_owner, and is_owner read and assign. A fresh instance starts unowned (-1) until the authority assigns it. Ownership is what gates who may write @Sync(to: owner) fields and who runs @Predicted control handlers, and it is stored in the world snapshot so it round-trips through replication and rollback. Mark the models that represent a player's avatar or units; leave shared scenery unowned.

+ +

Example

program OwnedModel {
+  @Sync property Position { column: int = 0, row: int = 0 }
+  @Owned model Player { @Sync Position }              # each Player instance has an owner slot
+
+  entry {
+    spawn Player { Position { column: 5, row: 6 } }
+    for (Position) in query [Position, {Player}] {
+      let player_id = self()
+      print(owner(player_id))          # -1 — unowned until assigned
+      set_owner(player_id, 0)
+      print(is_owner(player_id))       # 1  — the local peer now owns it
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-predicted.html b/annot-predicted.html new file mode 100644 index 00000000..c5ea153f --- /dev/null +++ b/annot-predicted.html @@ -0,0 +1,42 @@ + + + + + +@Predicted — Ludic + + + + + + +
+
API Reference › Annotations › @Predicted
+
+ annotation +

@Predicted

+
+ @Predicted handler Name { … } +

@Predicted marks a control handler that runs in two places: speculatively on the client that owns the affected instance, so local input feels instant, and authoritatively on the server, whose result reconciles the client if the two diverge. It is the responsive-control role from Quake-style client-prediction, named for the netcode behavior (owner-predicts plus server-authoritative plus reconcile) rather than the machine, and it matches Unity's GhostMode.Predicted so the concept transfers. Prediction is explicit — Ludic never silently predicts — and it only applies to instances of an @Owned model, since the dispatch reads is_owner to decide whether the local client should run it. Use it for the owning player's movement and actions; leave authority-only rules on @Server.

+ +

Example

program PredictedMovement {
+  @Sync property Position { column: int = 0, row: int = 0 }
+  @Owned model Player { @Sync Position }
+
+  @Predicted handler MovePlayer phase Input {        # owner predicts, server reconciles
+    let pressed = Input.key()
+    for (Position) in query [Position, {Player}] {
+      if pressed == 'd' { Position.column = Position.column + 1 }
+    }
+  }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 0, row: 0 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-public.html b/annot-public.html new file mode 100644 index 00000000..1e5ff2c7 --- /dev/null +++ b/annot-public.html @@ -0,0 +1,43 @@ + + + + + +@Public — Ludic + + + + + + +
+
API Reference › Annotations › @Public
+
+ annotation +

@Public

+
+ @Public @OnSpawn(Model) handler … { … } +

@Public stacks in front of a lifecycle hook to also emit a well-named event at the same moment the hook fires, so other modules (or mods) can react without touching the original code. It turns a private hook into a public seam: @Public @OnSpawn(Model) emits model_<Model>_spawn, @OnDespawn emits model_<Model>_despawn, the property hooks emit prop_<Property>_attach/_detach/_enable/_disable, and @OnStart/@OnQuit emit program_start/program_quit. This is the same lowering the event system uses, so anyone can @On the generated event to extend behavior at that lifecycle point. Add it when a hook marks an extension point you want to keep open across a modular codebase.

+ +

Example

program PublicSpawn {
+  property Health { current: int = 0, maximum: int = 100 }
+  model Enemy { Health }
+
+  @Public @OnSpawn(Enemy) handler InitEnemy {        # also emits model_Enemy_spawn
+    Health.current = Health.maximum
+  }
+
+  @On(model_Enemy_spawn) handler AnnounceEnemy {     # another module hooks the public event
+    print(1)                                         # 1 — reacts to every Enemy spawn
+  }
+
+  handler SpawnWave phase Start {
+    spawn Enemy { Health { current: 0, maximum: 100 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-queries.html b/annot-queries.html new file mode 100644 index 00000000..4d58ee1f --- /dev/null +++ b/annot-queries.html @@ -0,0 +1,42 @@ + + + + + +@Queries — Ludic + + + + + + +
+
API Reference › Annotations › @Queries
+
+ annotation +

@Queries

+
+ @Queries(these: [Position, Velocity]) +

@Queries lets a handler declare the properties it operates on up front, and the compiler wraps the whole body in a query loop over the matching model instances. The body then runs once per match with each listed property bound by name — you write Position.column directly instead of opening a for (…) in query […] yourself — and self() gives the current instance. It takes the same shape as a manual query: these: [ … ] lists the required properties (each may carry a filter like Health{current <= 0}), and an optional on: Model restricts the match to instances of one model. Reach for it when a handler's entire job is "for every matching instance, do this"; drop to an explicit query when you need finer control inside the loop.

+ +

Example

program AdvanceMovement {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Player { Position, Velocity }
+
+  @Queries(these: [Position, Velocity], on: Player)   # body runs once per matching Player
+  handler AdvancePositions phase FixedUpdate {
+    Position.column = Position.column + Velocity.delta_x
+    Position.row = Position.row + Velocity.delta_y
+  }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 0, row: 0 }, Velocity { delta_x: 1, delta_y: 0 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-reads.html b/annot-reads.html new file mode 100644 index 00000000..3089d597 --- /dev/null +++ b/annot-reads.html @@ -0,0 +1,43 @@ + + + + + +@Reads — Ludic + + + + + + +
+
API Reference › Annotations › @Reads
+
+ annotation +

@Reads

+
+ @Reads(Property) +

@Reads(Property) declares that a handler reads the named property. It is an analysis hint: it documents a handler's data dependencies so tooling and scheduling can reason about which handlers touch which state — for example, to tell whether two handlers can run without conflict. It does not itself bind fields the way @Queries does, nor does it grant access; it annotates intent alongside the handler's actual query. Pair it with @Writes to spell out the full read/write footprint of a handler. Keep the named property real so the declared footprint matches what the body does.

+ +

Example

program ReadFootprint {
+  property Health { current: int = 100 }
+  property Score  { total: int = 0 }
+  model Player { Health, Score }
+
+  @Reads(Health)                                     # declares the read dependency
+  handler ReportLowHealth phase Update {
+    for (Health) in query [Health, {Player}] {
+      if Health.current < 25 { print(Health.current) }
+    }
+  }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Health { current: 20 }, Score { total: 0 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-server.html b/annot-server.html new file mode 100644 index 00000000..c0a66e69 --- /dev/null +++ b/annot-server.html @@ -0,0 +1,43 @@ + + + + + +@Server — Ludic + + + + + + +
+
API Reference › Annotations › @Server
+
+ annotation +

@Server

+
+ @Server handler Name { … } +

@Server marks a handler as server-authoritative: it runs only on the peer acting as the authority, and clients receive its effects through ordinary @Sync replication rather than by running the handler themselves. This is the declarative alternative to sprinkling is_server() branches through gameplay code — the role is a property of the handler, so every line's placement stays legible. Unmarked handlers are the shared, deterministic simulation that runs everywhere; use @Server for decisions that must have a single source of truth, like awarding score, resolving damage, or spawning authoritative entities. Offline the role register defaults to server, so the guard collapses to "run here" and a single-player build is unchanged.

+ +

Example

program AuthoritativeScore {
+  property Score { total: int = 0 }
+  model Scoreboard { Score }
+
+  handler Tick phase Update {                        # unmarked -> runs on every peer
+    for (Score) in query [Score] { Score.total = Score.total + 1 }
+  }
+
+  @Server handler AwardBonus phase Update {          # authority only
+    for (Score) in query [Score] { Score.total = Score.total + 100 }
+  }
+
+  handler SpawnScoreboard phase Start {
+    spawn Scoreboard { Score { total: 0 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-sync.html b/annot-sync.html new file mode 100644 index 00000000..1cb66659 --- /dev/null +++ b/annot-sync.html @@ -0,0 +1,41 @@ + + + + + +@Sync — Ludic + + + + + + +
+ +
+ annotation +

@Sync

+
+ @Sync property … / @Owned +

These are the two declarative switches at the top of Ludic's networking: @Sync says what replicates and @Owned says who owns an instance. Replication is opt-in at the field level and decided per model use-site — a field crosses the wire only when it is both replicable (marked @Sync, either on the field or via @Sync property P which marks every field of P) and participating (the model marks the component @Sync). So the same property can replicate in one model and not another, and there is no @NoSync because the surface is purely additive. From these marks the compiler generates the per-model serialize/apply codecs; @Sync on a non-POD-scalar field (like a pointer) is a compile error, since a machine-local pointer cannot cross the wire. @Owned adds the owner slot the ownership builtins and @Predicted read. See the dedicated @Owned page for ownership details.

+ +

Example

program SyncedWorld {
+  @Sync property Position { column: int = 0, row: int = 0 }   # every field replicable
+  property Health { @Sync current: int = 0, maximum: int = 0 }  # only current replicable
+
+  @Owned model Player { @Sync Position, @Sync Health }         # participates -> column,row,current
+  model Scenery { Position }                                   # not @Sync here -> never replicates
+
+  entry {
+    spawn Player { Position { column: 3, row: 4 }, Health { current: 50, maximum: 100 } }
+    for (Position, Health) in query [Position, Health, {Player}] {
+      print(sync_size(self()))          # 12 = Position(8) + Health.current(4)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-system.html b/annot-system.html new file mode 100644 index 00000000..2a524880 --- /dev/null +++ b/annot-system.html @@ -0,0 +1,36 @@ + + + + + +@System — Ludic + + + + + + +
+
API Reference › Annotations › @System
+
+ annotation +

@System

+
+ "@System(Phase) function name() { … }" +

@System(Phase) marks a function in a prebuilt binary module (one compiled with --emit-module) as a runtime-registered system. The module carries a load-time constructor that hands each such function to the host through ludic_register_system, and the host's frame loop calls it every Phase after its own handlers — the runtime, escape-hatch counterpart of the compile-time @EngineSystem. The compiler supplies the function's address (Ludic source cannot take one). This is how a closed-source or other-language module contributes systems over the reflection C-ABI; source packages that compile into the consumer should prefer @EngineSystem. Phases are Input, FixedUpdate, Update, LateUpdate and Render.

+ +

Example

program ManaModule {
+  function module_init() -> void { world_register_prop("Mana", 2) }   # register a component at load
+
+  @System(Update) function mana_regen() -> void {                     # runs every Update in the host
+    let M = world_prop_id("Mana")
+    let e = world_query_next(M, 0)
+    if e >= 0 { world_set(e, M, 0, world_get(e, M, 0) + 1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-toclients.html b/annot-toclients.html new file mode 100644 index 00000000..08ee5035 --- /dev/null +++ b/annot-toclients.html @@ -0,0 +1,44 @@ + + + + + +@ToClients — Ludic + + + + + + +
+
API Reference › Annotations › @ToClients
+
+ annotation +

@ToClients

+
+ @ToClients event Name { … } +

@ToClients marks an event as a directional remote event that travels from the authority down to clients — a broadcast, again the ordinary event bus with a direction flag. When the server emits it, the POD payload is serialized as an event id plus its packed fields and sent through the transport seam to the clients; each client's pump re-emits it into normal @On dispatch, so an @On(Name) handler on every client reacts. Use it for authoritative notifications a client should present but not decide — an explosion happened, a round ended, a pickup was granted. Its mirror is @ToServer, which carries client requests the other way.

+ +

Example

program ExplosionBroadcast {
+  property EffectLog { count: int = 0 }
+  model Client { EffectLog }
+
+  @ToClients event Boom { column: int = 0, row: int = 0 }   # server -> clients broadcast
+
+  @On(Boom) handler PlayExplosion {                  # every client reacts
+    for (EffectLog) in query [EffectLog, {Client}] {
+      EffectLog.count = EffectLog.count + 1
+    }
+  }
+
+  entry {
+    spawn Client { EffectLog { count: 0 } }
+    emit Boom(column: 8, row: 3)                      # serialized to clients, re-emitted on arrival
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-toserver.html b/annot-toserver.html new file mode 100644 index 00000000..32979175 --- /dev/null +++ b/annot-toserver.html @@ -0,0 +1,44 @@ + + + + + +@ToServer — Ludic + + + + + + +
+
API Reference › Annotations › @ToServer
+
+ annotation +

@ToServer

+
+ @ToServer event Name { … } +

@ToServer marks an event as a directional remote event that travels from a client up to the authority — an RPC, expressed as the ordinary event bus with a direction flag rather than a new concept. At an emit site the event's POD payload is serialized as an event id followed by its packed fields and sent through the transport seam toward the server; it does not run locally. On the far side, draining the transport (the runtime's pump) re-emits it into the normal @On dispatch, so a @Server @On(Name) handler picks it up. Use it for player intent — a move request, a fire command — that the authority must validate before it affects the world. Its mirror is @ToClients, which broadcasts the other way.

+ +

Example

program FireRequest {
+  property AmmoLog { shots_fired: int = 0 }
+  model Turret { AmmoLog }
+
+  @ToServer event Fire { direction: int = 0 }        # client -> server request
+
+  @Server @On(Fire) handler HandleFire {             # authority validates and applies
+    for (AmmoLog) in query [AmmoLog, {Turret}] {
+      AmmoLog.shots_fired = AmmoLog.shots_fired + direction
+    }
+  }
+
+  entry {
+    spawn Turret { AmmoLog { shots_fired: 0 } }
+    emit Fire(direction: 1)                           # serialized onto the wire, not run locally
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/annot-writes.html b/annot-writes.html new file mode 100644 index 00000000..d14bbf8f --- /dev/null +++ b/annot-writes.html @@ -0,0 +1,42 @@ + + + + + +@Writes — Ludic + + + + + + +
+
API Reference › Annotations › @Writes
+
+ annotation +

@Writes

+
+ @Writes(Property) +

@Writes(Property) declares that a handler writes the named property. Like @Reads it is an analysis hint that documents a handler's data footprint so tooling and scheduling can reason about conflicts — two handlers that write the same property, or one that writes what another reads, cannot be reordered freely. It does not bind fields or grant access on its own; the handler still queries and mutates the property in its body as usual. Declaring reads and writes together makes a handler's effect on the world legible at a glance, which matters most in a large, modular codebase. Keep the named property accurate so the declared footprint matches the code.

+ +

Example

program WriteFootprint {
+  property Health { current: int = 100 }
+  model Enemy { Health }
+
+  @Reads(Health) @Writes(Health)                     # full read/write footprint
+  handler ApplyPoison phase Update {
+    for (Health) in query [Health, {Enemy}] {
+      Health.current = Health.current - 1
+    }
+  }
+
+  handler SpawnEnemy phase Start {
+    spawn Enemy { Health { current: 100 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/api.html b/api.html new file mode 100644 index 00000000..bb0d2c7c --- /dev/null +++ b/api.html @@ -0,0 +1,146 @@ + + + + + +Ludic — API Reference + + + + + + +
+
+
Reference
+

API Reference

+

Every keyword, type, phase, builtin, namespace method, annotation and color in Ludic — each on its own page. Search, or browse by section. In any code sample across this site, hover a token for a summary and click to jump to its page.

+ + +
+

Program structure

The shape of a Ludic program: one program block holding declarations.

Entities & the ECS

Entities are ids; properties are their data; queries walk them.

spawnCreate a model instance carrying the listed properties, seeding their fields.despawnRemove a model instance from the world, firing any @OnDespawn hooks.queryMatch every model instance carrying all the listed properties.selfThe model instance currently bound by the enclosing query loop.enableRe-activate a disabled property, model, or handler — its data is intact.disableDeactivate a property, model, or handler without destroying its data.attachStructurally add a property to a live model instance, seeding its fields.detachStructurally remove a property from a live model instance.world_attach_dynAttach a property to an instance by numeric id at runtime (dynamic ECS).world_countThe total number of live model instances in the world.world_detach_dynDetach a property from an instance by numeric id at runtime (dynamic ECS).world_field_idResolve a field name within a property to its numeric index.world_getRead one field of a model instance by numeric id (reflection ABI).world_hasTest whether a model instance currently carries a property.world_kindThe model id of an instance — which kind of thing it is.world_loadRestore the whole world from a serialized byte buffer.world_model_idResolve a model's name to its stable numeric id.world_prop_idResolve a property's name to its stable numeric id.world_query_nextStep to the next instance carrying a property, walking the world by id.world_register_propRegister a brand-new property at runtime and get its id (dynamic ECS).world_saveSerialize the whole world into a buffer; returns the number of bytes written.world_setWrite one field of a model instance by numeric id (reflection ABI).world_sizeThe number of live model instances in the world.world_spawnSpawn an instance of a model chosen by numeric id at runtime.

Control flow

Branches, loops, and state machines.

Scenes & layers

One active scene at a time, each grouping handlers into layers.

Events

Decoupled, named messages between handlers.

Screen — drawing

The 2D drawing surface. Every call takes named arguments; draw during the Render phase, then Screen.show().

Screen.clearFill the whole framebuffer with one color to start a fresh frame.Screen.fill_rectangleDraw a solid, filled rectangle at a pixel position.Screen.draw_rectangleDraw a one-pixel-thick rectangle outline (not filled).Screen.put_pixelSet a single pixel at a pixel coordinate to one color.Screen.draw_textDraw a string with the built-in font at an integer scale.Screen.draw_numberDraw an integer directly, with no string allocation or conversion.Screen.showPresent the finished frame — copy everything drawn this frame to the window.Screen.widthThe framebuffer width in pixels.Screen.heightThe framebuffer height in pixels.Screen.statusSet the persistent one-line status/HUD string shown by the runtime.Screen.lineDraw a straight line between two points.Screen.circleDraw a circle outline.Screen.fill_circleDraw a filled disc.Screen.triangleDraw a triangle outline.Screen.fill_triangleDraw a filled triangle.Screen.spriteBlit a sprite at its natural size.Screen.sprite_scaledBlit a sprite at an integer scale.Screen.ovalDraw an axis-aligned ellipse outline.Screen.cameraSet the world-space camera offset for the draw path.Screen.clipRestrict drawing to a screen-space rectangle.Screen.clip_resetLift the clip rectangle (draw to the whole screen again).Screen.blend_modeChoose replace or additive pixel blending.Screen.measure_textThe pixel advance width of text in the built-in font.Screen.pixelRead a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.Screen.barA filled meter: value of max over a track.

Clock

A game-controlled simulated clock: a single seconds counter the game owns. Unlike Time.now / Time.since, it never reads the wall clock, so any gameplay that reads Clock.now() is deterministic and replay-safe. Set it outright, or advance it by a Duration each tick to run time at whatever rate the simulation wants. The clock is a plain instant (seconds since 1970), so all the DateTime.* readers work on it directly.

Collision

2D overlap tests on integer coordinates (pixels or tiles). Rectangles are (x, y, w, h) from the top-left; circles are (x, y, r). Each returns a bool. Squared distances are computed in 64-bit so large coordinates never overflow. + +These are static boolean tests. For a *moving* body with real collision resolution, the engine ships a physics-lite movement system built on the same integer math — declare the well-known components and the engine advances them for you each frame, no handler wired: + +- property Body { vx, vy, gravity, max_fall, rx, ry, policy, on_ground, hit_wall, hit_ceiling } — velocity/accel state in Q16.16 fixed-point (vx/vy/gravity/max_fall), engine-owned sub-pixel accumulators (rx/ry), an integration policy (0 platformer with gravity, 1 top-down), and the derived contact flags the engine sets each frame. +- property Collider { w, h, offx, offy, is_trigger, one_way, layer, mask, hit, entered, exited } — an AABB shape offset from the entity's Position { x, y }, with layer/mask filtering, one-way-platform and trigger flags, and per-frame trigger outputs. +- property Solids { tile, wall, oneway } — an optional single config entity that turns on the tile-grid broadphase over the Map tilemap (tile px size, the solid wall glyph, and an optional one-way oneway glyph). + +The engine-owned esys_move system (Update phase) integrates velocity and gravity into a tentative move, then resolves it with a per-axis **swept AABB** — against both solid Collider entities and the tile grid — so a fast body never tunnels through a wall. It handles one-way platforms (which block only a downward landing), reports trigger/sensor overlaps without resolving them, and sets on_ground / hit_wall / hit_ceiling for a controller to read. Everything is integer and deterministic, so movement reproduces exactly under replay, lockstep and world_save snapshots. Contact is surfaced as polled flags (the SpriteAnim.event_fired shape) rather than engine-emitted events, so a game raises its own CollisionResolved / TriggerEntered events from its handler with no coupling. This is the shared foundation the built-in gameplay controllers build on. Related: Grid, Map, Motion, @EngineSystem.

Color functions

Building and blending colors at runtime. Colors are 0x00RRGGBB ints; these pack channels and transform an existing color. The named palette constants (Color.Charcoal, …) are a separate compile-time set.

Crypto

Secure, test-vector-backed hashing for the handful of security-sensitive things a game actually does: signing a save or leaderboard payload so casual tampering is detectable, and verifying that a network message or token was not forged by someone who does not hold the key. This is the deliberate counterpart to the fast Hash library — same idea, opposite trade-off. Hash is fast and reversible and must never guard anything; Crypto is SHA-256 and HMAC-SHA256 implemented to the standard, so the algorithms are the ones with published known-answer tests rather than anything home-grown. + +Digests are returned as lowercase hex strings, not raw bytes — a str is null-terminated and a raw digest can contain a zero byte, so hex is the form you can print, store, and compare directly. + +Alongside hashing, this library exposes the OS cryptographically-secure random generator — random_bytes, random_hex, and random_u32 — for tokens, nonces, and Uuid generation, plus base64 for moving bytes through text-only channels. The secure-random helpers are deliberately non-deterministic and must never seed the lockstep simulation RNG (Random). + +What this is not: it is not DRM and it is not unbeatable anti-cheat. A client-side game cannot keep a secret from the machine it runs on — a determined owner can always read the key out of the binary. Use it to make *casual* tampering detectable and to authenticate messages between parties who share a key. To verify a MAC always use Crypto.verify_hmac (a constant-time check), never ==, which leaks how much of a guessed MAC was correct.

Date

Calendar days on the proleptic Gregorian calendar (UTC). A Date is stored as a plain int: the count of days since 1970-01-01, so shifting a date by whole days is ordinary integer arithmetic and two dates subtract to a day count. Build one with Date.new, read its parts with year/month/day/weekday. Every operation is deterministic integer math — no leap seconds, no timezone, no floating point.

DateTime

Instants on the wall-clock timeline (UTC), stored as a plain int: the count of seconds since 1970-01-01, matching Time.now. Build one with DateTime.from, shift it by a Duration with DateTime.add, and pull it apart with the year/month/day/hour/minute/second readers. Deterministic integer math throughout; v1 is UTC-only with no leap seconds, and assumes instants at or after 1970.

Duration

Spans of real time, measured in whole seconds and carried in a plain int — so a duration adds, subtracts and compares with the ordinary operators (Duration.minutes(5) + Duration.seconds(30), away > Duration.hours(3)). The constructors build a span from a unit; the as_* readers convert a span back to whole units (truncating toward zero). Integer-only, so no floating-point drift.

Ease

Tween curves — the "juice" layer. Each takes a normalized amount t in 0.0..1.0 and returns an eased fixed, ready to feed to Math.lerp. All deterministic fixed-point.

Fs

The filesystem, wrapped into one safe, ergonomic API — no file descriptors, no byte buffers, no half-written files. The foundation for saves, config, mods, and asset loading: read and write whole files as text, check existence, make directories, copy, and list a folder. The bare file_* builtins remain underneath for streaming; Fs.* is the everyday layer. + +Fallible calls surface failure as a value — null, false, or -1 — that you branch on, never a crash (a first-class try/else lands with the error-handling work). Writes are atomic (written to a temporary file and renamed into place), so a crash mid-write never corrupts the previous file. list returns entries sorted, so a directory walk is reproducible across runs and platforms. + +Paths are /-separated over the native (macOS/BSD) filesystem — the fully supported target today; a sandboxed virtual filesystem for wasm and recursive directory copy are follow-ups. Text helpers are UTF-8 (they pair with Unicode); use Path to build the paths and Os for per-user locations.

Hash

Fast, non-cryptographic hashing for everyday game needs: turning string IDs into integer handles, mixing a few numbers into one deterministic seed, and checksumming data to catch corruption. Every function is plain 32-bit integer arithmetic with a defined byte order and fixed constants, so a given input hashes to exactly the same value on every platform and every run — the guarantee that makes it safe for procedural generation and lockstep networking. Results are 32-bit and print as signed integers. Arguments are positional. + +Not for security. These hashes are fast and reversible — never use them for passwords, tokens, or tamper-proofing. For secure hashing reach for the cryptography library instead.

Input

Reading input. At the base, Input.key gives this frame's key and Input.poll is the single per-frame read that also drives deterministic record/replay. Over that sit named actions — Input.bind / Input.down / Input.pressed / Input.rebind — so gameplay reads rebindable actions, not physical keys. + +The device layer adds everything past one key per frame: multiple simultaneous held keys (Input.key_down / key_pressed / key_released), analog Input.axis and normalized Input.vector, the mouse (position, delta, buttons, wheel), gamepads and touch. The held set is fed by the platform when windowed and by the Input.press / Input.set_* injection on every target — the same idea as Godot's action_press, and what a replay, an AI, or the network feeds. Everything is integer and deterministic, and record/replay snapshots the whole per-frame state.

Input.keyThe key pressed this frame as a character code (0 when nothing is pressed).Input.bindBind a physical key to a named action, so gameplay reads the action, not the key.Input.rebindRemap an action from one key to another at runtime (rebinding menus).Input.pollAdvance one frame of input; the single per-frame read behind actions and replay.Input.downIs a named action held on the frame last polled?Input.pressedDid a named action go down this frame (a one-shot edge)?Input.recordStart recording polled input each frame (for deterministic replay).Input.replayReplay recorded input; poll then reads the tape, not the device.Input.key_downIs a physical key held this frame?Input.key_pressedDid a key go down this frame (edge)?Input.key_releasedDid a key go up this frame (edge)?Input.key_labelThe name to show a player for a key - in their own keyboard layout.Input.pressInject a held key (AI, tutorial, testing, network).Input.releaseRelease an injected key.Input.axisA -1..+1 axis from two keys.Input.axis_iA -1/0/1 directional intent as a plain int, no bool->int glue.Input.vectorA normalized 2D vector from four keys.Input.strength0..1 strength of a named action.Input.mouse_xThe mouse x position this frame.Input.mouse_yThe mouse y position this frame.Input.mouse_dxMouse x movement since the last poll.Input.mouse_dyMouse y movement since the last poll.Input.mouse_downIs a mouse button held?Input.wheelScroll-wheel delta this frame.Input.set_mouseInject the mouse state (headless / AI / testing).Input.pad_connectedIs a gamepad connected?Input.pad_buttonIs a gamepad button held?Input.pad_axisA gamepad analog axis, -1..+1.Input.set_padInject a gamepad's whole state.Input.touch_countHow many touch points are active?Input.touch_xThe x of a touch point.Input.touch_yThe y of a touch point.Input.set_touchInject a touch point.Input.actionRegister a default key binding for an action (kept if already bound).Input.bind_padAlso fire a named action from a gamepad button (device-agnostic).Input.activeIs a named action active right now (any bound key held, or pad button down)?Input.just_pressedDid a named action go active this frame (the deterministic on-press)?Input.just_releasedDid a named action go inactive this frame (the on-release edge)?Input.cursor_modeHide / lock / confine the OS mouse cursor to the window (windowed).Input.move_iThe standard top-down movement intent, -1/0/1 per axis.

List

Operations over []T slices — length, ends access, push/pop, swap, search, and in-place reverse. A slice is a shared growable buffer, so these mutate it in place and every holder sees the change. Arguments are positional.

Log

Levelled, structured logging — the default way to answer "what is my game doing?" and "why did that break?", and a real step up from scattering print calls through your code. Each message carries a level, from trace (most verbose) through debug, info, and warn to error, and a runtime threshold set with Log.set_level decides which ones actually appear — so a development build can be chatty and a release build quiet, without touching the call sites. + +Messages go to standard error, kept separate from a program's real stdout, tagged with their level. Beyond the message you can pass **structured fields** as trailing key/value pairs — Log.warn("missing texture", "path", p, "id", n) prints [WARN] missing texture path=... id=... — cheap to write and easy to grep. Numeric values (int, long) are formatted for you; the level tag is chosen at compile time, so a filtered-out level costs only a threshold comparison at runtime. + +Logging never touches the simulation — it writes to stderr and returns — so it has no effect on gameplay determinism or replays. This first version ships the console (stderr) sink; a rotating-file sink and an in-engine overlay sink are planned follow-ups.

Math

Deterministic fixed-point math. Every function is computed in Q16.16 with plain integer arithmetic, so results are bit-identical on every platform and every run — the same guarantee the rest of the runtime gives. Arguments are positional. + +Given a float or double argument, the same functions compute in that type instead (using the platform's math library) and return it — floor, ceil and round included, which return an int only for fixed. sign returns an int either way. trunc and atan exist only for floats.

Math.minThe smaller of two values.Math.maxThe larger of two values.Math.absThe magnitude of a value, dropping its sign.Math.clampConstrain a value to the range [lo, hi].Math.signThe sign of a value as -1, 0, or 1.Math.floorRound a fixed value down to the nearest whole int.Math.ceilRound a fixed value up to the nearest whole int.Math.roundRound a fixed value to the nearest whole int.Math.lerpBlend between two values by a 0..1 amount.Math.inverse_lerpFind where a value sits between two endpoints as a 0..1 fraction.Math.remapRescale a value from one range into another.Math.sqrtThe square root of a fixed value.Math.sinSine of an angle in radians.Math.cosCosine of an angle in radians.Math.tanTangent of an angle in radians.Math.hypotLength of the vector (x, y).Math.distDistance between two points.Math.dist2Squared distance between two points.Math.deg_to_radConvert degrees to radians.Math.rad_to_degConvert radians to degrees.Math.posmodModulo that is always non-negative.Math.wrapWrap a value into the range [lo, hi).Math.ping_pongBounce a counter back and forth in [0, len].Math.snappedRound a value to the nearest multiple of step.Math.move_towardStep from one value toward another by at most delta.Math.smoothstepA smooth 0..1 ramp between two edges.Math.atan2The angle of the vector (x, y), in radians.Math.asinThe arcsine of a value, in radians.Math.acosThe arccosine of a value, in radians.Math.expe raised to the power x.Math.logThe natural logarithm of x.Math.powbase raised to a fixed exponent.

Memory

Raw memory — allocate byte and word buffers, copy and fill regions, and peek/poke individual bytes. The low-level escape hatch.

Mime

Content-type detection for assets and downloads: map a filename to its MIME type, or refine the guess by peeking at a file's leading bytes. Useful for deciding how to load a dropped-in mod file, labelling an export, or picking a decoder. + +of is a pure lookup by extension; sniff reads the first bytes and recognises a few well-known signatures (PNG, JPEG, GIF, PDF), falling back to the extension when nothing matches. The extension table is deliberately compact and documented; richer magic-byte coverage is a follow-up.

Network

The low-level networking seam — send and poll datagrams, serialize and apply entity state, and read ownership and role. Multiplayer is normally a compile-time property of the ECS, not glue you thread by hand: mark fields @Sync and models @Owned and the compiler generates the replication. These Network.* primitives are what that sugar lowers to, for when you drive the transport yourself. Offline they collapse to single-player defaults.

Noise

Procedural noise — the primitive that terrain, caves, biomes, textures, clouds, wind, and object placement are built on. Every generator is implemented in Q16.16 **fixed point** over an integer permutation hash seeded from an explicit seed, so a given seed reproduces the *exact* same field on every platform and every run: native, headless, and (later) wasm all agree bit-for-bit. That is a real edge over float-based engines, whose worlds can drift subtly across CPUs and break shared-seed multiplayer or replays. + +Coordinates are fixed values. The integer part of a coordinate selects a lattice cell and the fraction interpolates within it, so you scale feature size by sampling at a fractional *frequency* (e.g. multiply coordinates by 1/64). Outputs are fixed normalised to [-1, 1]; Noise.unit remaps that to [0, 1] when you want a height or a probability. + +Pick a generator by feel: value2 is cheap and blocky; perlin2 is the classic gradient noise; simplex2 is the organic default with fewer directional artifacts; fbm2 stacks octaves of simplex for natural, detailed fields; and cellular2 (Worley) gives Voronoi-cell structure for stone, cracks, and biome boundaries. Every sampler is a pure function of (x, y, seed) — no global state, no allocation — so it is safe to call across a whole worldgen pass or a per-pixel fill. + +Seed worldgen from its own seed (or a dedicated Random stream), kept separate from gameplay RNG, so generating the world never desyncs the simulation.

Os

The operating-system interface — the environment *around* the game rather than the game itself: the command line, environment variables, the standard streams, the process exit code, the host platform, and the per-user folders a game writes its saves, config, and cache into. It rounds the bare System builtins (arg, getenv, exit) into one coherent, Go-flavored namespace. + +It is deliberately small and game-scoped: there is no process spawning, no signals, and no user/permission APIs. Reach for it in launchers, asset pipelines, and dev tools — parsing launch flags like --level 3, reading config from the environment, or resolving where a save file belongs — far more than in the simulation itself. + +**Determinism:** args, the env family, and platform/arch are non-deterministic host input. Read them once at startup to configure the game, and keep them out of the replayable simulation so a shared seed still reproduces. + +**Platform coverage:** this first version targets the native macOS/BSD host — the fully supported target today. platform is portable (the uname system name is available on every Unix); arch and the known-folder layout (save_dir/config_dir/cache_dir) follow the macOS conventions. Linux/Windows/wasm folder resolution and a target-aware arch are documented follow-ups.

Path

Path manipulation as pure string operations — no disk access, no allocation surprises, just the joining and splitting every save system, mod loader, and asset pipeline needs. Separate a filename from its folder, get an extension, join segments without worrying about doubled or missing separators, and collapse ./../duplicate slashes into a canonical form. + +Paths use / as the separator, matching the native (macOS/BSD) filesystem — the fully supported target today; Windows-style separators are a follow-up. Pair these with Os (which supplies per-user directory *locations*) and Fs (which does the actual reading and writing).

Save

Whole-world save and restore — write a binary snapshot of the ECS world and read it back.

System

Low-level file and process access — raw file handles, reading a character, and running a shell command. Outside the deterministic simulation. For command-line arguments, environment variables, the exit code, and the standard streams, use the Os namespace.

Text

String queries and building over string values — length, character access, substrings, search, prefix/suffix tests, and int conversion. Arguments are positional. Strings are byte sequences, so indices and lengths count bytes.

Time

The frame clock. frame/elapsed/delta are deterministic (driven by a per-frame counter at a fixed 60 fps), so a replay reproduces every value; now is the wall clock and is explicitly non-deterministic.

Unicode

Correct-by-default text over UTF-8. Player names, translated menus, and chat arrive as UTF-8 bytes, and counting *bytes* gets non-ASCII text wrong — the wrong length, and truncation that slices a character in half into mojibake. The Unicode namespace works in code points and (approximately) grapheme clusters instead, so measuring, indexing, truncating, and case-mapping behave for every language. + +This is the correctness layer, not a replacement: the byte-oriented Text operations stay for speed on ASCII and for raw byte work. Reach for Unicode.* whenever the text came from a human — a name, a message, a localized string. + +Three notions of "length" matter, and the API keeps them distinct: byte_len (storage), len (code points — Unicode scalar values), and grapheme_len (user-perceived characters, where a base letter plus its combining accent, or a ZWJ emoji sequence, count as one). + +**Coverage (v1):** decoding and validation cover the full UTF-8 range. Case mapping covers ASCII and the Latin-1 letters — correct for Western-European text; wider scripts (Latin-Extended, Greek, Cyrillic), locale rules (Turkish i, German ß), and NFC normalization are follow-ups. grapheme_len approximates UAX#29 for the cases real player text hits — combining marks, variation selectors, ZWJ sequences, and flag pairs.

Uuid

Universally-unique identifiers — stable IDs that don't collide, generated locally with no central authority handing out numbers. Reach for a UUID whenever something needs an identity that survives being saved, shared, or sent over a network: players and sessions in multiplayer, user-created content (levels, items, mods) that has to merge cleanly across installs, or a per-install / per-run ID for analytics and bug reports. + +Two versions are provided. Uuid.new makes a **v4** (random) UUID — 122 bits of entropy, effectively never colliding. Uuid.new_v7 makes a **v7** (time-ordered) UUID whose leading bits are a millisecond timestamp, so a batch of v7 IDs sorts by creation time — friendly to database indexes and append logs. Both set the RFC 4122 version and variant bits correctly. + +A UUID is represented as its canonical lowercase 36-character text form (8-4-4-4-12), the same shape you store, print, send, and compare — so there is no conversion at each boundary. Validate untrusted input with Uuid.is_valid or normalise it with Uuid.parse, and compare with Uuid.equals, which ignores case. + +**Determinism caveat.** v4 and the random tail of v7 come from the OS cryptographically-secure RNG (Crypto), which is non-deterministic by design. Minting a UUID inside the lockstep simulation will desync replays and networked peers — generate IDs at the edges (on connect, on save, on spawn-from-input), never per tick in reproducible gameplay code.

World

Runtime reflection over the ECS world — read and write component fields by id, spawn and inspect entities, and register components at runtime. The introspection layer a mod uses.

Audio

Sound effects and music. A sound is loaded once with Audio.load into a handle; Audio.play fires it one-shot and Audio.play_music loops it on a single music channel. Playback is out-of-band — the audio device is real-time, not part of the deterministic simulation — but every trigger here is an ordinary frame-driven call, so a recorded run replays the same sounds at the same frames. Headless builds carry the whole API as no-ops (no audio device needed), so the same game code runs under the test harness.

Random

A seeded, deterministic RNG — same seed, same sequence, every run and every platform.

Vector

2D vector math for positions, velocities, and directions. A Vector is a pair of Q16.16 fixed components packed into one value, so it is copied by value and never allocates. Every operation is deterministic integer fixed-point — bit-identical on every platform, the same guarantee the rest of the runtime gives. Arguments are positional; angles are in radians.

Http

A poll-based HTTP / HTTPS client for out-of-band data — leaderboards, cloud saves, remote config, telemetry, downloads. Open a request with Http.get / Http.post (or Http.open to add headers first), then each frame call Http.poll — it returns -1 while pending, 0 on error, or the status code — so the frame never blocks. Read the reply with Http.status / ok / text / header, and pair it with Json.* for (de)serialization. TLS is the system's, on by default. A large download goes to a file instead of memory with Http.save_to, and Http.received / expected drive a progress bar while it is pending. + +HTTP depends on the network and the wall clock, so — like Net.* and Time.now — it is out-of-band and must never feed the deterministic lockstep/replay simulation. The transport is NSURLConnection / NSURLSession on macOS and WinHTTP on Windows; the raw-response parser (Http.parse) is pure and portable.

IVec2

Integer 2D vector math for tile and grid coordinates, cell offsets, and integer sizes. An IVec2 is a pair of whole-number int components (x, y) packed into one value, so it is copied by value and never allocates. Every operation is exact integer arithmetic — no rounding, and bit-identical on every platform. Use it wherever a fractional part would be meaningless; reach for Vector when you need sub-pixel precision. Arguments are positional.

Map — tilemap

A character grid the game paints and reads.

Rect

Axis-aligned rectangles for HUD layout boxes, hitboxes, and camera regions. A Rect is four Q16.16 fixed components — position (x, y) (its top-left corner) and size (w, h) — packed into a single value that is copied by value and never allocates. It offers fast point-in-rect and rectangle-overlap tests. Every operation is deterministic fixed-point, bit-identical on every platform. Arguments are positional.

Udp

Plain IPv4 datagrams, polled — the transport under a game's own netcode: peer-to-peer play, a LAN lobby, a STUN query. Open a socket with Udp.open, send with Udp.send, and each frame drain what arrived with Udp.recv, which returns 0 when nothing is waiting and never blocks. The sender of the last datagram read is Udp.from_ip / from_port. + +An address is an int: a.b.c.d is (a << 24) | (b << 16) | (c << 8) | d, converted by Udp.ip and Udp.ip_text. Datagrams can be lost, repeated and reordered; the protocol a game builds on top says what to do about that. Like Http.* it is out-of-band and must never feed the deterministic lockstep/replay simulation directly. BSD sockets on macOS, Winsock on Windows.

BigInt

Arbitrary-precision integers with no upper bound, for idle/incremental counters, exact huge currencies, and score arithmetic that a 32- or 64-bit int would overflow. A BigInt is a sign-magnitude number stored as base-1e9 limbs, so every operation is exact and therefore deterministic — bit-identical on every platform, with no binary floating point. Build one with BigInt.from (an int) or BigInt.parse (decimal text), combine with add / sub / mul / pow / div / mod, compare with cmp / eq / is_zero, and render with str. Arguments are positional. The runtime is spliced in only when a program mentions BigInt.*.

Color — the named palette

Color.Name lowers to a plain 0xRRGGBB integer at compile time — no runtime cost, identical to writing the hex by hand, but readable. 221 names are built in; a custom shade is any hex literal or a named const. The full palette:

Process

Child processes, started and polled — what a launcher is made of. Process.spawn starts a program directly, with no shell, and returns at once; each frame Process.poll answers -1 while the child runs and its exit code once it has ended, so a crash is a non-zero code the game can act on. Process.kill ends a child and Process.free lets a handle go. Pair it with App.window_hide to step aside while the child runs. + +The child inherits the environment and the working directory. posix_spawn on macOS; CreateProcessW on Windows, with each argument quoted by the MSVC rules and no console window. Like Http.* it is out-of-band and must never feed the deterministic lockstep/replay simulation.

Decimal

Exact base-10 fixed-point numbers for game economies — prices, balances and taxes on values like 0.10 that binary floating point cannot represent, so they always add up exactly. A Decimal is a BigInt mantissa with a decimal scale (the number of digits after the point), giving unbounded range and exact add / sub / mul. Build one with Decimal.from (an int) or Decimal.parse (text like "19.99"), compare with cmp / eq, change precision with rescale (truncates toward zero), read the current precision with scale, and render with str. Every operation is exact and deterministic. The runtime is spliced in only when a program mentions Decimal.* (or BigInt.*).

Types

Ludic is statically typed; most code uses just int.

Dict

A hash map from string keys to int values — the everyday lookup a game needs, like resource counts or an id registry by name. A Dict is backed by an open-addressing hash table (FNV-1a, linear probing, tombstone deletes, growing at load factor 0.7), so get/set/has are O(1) on average rather than the linear scan a list would give. Create one with Dict.new, then set / get / get_or / has / remove / size / clear / keys. Values are int (which also holds an entity or any small id); for richer values use the Value.* tree. Arguments are positional. The runtime is spliced in only when a program mentions Dict.* (or Set.*).

Operators & tokens

The symbols the grammar recognizes.

Builtin functions

Global functions available anywhere, beyond the namespaced APIs above.

printPrint an int or string followed by a newline — for headless tests and debugging.stringConvert an int, bool, or fixed value to text; a string passes through unchanged.quitStop the game loop cleanly after the current frame finishes.saveSerialize the whole world — every model instance, property, and program var — in one call.loadRestore the world from a snapshot previously written by save().lenThe number of elements in a slice, or the byte length of a string.pushAppend one element to the end of a growable slice.absThe absolute value of an integer (its magnitude, never negative).clampConstrain a value to the inclusive range [lo, hi].panicAbort with a located error message instead of crashing.assertAbort with a located message when an invariant is false.okWrap a success payload in a result — the happy half of try/else.errWrap a failure message in a result — the sad half, recovered by try/else.is_okTrue when a result carries a success payload.is_errTrue when a result carries a failure.someWrap a present value in an option.noneThe empty option — a missing value.is_someTrue when an option holds a value.is_noneTrue when an option is empty.unwrap_orThe option value, or a fallback if empty.exitTerminate the process immediately with a status code.runRun a string as a shell command.getenvRead an environment variable, returning empty when it is unset.read_charRead one byte from standard input, or -1 at end of input.argThe i-th command-line argument as a string.arg_countThe number of command-line arguments, counting the program name.bytesAllocate a raw buffer of n bytes and return a pointer to it.file_stderrThe standard-error stream handle for use with file_write.file_stdoutThe standard-output stream handle for use with file_write.file_writeWrite a run of raw bytes to a stream handle.fixedConvert an int (or a float) into a Q16.16 fixed-point value.floorFloor a fixed-point value down to the nearest integer.maxThe larger of two integers.minThe smaller of two integers.ui_buildBuild the declared UI tree so it can be opened and rendered.wordsAllocate a raw buffer of n 32-bit words, indexable with [i].floatConvert any number to a float (or double).intConvert a number to a whole number, truncating toward zero.float_bitsA float's IEEE bit pattern as an int, and back.

Set

A set of string members — membership tests for tags, unlocked achievements, or visited tiles. A Set shares the same open-addressing hash table as Dict, so add / has / remove are O(1) on average and duplicates are ignored. Create one with Set.new, then add / has / remove / size / clear / members. Arguments are positional. The runtime is spliced in only when a program mentions Set.* (or Dict.*).

Annotations

@Name(...) decorators attach compile-time behavior to a handler, property, or function.

@QueriesDeclare the properties a handler operates on, binding their fields by name in the body.@ComputedA derived field expanded inline wherever it is read, never stored.@OnRegister a compile-time listener that runs whenever an event is emitted.@exportExpose a function as a native symbol so a host can call it.@SyncMark fields as replicated and models as owned so the compiler generates networking.@HandlesDeclarative hint naming the event or subsystem a handler is responsible for.@OnAttachRun a handler when a property is structurally attached to a live instance.@OnDespawnRun a handler when a model instance is torn down, optionally knowing why.@OnDetachRun a handler when a property is structurally detached from a live instance.@OnDisableRun a handler when a property is paused (disabled) on an instance.@OnEnableRun a handler when a paused property is re-enabled on an instance.@OnQuitRun a handler once at shutdown, after the last frame.@OnSpawnRun a handler once each time an instance of a model is spawned.@OnStartRun a handler once at boot instead of assigning it a frame phase.@OwnedGive a model a network owner slot so its instances can be assigned to a peer.@PredictedRun a control handler on the owning client speculatively and on the server authoritatively.@PublicPromote a lifecycle hook to a public event other modules can listen for.@ReadsDeclare that a handler reads a property — an analysis and scheduling hint.@ServerRun a handler only on the authority; clients receive the result via @Sync.@ToClientsA remote event broadcast from the server to clients — a server→clients notification.@ToServerA remote event sent from a client to the server — a client→server request.@WritesDeclare that a handler writes a property — an analysis and scheduling hint.@EngineSystemRegister a package's engine-owned system so the frame loop runs it each phase.@NamespaceLet a package provide a Name.method(…) namespace that dispatches to name_method."@ClearColor"Declare a clear colour so the Render phase auto-clears + auto-presents for you.@SystemRegister a prebuilt binary module's system with the host at load, per phase.

Huge

Idle/incremental big numbers — magnitudes far past what a 32- or 64-bit integer can hold, for prestige currencies and exponential growth. A Huge is a normalized mantissa (a Q16.16 fixed in [1, 10)) times 10^exponent, so it can represent 10^100 or 10^1000 while staying one small value. It is a display-scale number (about four significant digits), not a lockstep-exact one — keep it out of the deterministic simulation and reach for BigInt / Decimal when exactness matters. Build one with Huge.from, combine with add / sub / mul / neg, compare with cmp / sign, read mantissa / exp, and render as 1.23e45 with str. Arguments are positional. Spliced in only when a program mentions Huge.*.

Angle

An auto-wrapping angle in radians, so you never juggle % TAU by hand. Every Angle.* result is normalized into [-pi, pi), which makes diff the shortest signed rotation between two headings and lerp turn the short way around. Build from degrees with Angle.from_degrees (read back with to_degrees), take sin / cos, combine with add, and normalize any raw value with wrap. It builds on the deterministic fixed-point Math.* trig, so results are bit-identical on every platform. Arguments are positional; angles are fixed radians. Spliced in only when a program mentions Angle.*.

Percent

A value clamped to [0, 1] — health fractions, volumes, and the t of an interpolation, without stray values slipping below 0 or above 1. Percent.clamp pins any fixed into range; Percent.of forms a clamped ratio num / den; Percent.lerp interpolates a..b by a clamped t; and Percent.apply scales a value by a clamped fraction. Every operation is deterministic fixed-point. Arguments are positional. Spliced in only when a program mentions Percent.*.

Regex

Regular expressions with PCRE/PECL-compatible syntax over a **linear-time** Thompson NFA (a Pike VM), so a bad pattern from a modder can never trigger catastrophic backtracking — matching is O(n·m), never exponential. Compile a pattern once and reuse it; an invalid pattern is an error value, never a crash. Supported: literals, ., classes [...] with ranges/negation and \d \w \s (and their negations), anchors ^ $, alternation |, capturing and (?:…) groups, and the quantifiers * + ? and {n,m} in greedy or lazy form. Backreferences and look-around are out of scope for a linear engine.

Grid

Tile geometry and pathfinding over the Map tilemap. A cell is passable unless it is out of bounds or holds the caller's wall tile (a char code, e.g. '#'), so any impassable glyph works. Everything is integer and deterministic — same map and query reproduce the same path every run. Grid.line/Grid.flood/Grid.a_star return Cell slices (index them with len / [i]; each cell has .x and .y).

Anim

Spritesheet frame animation off the fixed frame clock. Store an elapsed timer (seconds, a fixed) on a component and each frame ask Anim.frame / Anim.once / Anim.pingpong which cell to draw; Anim.cell_x/Anim.cell_y turn a frame index into a source rectangle on the sheet. Everything is integer/fixed and deterministic — the same timer reproduces the same frame every run, so replays and lockstep netcode match exactly. For the ECS engine-owned SpriteAnim component there is also an ergonomic layer: register named clips with Anim.clip and play them by name with Anim.play, and arm frame events with Anim.on_frame / Anim.fired.

Motion

Ergonomic control of the engine-owned Motion component — the value tween the engine advances each tick (see the ECS engine-systems). Motion.to starts a tween on an entity from one value to another over a number of ticks with an easing curve, rewinding it so it plays from the start, instead of setting the component's from/to/dur/ease fields by hand. The tween is integer and deterministic, so motion reproduces exactly under replay and lockstep. For a standalone, sequenced tween not tied to a component, see the fluent Tween handles. Related: Anim, Ease, Tween.

Tween

Value interpolation over a timeline, off the fixed frame clock. The timeline helpers Tween.progress/Tween.loop/Tween.yoyo turn an elapsed timer and a duration into a normalized amount; Tween.ease shapes that amount through an easing curve (shared with Ease); and the typed blends Tween.number/Tween.round/Tween.point/Tween.tint interpolate a fixed, int, Vector, or color. All deterministic fixed-point, so a replay reproduces every eased value exactly. Beyond these pure interpolators there are fluent handles the engine advances for you: Tween.to starts one and returns a handle, Tween.chain and Tween.delay sequence more segments, and Tween.value / Tween.done / Tween.parallel / Tween.stop read and control them.

Query

Spatial and set queries over the live entities that carry a property, built on the World reflection ABI. Query.count and Query.first ask how many bearers there are and which is first; Query.nearest and Query.within add a position — read from a coordinate property's two int fields — to find the closest entity to a point or every entity inside a radius. A property id comes from World.prop_id and a field id from World.field_id. Everything is integer and deterministic: the same world reproduces the same answers, entity order included, every run. The scan is linear over the entity table — ample for the entity counts Ludic targets — and a game that uses Query.* gets the reflection ABI emitted automatically, no @event required.

Reflect

Runtime reflection over the world schema — inspect properties, fields, and entity state by name and by index. It powers the conveniences game devs consume without writing reflection code: automatic save/load, data-driven tools, and debug/inspector overlays. Enumerate with Reflect.prop_count/Reflect.prop_name and Reflect.field_count/Reflect.field_name/Reflect.field_type; resolve ids with Reflect.prop/Reflect.field; read and write an entity's fields with Reflect.get/Reflect.set/Reflect.has; and identify its model with Reflect.kind/Reflect.model. The metadata tables are generated at compile time (the same reflection ABI a foreign mod binds), so introspection is a table walk, not heavy runtime machinery. This is an advanced/tooling surface — most developers get its benefits through built-in features. Related: World, Query.

Camera

A world-space camera — a draw offset threaded through the render path (the same offset Screen.camera sets). Camera.set places it, Camera.follow eases it toward a target, and Camera.shake jitters it from the seeded RNG for impact and explosions. Everything is integer and deterministic — driven off the same seed and inputs, a replay reproduces the exact camera path, shake included. The camera moves everything drawn; reset it to (0, 0) to draw a fixed HUD.

Light

A software 2D light-accumulation pass over the framebuffer, run in a render phase after drawing the scene and before Screen.show. Light.ambient multiplies the whole scene toward a tint — the night/cave modulate that darkens everything so lights add mood back on top. Light.point accumulates a radial glow that falls off with distance and clamps per channel, and Light.occlude registers rectangles that block a light's rays to cast hard shadows (cleared each frame with Light.clear_occluders). Lighting is a rendering concern only — it never touches game state or replays — and it is fully deterministic (integer and Q16.16 fixed), so the same scene lights identically every run and in a headless render, keeping screenshots diffable. Colours are 0x00RRGGBB. Beyond the radial core, the pass carries the render-quality tiers: Light.spot cones, a Light.falloff exponent, Light.soft shadows (penumbra), Light.gel colour cookies, normal-mapped surfaces (Light.normal + Light.height) that shade by facing, and a Light.time_of_day day/night ramp — every one deterministic. This is the imperative surface; the engine also consumes Light2D/Occluder components automatically (a Light2D may carry optional direction/spread/falloff/softness/gel fields). Related: Screen, Color, Camera.

Sprite

A namespaced spritesheet / atlas API. Sprite.sheet loads one image and remembers its cell grid; Sprite.cell addresses a cell by grid coords and Sprite.cell_span a sprite that spans more than one cell; Sprite.define / Sprite.named name and look up a cell; Sprite.draw / Sprite.draw_scaled blit it (through the camera / zoom / clip). It stands on the variable-size image loader, so a cell can be any size — not just 16x16. (Distinct from the Sprite engine component of the sprite-render system.)

Assets

Loading and lookup for whole-file assets, paired with the Sprite.* atlas API. Assets.image (and its alias Assets.load) loads a whole image file as one sprite; Assets.get looks a named sprite up.

Testing

A built-in testing framework in the spirit of Go's go test: tests live next to the code, run with one command, and report pass/fail — no harness to wire up. A test "name" { … } block is discovered automatically and run by a synthetic entry point that prints ok - name or FAIL - name for each, a == N passed, M failed == summary, and exits non-zero if anything failed (so CI and the bin/ludic runner catch it). Inside a test, the expect, expect_eq and expect_near assertions check a condition and, on failure, print file:line: … failed (got …, want …) and mark the test failed — without aborting, so one run reports every failure. expect_near takes a tolerance, which is what fixed-point and accumulated-integer game math need. Everything is deterministic and compiles to a native binary, so a suite runs in the same C-free toolchain as the rest of Ludic. Coverage instrumentation is a planned follow-up. Related: function, print.

Pool

Entity-pool statistics. Ludic's ECS is already pool-based: the allocator recycles freed entity slots through a freelist (a despawned slot is reused by the next spawn before any new slot is taken), and component storage is fixed per-entity arrays — so spawning and despawning many entities per frame does no per-spawn heap allocation and cannot fragment. Pool.live / Pool.free / Pool.reserved / Pool.capacity read those counters so a bullet-hell or horde game can watch reuse and budget against the cap.

Value

A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through. A node is one of null, int, fixed, bool, str, list, or object (see Value.kind). Build one with the constructors (Value.int, Value.object, …) and the builders Value.add/Value.put; read it with Value.get/Value.at/Value.count and the as_* accessors. Related: Json, Reflect.

Json

The text bridge over the Value tree: Json.encode turns a value tree into compact, stable JSON text and Json.parse reads it back. Together with Reflect.serialize/Reflect.apply this is a one-call, bit-exact save/load for entities, and a diffable on-disk format for tooling. Determinism holds: the same tree always encodes to the same bytes.

Job

Background work that stays out of the frame. A Job is a future — a handle to a result that lands later. Kick one off with Job.run (a background compute that advances a little each Job.pump and finishes after enough frames, so heavy work never hitches) or Job.defer (a future you resolve yourself with Job.fulfill / Job.fail). Poll it with done / ok / failed / cancelled, read result / error, and always collect on the main thread — a Job must never touch the ECS world directly. The scheduler is deterministic and cooperative, so the same jobs and the same budget reproduce byte-for-byte, every run and every target. For work that should use every core now, Job.parallel_for(count, fn work, ctx) runs work(i, ctx) across a pool of real OS threads and returns when all of it is done; a worker computes on what it was handed and never changes the world. Arguments are positional. Spliced in only when a program mentions Job.*.

Promise

Combine several Job futures and resolve the group on the main thread. Promise.all succeeds once every member has, Promise.race once the first does; both return an ordinary job handle you poll like any other. For a loading screen, Promise.count_done over the same handles is the bar's numerator and len the denominator, and Promise.all_done is the ready check. Ludic has no closures, so progress is polled rather than chained through a then callback. Build the handle list with new []int + push. Spliced in only when a program mentions Promise.*.

Sync

The advanced, opt-in tier — here be dragons. Raw building blocks for engine-level systems that pass data around: a mutex (cooperative lock), an atomic counter (get / set / add / cas) and a bounded channel (send / recv / can_recv / len). On today's single-threaded deterministic runtime these are cooperative — correct, ordered, replayable and impossible to deadlock — and exist so message-passing code reads the same now as it will when a preemptive OS-thread backend lands behind this same API. Beginners never need this; reach for Job.* / Promise.* instead. Spliced in only when a program mentions Sync.*.

Xml

A minimal, deterministic XML reader for the element/attribute/CDATA subset the native Tiled formats (TMX/TSX/TX) use. Xml.parse turns a document into an element tree; the accessors read a node's tag, text, attributes (Xml.attr/Xml.attr_int/Xml.has) and children (Xml.child/Xml.find/Xml.count). It expands the five predefined entities and numeric character references, skips the <?xml?> prolog, comments and <!DOCTYPE>, and is best-effort rather than validating — the same contract as Json. Spliced on demand when a program mentions Xml.*.

Base64

Standard base64 (RFC 4648) — Base64.decode reads base64 text into the bytes it stands for, and Base64.encode is the inverse. The decoder ignores ASCII whitespace, so it reads the newline-wrapped base64 that Tiled writes inside a <data> element; that output then feeds the DEFLATE inflater for zlib/gzip-compressed layer data. Deterministic; spliced on demand when a program mentions Base64.*. (The security-sensitive encoder Crypto.base64 is a separate, hardened path.)

Tiled

Load and draw Tiled maps. Both native format families — TMX/TSX/TX (XML) and TMJ/TSJ/TJ (JSON) — read onto one intermediate, a Value tree in Tiled's JSON schema, with layer data decoded to a dense GID list; so a CSV .tmx and a base64+zlib .tmj of the same map read identically. Tiled.read returns that intermediate tree for a map file and Tiled.read_tsx for a tileset file. Spliced on demand when a program mentions Tiled.*, alongside the Xml, Base64 and Value runtimes it builds on.

Tiled.readRead a map file into the intermediate value tree.Tiled.read_tsxRead a tileset file into a value object.Tiled.loadLoad a map file into the runtime map model.Tiled.gidThe raw GID at a cell in a tile layer.Tiled.resolveDecode a GID into tileset, local id and flip flags.Tiled.widthThe map width in tiles.Tiled.heightThe map height in tiles.Tiled.layer_countHow many layers the map has.Tiled.layer_nameA layer's name by index.Tiled.drawDraw every visible tile layer.Tiled.projectProject a collision layer to the byte tilemap.Tiled.treeThe underlying intermediate value tree.Tiled.tile_propWhether a GID's tile carries a bool property.Tiled.collideDrive collision from per-tile metadata.Tiled.collision_kindClassify a GID's collision from its tile metadata.Tiled.tile_shapesWhether a GID's tile has a collision shape.Tiled.draw_animDraw with animated tiles advanced to a frame.Tiled.frame_gidThe current GID of an animated tile at a frame.Tiled.animatedWhether a GID's tile is animated.Tiled.object_countHow many objects an object layer holds.Tiled.objectAn object on an object layer by index.Tiled.object_shapeClassify an object's shape.Tiled.propA custom property's value, with class-default fallback.Tiled.prop_intA custom property parsed as an integer.Tiled.prop_typeA custom property's declared type.Tiled.load_typesLoad a project custom-type table.Tiled.templateRead a template file into an object value.Tiled.spawnSpawn a Ludic entity from an object.Tiled.spawn_layerSpawn every object on a layer.Tiled.cell_xThe screen x of a tile cell for the map orientation.Tiled.cell_yThe screen y of a tile cell for the map orientation.Tiled.layer_kindA layer's kind.Tiled.layer_opacityA layer's opacity.Tiled.layer_tintA layer's tint colour.Tiled.layer_offsetxA layer's horizontal offset.Tiled.layer_offsetyA layer's vertical offset.Tiled.worldRead a .world file.Tiled.world_countHow many maps a world stitches.Tiled.world_mapA world member map by index.

Ui

The retained-mode menu API over a ui block. Ui.build constructs every declared widget tree once (after fonts and skins are loaded); Ui.open makes one menu active and Ui.close deactivates it; the frame loop ticks navigation on its own, an activation fires the UiClicked event, and Ui.clicked is the polled form; Ui.set_text updates a label or button, and Ui.render draws the active menu (call it from an Overlay handler so it paints over the world). Every id: Name in a ui block mints a UI_Name handle.

File

Raw file access over the C stdio calls, for programs that read and write their own formats. File.open returns a handle (or null), File.read / File.write move bytes through a bytes buffer, File.seek / File.tell position the cursor, and File.close releases the handle. For text and structured data prefer the higher-level Fs, Json and Prefs APIs.

Font

TrueType fonts for the retained UI and for Screen.draw_text-style drawing. Font.load reads a .ttf / .ttc file and returns a handle that a ui block's font: property or a text call takes.

Fx

Engine-owned transient effects. A game asks for a burst of sparks or a floating number and the engine owns the rest: it moves and ages them every Update, draws them every Render after the sprites (through the camera, shake and clip), and drops them when they expire. Nothing is an entity, so a hit effect needs no component, model, handler or draw call. Velocities and lifetimes come from the seeded RNG, so a replay produces the same sparks.

Prefab

A prefab is a model with preset component fields — prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } } — spawned with spawn Grunt { Position { x: 40 } }, where the spawn's own fields override the presets. Prefabs chain (prefab Grunt: Foe, where Foe is itself a prefab) so shared presets live once. spawn is also an expression yielding the new entity (let e = spawn Grunt { … }), and Prefab.spawn spawns a prefab chosen at runtime by name.

App

The running application, as distinct from its window. Today that is the boot splash: the runtime raises it before main from the game's asset pack, and App.splash_hide takes it down when the game has something to show instead. App.window_hide and App.window_show take the game's own window off the screen and bring it back without closing it — a launcher stepping aside while the game it started runs.

Gl

OpenGL for Ludic. Gl.* binds the whole OpenGL 4.1 core API: every gl* entry point of the platform gl3.h is a method named by its snake case (glBindBuffer → Gl.bind_buffer, glTexImage2D → Gl.tex_image2d, glDrawElementsInstanced → Gl.draw_elements_instanced), with every GL_* constant available as written. Parameters keep the header's names as labels (glVertexAttribPointer's pointer is called offset, program is prog); GLfloat/GLdouble parameters take fixed, pointer parameters take the raw bytes/words buffers Ludic already has, and 64-bit sizes take long (an int widens). The binding is generated by ludic-dev glgen from the header, with one ABI thunk per entry point. + +A context comes from Gl.open(width, height, title): windowed, an NSOpenGLContext on the game's window at the display's backing resolution (2× on Retina — Gl.width()/Gl.height() are the drawable's pixels); headless, an offscreen context with a framebuffer standing in for the screen (Gl.screen_fbo()), so the same program renders and screenshots under the test harness. Gl.swap() presents, Gl.screenshot(path) writes the screen as a PPM, Gl.check(tag) prints any pending error. + +The glue: Gl.program(vs, fs) / Gl.program5(vs, tcs, tes, gs, fs) compile and link (logs on failure), Gl.uniform(prog, name), Gl.vao(), Gl.buffer(), Gl.texture(), Gl.framebuffer() allocate, and float data is filled from Q16.16 with Gl.floats(n) / Gl.put(buf, i, v) / Gl.bytes_of(n) or from IEEE bits with Gl.put_bits; Gl.f32(v) and Gl.fixed(bits) convert single values. The bare f_add/f_mul/… helpers do IEEE single-precision arithmetic on those bit patterns for programs that want real floats on the CPU. + +Using Gl.* links gl.ll, the thunks and OpenGL.framework; a program that does not is byte-identical to before. The ludic.render3d package is a physically based 3D renderer written on this surface (see examples/rendering/smooth.ludic here, and Maroon Lake for a game built on it). + +``ludic +program Triangle { + property Marker { on: int = 1 } + model Anchor { Marker } + var prog: int = 0 + var vao: int = 0 + handler Boot phase Start { + spawn Anchor {} + if not Gl.open(width: 640, height: 360, title: "GL") { quit() } + prog = Gl.program(vs: "#version 410 core\nvoid main(){ gl_Position = vec4(float(gl_VertexID == 1) * 2.0 - 0.5, float(gl_VertexID == 2) * 2.0 - 0.5, 0.0, 1.0); }\n", + fs: "#version 410 core\nout vec4 o; void main(){ o = vec4(1.0, 0.5, 0.2, 1.0); }\n") + vao = Gl.vao() + } + handler Draw phase Render { + Gl.clear_color(red: 0.1, green: 0.1, blue: 0.15, alpha: 1.0) + Gl.clear(mask: GL_COLOR_BUFFER_BIT) + Gl.use_program(prog: prog) + Gl.bind_vertex_array(array: vao) + Gl.draw_arrays(mode: GL_TRIANGLES, first: 0, count: 3) + Gl.swap() + } +} +``

Vk

Vulkan for Ludic. Vk.* binds Vulkan 1.0–1.4 and the extensions a modern renderer is built around — swapchain and HDR colour spaces, ray query and acceleration structures, opacity micromaps, mesh shaders, variable rate shading, memory budget, pipeline libraries, NVIDIA low latency, and portability enumeration for MoltenVK. Every command is a method named by its snake case (vkCreateInstance → Vk.create_instance, vkCmdDrawIndexedIndirectCount → Vk.cmd_draw_indexed_indirect_count), every VK_* constant is available as written, and every struct has its size and field offsets as constants: VkDeviceCreateInfo_sizeof, VkDeviceCreateInfo_queueCreateInfoCount. The binding is generated by ludic-dev vkgen from the Vulkan registry (vk.xml), and every size and offset is checked against the SDK's C headers. + +A struct is plain memory filled by field name: bytes(Vk…_sizeof), Vk.zero(p, n), then Vk.put_i32 / Vk.put_i64 / Vk.put_ptr(p, offset, value), read back with Vk.get_i32 / get_i64 / get_ptr, and Vk.at(p, offset) for a nested struct or an inline array. Numbers follow the C types: a float is its IEEE bit pattern in an int, and uint64_t, VkDeviceSize and non-dispatchable handles are long. A negative long has to come from a long variable — an int literal passed straight to a long parameter is zero-extended. + +The loader is opened at run time by Vk.open(), never linked: vulkan-1.dll on Windows, libvulkan.1.dylib (the LunarG loader and MoltenVK) on macOS — beside the executable, in the library paths, or under $VULKAN_SDK. It returns 0 on a machine without Vulkan, and the program carries on; Vk.has(name) says whether one command is there. Using Vk.* links the generated thunks and the loader; a program that does not is unchanged. See examples/rendering/vk_probe.ludic (what a machine's Vulkan can do) and vk_compute.ludic (a Slang compute shader, dispatched and read back). + +``ludic +program Probe { + property Marker { on: int = 1 } + model Anchor { Marker } + handler Boot phase Start { + spawn Anchor {} + if Vk.open() == 0 { print("no Vulkan here"); quit() } + let app = bytes(VkApplicationInfo_sizeof) + Vk.zero(app, VkApplicationInfo_sizeof) + Vk.put_i32(app, VkApplicationInfo_sType, VK_STRUCTURE_TYPE_APPLICATION_INFO) + Vk.put_i32(app, VkApplicationInfo_apiVersion, (1 << 22) | (3 << 12)) + let ci = bytes(VkInstanceCreateInfo_sizeof) + Vk.zero(ci, VkInstanceCreateInfo_sizeof) + Vk.put_i32(ci, VkInstanceCreateInfo_sType, VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) + Vk.put_ptr(ci, VkInstanceCreateInfo_pApplicationInfo, app) + let out = bytes(8) + if Vk.create_instance(ci, null, out) == VK_SUCCESS { + print("a Vulkan instance") + Vk.destroy_instance(Vk.get_ptr(out, 0), null) + } + quit() + } +} +``

Phases

+
+ + + \ No newline at end of file diff --git a/app-set_icon.html b/app-set_icon.html new file mode 100644 index 00000000..4e3f1126 --- /dev/null +++ b/app-set_icon.html @@ -0,0 +1,32 @@ + + + + + +App.set_icon — Ludic + + + + + + +
+
API Reference › App › App.set_icon
+
+ method +

App.set_icon

+
+ App.set_icon(path) -> void +

Sets the application's icon — the Dock tile, the Cmd-Tab switcher, the About box — from an image the game ships.

A **bundled** app already has one: ludic bundle builds AppIcon.icns from the manifest's app icon and macOS reads it from the bundle, so calling this there is harmless and changes nothing you can see. A **plain binary** is the case this exists for. ludic build produces an executable with no bundle around it, so macOS has nowhere to find an icon and draws the generic one — which is what a developer looks at every time they run the game.

The path is resolved the same way every other asset is, through the pack first and the filesystem second, so the same call works in a packed build and in a project directory. An image that cannot be found or decoded leaves the current icon alone rather than clearing it.

Headless builds have no AppKit, so this compiles to nothing there.

+

Parameters

pathan image the game ships (PNG, JPEG or TIFF); 1024x1024 is the size macOS wants
+

Example

program Titled {
+  entry {
+    App.set_icon("assets/app/icon.png")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/app-splash_hide.html b/app-splash_hide.html new file mode 100644 index 00000000..ed609098 --- /dev/null +++ b/app-splash_hide.html @@ -0,0 +1,37 @@ + + + + + +App.splash_hide — Ludic + + + + + + +
+
API Reference › App › App.splash_hide
+
+ method +

App.splash_hide

+
+ App.splash_hide() -> void +

Dismisses the boot splash. Safe to call when there is no splash, and safe to call twice.

A bundled game shows a splash while it starts: ludic bundle records app splash from the manifest into the bundle, and the runtime puts it on screen before main runs, out of the asset pack. Nothing takes it down on its own, because nothing else knows when the game has a first frame worth showing - a splash that disappears while the terrain is still loading leaves the same black gap it was there to cover.

So the game says when:

``ludic skip +program MyGame { + entry { + load_the_world() # the slow part the splash is covering + open_the_window() + draw_one_frame() + App.splash_hide() # ...and only now + } +} +``

Headless there is no splash and no window, and the call lowers to nothing.

See also: shipping a game.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/app-window_hide.html b/app-window_hide.html new file mode 100644 index 00000000..e6c2b35e --- /dev/null +++ b/app-window_hide.html @@ -0,0 +1,43 @@ + + + + + +App.window_hide — Ludic + + + + + + +
+
API Reference › App › App.window_hide
+
+ method +

App.window_hide

+
+ App.window_hide() -> void +

Takes the game's window off the screen without closing it: its GL context or Vulkan surface, its size and its contents are kept, and the program goes on running — a hidden window does not end the run. App.window_show brings it back. It is how a launcher steps aside while the game it started with Process.spawn runs.

Keep the frame loop going while hidden so the child can be polled, but there is nothing to see: skip the drawing, and throttle the loop yourself — a hidden window is not paced by the display, so an unthrottled loop spins. It does not touch the boot splash (App.splash_hide does). Safe to call when there is no window yet, and twice. Headless there is no window and the call lowers to nothing.

``ludic skip +program Launcher { + var game: int = -1 + function play() -> void { + game = Process.spawn(game_path(), new []string) + if game >= 0 { App.window_hide() } + } + handler Watch phase Update { + if game >= 0 and Process.poll(game) != -1 { + Process.free(game) + game = -1 + App.window_show() + } + } +} +``

orderOut: on macOS; ShowWindow(SW_HIDE) on Windows, which also forgets any key held as the window went.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/app-window_show.html b/app-window_show.html new file mode 100644 index 00000000..343ac581 --- /dev/null +++ b/app-window_show.html @@ -0,0 +1,36 @@ + + + + + +App.window_show — Ludic + + + + + + +
+
API Reference › App › App.window_show
+
+ method +

App.window_show

+
+ App.window_show() -> void +

Puts a window hidden by App.window_hide back on screen, in front and focused, as it was. Safe to call on a window that is already showing, when there is no window, and headless, where it lowers to nothing.

``ludic skip +program Demo { + entry { + App.window_hide() + wait_for_the_game() + App.window_show() + } +} +``

On macOS makeKeyAndOrderFront: and the application is activated. On Windows ShowWindow(SW_SHOW) and SetForegroundWindow; Windows may refuse the focus to a program that is not in the foreground, in which case the window comes back and its taskbar button flashes instead.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-enqueue.html b/assets-enqueue.html new file mode 100644 index 00000000..c1073f07 --- /dev/null +++ b/assets-enqueue.html @@ -0,0 +1,28 @@ + + + + + +Assets.enqueue — Ludic + + + + + + +
+
API Reference › Assets › Assets.enqueue
+
+ method +

Assets.enqueue

+
+ Assets.enqueue(name, path) +

Adds a named image file to the preload queue without loading it now. Load the queue incrementally with Assets.pump from a loading scene, then reach each asset by name with Assets.get once Assets.ready. Enqueueing is deterministic — the same order loads the same assets in the same order every run.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-enqueue_dir.html b/assets-enqueue_dir.html new file mode 100644 index 00000000..9619f965 --- /dev/null +++ b/assets-enqueue_dir.html @@ -0,0 +1,31 @@ + + + + + +Assets.enqueue_dir — Ludic + + + + + + +
+
API Reference › Assets › Assets.enqueue_dir
+
+ method +

Assets.enqueue_dir

+
+ Assets.enqueue_dir(path: dir) +

Each file is enqueued under its name without the extension (assets/audio/hit.wav → hit); the queue sorts it by extension as usual, so a sound directory becomes the sound bank in one line.

+ +

Example

# doc-check: skip — illustrative
+Assets.enqueue_dir(path: "assets/audio")
+…
+Audio.play(name: "hit")
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-font.html b/assets-font.html new file mode 100644 index 00000000..aac4a0a8 --- /dev/null +++ b/assets-font.html @@ -0,0 +1,31 @@ + + + + + +Assets.font — Ludic + + + + + + +
+
API Reference › Assets › Assets.font
+
+ method +

Assets.font

+
+ Assets.font(name: s) -> int +

The font handle for a .ttf / .ttc that Assets.enqueue loaded under name; 0 while it is not loaded yet. The queue sorts by extension: images become sprites (Assets.get), sounds go to the audio bank (Audio.play(name:)), fonts here.

+ +

Example

# doc-check: skip — illustrative
+Assets.enqueue(name: "menu", path: "assets/fonts/PixelOperator.ttf")
+…
+menu_font = Assets.font(name: "menu")
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-get.html b/assets-get.html new file mode 100644 index 00000000..2f1b74f2 --- /dev/null +++ b/assets-get.html @@ -0,0 +1,28 @@ + + + + + +Assets.get — Ludic + + + + + + +
+
API Reference › Assets › Assets.get
+
+ method +

Assets.get

+
+ Assets.get(name) -> int +

Alias of Sprite.named: returns the sprite id registered under name, or -1.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-image.html b/assets-image.html new file mode 100644 index 00000000..6bbf2943 --- /dev/null +++ b/assets-image.html @@ -0,0 +1,28 @@ + + + + + +Assets.image — Ludic + + + + + + +
+
API Reference › Assets › Assets.image
+
+ method +

Assets.image

+
+ Assets.image(path) -> int +

Loads the image at path as a single atlas sprite the size of the image, returning its id — the namespaced replacement for a bare png_load. Draw it with Sprite.draw.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-load.html b/assets-load.html new file mode 100644 index 00000000..b8c93a8f --- /dev/null +++ b/assets-load.html @@ -0,0 +1,28 @@ + + + + + +Assets.load — Ludic + + + + + + +
+
API Reference › Assets › Assets.load
+
+ method +

Assets.load

+
+ Assets.load(path) -> int +

Alias of Assets.image: loads a whole image file as one sprite and returns its id.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-loaded.html b/assets-loaded.html new file mode 100644 index 00000000..31063d4e --- /dev/null +++ b/assets-loaded.html @@ -0,0 +1,28 @@ + + + + + +Assets.loaded — Ludic + + + + + + +
+
API Reference › Assets › Assets.loaded
+
+ method +

Assets.loaded

+
+ Assets.loaded() -> int +

Returns how many of the enqueued assets have loaded so far (advanced by Assets.pump) — the numerator for a progress bar.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-progress.html b/assets-progress.html new file mode 100644 index 00000000..d45c8e18 --- /dev/null +++ b/assets-progress.html @@ -0,0 +1,28 @@ + + + + + +Assets.progress — Ludic + + + + + + +
+
API Reference › Assets › Assets.progress
+
+ method +

Assets.progress

+
+ Assets.progress() -> int +

Returns the loading progress as a whole-number percent (0..100); an empty queue is 100. Draw it as a progress bar on the loading screen.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-pump.html b/assets-pump.html new file mode 100644 index 00000000..8560d073 --- /dev/null +++ b/assets-pump.html @@ -0,0 +1,28 @@ + + + + + +Assets.pump — Ludic + + + + + + +
+
API Reference › Assets › Assets.pump
+
+ method +

Assets.pump

+
+ Assets.pump(max) -> int +

Loads up to max queued assets this frame, registering each under its name, and returns how many it loaded. Call it each frame in a loading scene with a small max so the frame stays short and the loading screen animates; Assets.ready flips true when the queue is drained. This is the deterministic, no-threads form of async preloading — the work is spread across frames instead of stalling one.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-ready.html b/assets-ready.html new file mode 100644 index 00000000..eedd5f3c --- /dev/null +++ b/assets-ready.html @@ -0,0 +1,28 @@ + + + + + +Assets.ready — Ludic + + + + + + +
+
API Reference › Assets › Assets.ready
+
+ method +

Assets.ready

+
+ Assets.ready() -> int +

Returns 1 once every enqueued asset has been loaded (the queue is drained), else 0. A loading scene pumps until this is true, then enters play (e.g. become the game scene).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/assets-total.html b/assets-total.html new file mode 100644 index 00000000..ee5a88b4 --- /dev/null +++ b/assets-total.html @@ -0,0 +1,28 @@ + + + + + +Assets.total — Ludic + + + + + + +
+
API Reference › Assets › Assets.total
+
+ method +

Assets.total

+
+ Assets.total() -> int +

Returns the total number of assets enqueued with Assets.enqueue — the denominator for a progress bar.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-define.html b/audio-define.html new file mode 100644 index 00000000..5f681a61 --- /dev/null +++ b/audio-define.html @@ -0,0 +1,30 @@ + + + + + +Audio.define — Ludic + + + + + + +
+
API Reference › Audio › Audio.define
+
+ method +

Audio.define

+
+ Audio.define(name: s, path: p) -> int +

Loads a sound file and registers it under name, so Audio.play(name: …) and Audio.play_music(name: …) can fire it without a handle in the game's own state. Returns the handle (0 headless). Assets.enqueue does the same for a .wav / .mp3 path.

+ +

Example

# doc-check: skip — illustrative
+Audio.define(name: "hit", path: "assets/audio/hit.wav")
+Audio.play(name: "hit")
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-is_playing.html b/audio-is_playing.html new file mode 100644 index 00000000..9082a2fb --- /dev/null +++ b/audio-is_playing.html @@ -0,0 +1,33 @@ + + + + + +Audio.is_playing — Ludic + + + + + + +
+
API Reference › Audio › Audio.is_playing
+
+ method +

Audio.is_playing

+
+ Audio.is_playing(id) -> bool +

Returns whether the sound id is currently playing. Always false in a headless build.

+ +

Example

program Demo {
+  entry {
+    let sfx = Audio.load("blip.wav")
+    if Audio.is_playing(sfx) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-load.html b/audio-load.html new file mode 100644 index 00000000..6f3d0f6b --- /dev/null +++ b/audio-load.html @@ -0,0 +1,33 @@ + + + + + +Audio.load — Ludic + + + + + + +
+
API Reference › Audio › Audio.load
+
+ method +

Audio.load

+
+ Audio.load(path) -> int +

Loads a sound file and returns a handle (>= 1), or 0 on failure or in a headless build. Load each sound once (e.g. during the Start phase) and reuse the handle.

+ +

Example

program Demo {
+  entry {
+    let sfx = Audio.load("blip.wav")
+    print(sfx)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-named.html b/audio-named.html new file mode 100644 index 00000000..f90d98d2 --- /dev/null +++ b/audio-named.html @@ -0,0 +1,29 @@ + + + + + +Audio.named — Ludic + + + + + + +
+
API Reference › Audio › Audio.named
+
+ method +

Audio.named

+
+ Audio.named(name: s) -> int +

Looks a sound up in the bank by the name Audio.define (or Assets.enqueue) gave it.

+ +

Example

# doc-check: skip — illustrative
+let hit = Audio.named(name: "hit")
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-pitch.html b/audio-pitch.html new file mode 100644 index 00000000..375b9fa7 --- /dev/null +++ b/audio-pitch.html @@ -0,0 +1,32 @@ + + + + + +Audio.pitch — Ludic + + + + + + +
+
API Reference › Audio › Audio.pitch
+
+ method +

Audio.pitch

+
+ Audio.pitch(v) -> void +

Sets the playback rate / pitch (1.0 normal, 0.5 an octave down, 2.0 up), applied to every loaded sound.

+ +

Example

program Demo {
+  entry {
+    Audio.pitch(1.5)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-play.html b/audio-play.html new file mode 100644 index 00000000..953589b3 --- /dev/null +++ b/audio-play.html @@ -0,0 +1,33 @@ + + + + + +Audio.play — Ludic + + + + + + +
+
API Reference › Audio › Audio.play
+
+ method +

Audio.play

+
+ Audio.play(id) -> void +

Plays the sound id from the start as a one-shot effect, at the current master volume and pitch.

+ +

Example

program Demo {
+  entry {
+    let sfx = Audio.load("blip.wav")
+    Audio.play(sfx)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-play_at.html b/audio-play_at.html new file mode 100644 index 00000000..8587a676 --- /dev/null +++ b/audio-play_at.html @@ -0,0 +1,46 @@ + + + + + +Audio.play_at — Ludic + + + + + + +
+
API Reference › Audio › Audio.play_at
+
+ method +

Audio.play_at

+
+ Audio.play_at(id, gain, pitch, pan) -> void +

Plays the sound id from the start as a one-shot, with its **own** gain, pitch +and stereo position, leaving the master settings alone.

Audio.volume and Audio.pitch are global: they exist so a player +can turn the game down, and a game that used them to place a sound in the world would be +fighting its own options screen. This is the per-voice version, and it is what distance +attenuation is made of — work out how far away a sound is and which side it is on, and say +so here.

- gain — 0.0 to 1.0, multiplied by the master volume, so the options screen + still wins. +- pitch — 1.0 is as recorded; 0.5 an octave down, 2.0 an octave up. Clamped + to the backend's 0.25 .. 4.0. +- pan — -1.0 hard left, 0.0 centred, 1.0 hard right.

One honest limitation, and it is the backend's: a loaded sound is one player, so firing +the same handle again restarts it rather than layering a second copy over the first. Load +a handle per variant when several need to overlap — which is what a game does anyway, to +stop a repeated sound machine-gunning.

+ +

Example

program Demo {
+  entry {
+    let call = Audio.load("elk.wav")
+    # an elk eighty metres off, over your left shoulder
+    Audio.play_at(id: call, gain: 0.3, pitch: 0.96, pan: -0.55)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-play_music.html b/audio-play_music.html new file mode 100644 index 00000000..537576ea --- /dev/null +++ b/audio-play_music.html @@ -0,0 +1,33 @@ + + + + + +Audio.play_music — Ludic + + + + + + +
+
API Reference › Audio › Audio.play_music
+
+ method +

Audio.play_music

+
+ Audio.play_music(id) -> void +

Plays the sound id as looping background music on the single music channel; any music already playing is stopped first.

+ +

Example

program Demo {
+  entry {
+    let theme = Audio.load("theme.ogg")
+    Audio.play_music(theme)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-play_sound.html b/audio-play_sound.html new file mode 100644 index 00000000..e68ec855 --- /dev/null +++ b/audio-play_sound.html @@ -0,0 +1,33 @@ + + + + + +Audio.play_sound — Ludic + + + + + + +
+
API Reference › Audio › Audio.play_sound
+
+ method +

Audio.play_sound

+
+ Audio.play_sound(id) -> void +

An alias of Audio.play — plays the sound id once from the start.

+ +

Example

program Demo {
+  entry {
+    let sfx = Audio.load("blip.wav")
+    Audio.play_sound(sfx)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-stop.html b/audio-stop.html new file mode 100644 index 00000000..a1b9d124 --- /dev/null +++ b/audio-stop.html @@ -0,0 +1,34 @@ + + + + + +Audio.stop — Ludic + + + + + + +
+
API Reference › Audio › Audio.stop
+
+ method +

Audio.stop

+
+ Audio.stop(id) -> void +

Stops the sound id and rewinds it to the start. If it was the current music, the music channel is cleared.

+ +

Example

program Demo {
+  entry {
+    let sfx = Audio.load("blip.wav")
+    Audio.play(sfx)
+    Audio.stop(sfx)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-stop_all.html b/audio-stop_all.html new file mode 100644 index 00000000..7208a020 --- /dev/null +++ b/audio-stop_all.html @@ -0,0 +1,32 @@ + + + + + +Audio.stop_all — Ludic + + + + + + +
+
API Reference › Audio › Audio.stop_all
+
+ method +

Audio.stop_all

+
+ Audio.stop_all() -> void +

Stops every loaded sound, including the music channel.

+ +

Example

program Demo {
+  entry {
+    Audio.stop_all()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-stop_music.html b/audio-stop_music.html new file mode 100644 index 00000000..847714d3 --- /dev/null +++ b/audio-stop_music.html @@ -0,0 +1,32 @@ + + + + + +Audio.stop_music — Ludic + + + + + + +
+
API Reference › Audio › Audio.stop_music
+
+ method +

Audio.stop_music

+
+ Audio.stop_music() -> void +

Stops whatever is playing on the music channel (started by Audio.play_music).

+ +

Example

program Demo {
+  entry {
+    Audio.stop_music()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/audio-volume.html b/audio-volume.html new file mode 100644 index 00000000..717f1fe8 --- /dev/null +++ b/audio-volume.html @@ -0,0 +1,32 @@ + + + + + +Audio.volume — Ludic + + + + + + +
+
API Reference › Audio › Audio.volume
+
+ method +

Audio.volume

+
+ Audio.volume(v) -> void +

Sets the master volume (0.0 .. 1.0), applied to every loaded sound now and to every future play.

+ +

Example

program Demo {
+  entry {
+    Audio.volume(0.8)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/base.css b/base.css new file mode 100644 index 00000000..bd94c7f1 --- /dev/null +++ b/base.css @@ -0,0 +1,315 @@ +/* base.css — the design tokens and shared chrome for every generated page. + * + * The site is deliberately typographic and light-first: a warm paper ground, a + * serif display face for headings, one ink-blue accent used only where it means + * something (links, the current page, a focused control), and colour otherwise + * reserved for code. Dark mode is the same design with the ground inverted, not + * a second theme — every colour below is a token, redefined once under + * prefers-color-scheme and nowhere else. + * + * Fonts are the platform's own. A language reference should render instantly + * and identically offline; a webfont round-trip buys nothing here. */ + +:root { + /* ground and ink */ + --paper: #fcfcfa; + --paper-2: #f5f4f0; + --panel: #ffffff; + --ink: #17171a; + --ink-2: #3d3d44; + --muted: #67676f; + --rule: #e3e2dc; + --rule-2: #cfcec6; + + /* the single accent */ + --accent: #0f4c81; + --accent-ink: #0b3a63; + --accent-bg: #eaf1f8; + + /* code surface and tokens */ + --code-bg: #f7f6f2; + --code-rule: #e3e2dc; + --c-comment: #7a7a70; + --c-key: #8f2d56; + --c-type: #0f6b5c; + --c-str: #9a5518; + --c-num: #1d4f8c; + --c-annot: #6b3fa0; + --c-fn: #0f4c81; + --c-punc: #6a6a72; + --c-arg: #9a5518; + + --radius: 6px; + --max: 1080px; + --hdr: 58px; + + --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif; + --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; +} + +@media (prefers-color-scheme: dark) { + :root { + --paper: #16161a; + --paper-2: #1c1c21; + --panel: #1a1a1f; + --ink: #e6e5e0; + --ink-2: #c2c1bb; + --muted: #8f8f98; + --rule: #2b2b32; + --rule-2: #3d3d46; + + --accent: #7fb0dd; + --accent-ink: #a8ccec; + --accent-bg: #1b2733; + + --code-bg: #1b1b21; + --code-rule: #2b2b32; + --c-comment: #7b7b84; + --c-key: #e08aa8; + --c-type: #64c9b4; + --c-str: #d9a066; + --c-num: #8fb8e8; + --c-annot: #b596e0; + --c-fn: #7fb0dd; + --c-punc: #8b8b94; + --c-arg: #d9a066; + } +} + +*, *::before, *::after { box-sizing: border-box } + +html { + -webkit-text-size-adjust: 100%; + scroll-behavior: smooth; +} +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto } +} + +body { + margin: 0; + background: var(--paper); + color: var(--ink-2); + font-family: var(--sans); + font-size: 16px; + line-height: 1.65; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +h1, h2, h3, h4 { + font-family: var(--serif); + color: var(--ink); + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.005em; + margin: 0; +} + +a { color: var(--accent); text-decoration: none } +a:hover { text-decoration: underline; text-underline-offset: 2px } + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 3px; +} + +.wrap { max-width: var(--max); margin: 0 auto; padding: 0 24px } +.muted { color: var(--muted) } +.mono, code, pre, kbd { font-family: var(--mono) } + +/* Inline code carries no chip: the mono face plus the darker ink is already a + clear enough break from running text, and a tinted box behind every keyword + in a language reference turns a paragraph into confetti. */ +code { + font-size: 0.905em; + color: var(--ink); +} +pre code { font-size: inherit } + +pre { + margin: 14px 0 0; + padding: 15px 17px; + overflow-x: auto; + font-size: 13px; + line-height: 1.7; + tab-size: 2; + background: var(--code-bg); + border: 1px solid var(--code-rule); + border-radius: var(--radius); + color: var(--ink); +} +pre::-webkit-scrollbar { height: 9px } +pre::-webkit-scrollbar-thumb { background: var(--rule-2); border-radius: 5px } + +/* syntax tokens, shared by the highlighter and the hand-written terminal block */ +.t-com { color: var(--c-comment); font-style: italic } +.t-key { color: var(--c-key) } +.t-type { color: var(--c-type) } +.t-str { color: var(--c-str) } +.t-num { color: var(--c-num) } +.t-annot { color: var(--c-annot) } +.t-fn { color: var(--c-fn) } +.t-punc { color: var(--c-punc) } +.t-arg { color: var(--c-arg) } + +/* ---------- token cards ------------------------------------------------- * + * Every page runs the highlighter, so the card chrome lives here rather than + * in docs.css — the landing page links base.css and site.css only, and a card + * whose stylesheet never loaded renders as a static block at the end of the + * body instead of a panel beside the token that was clicked. + * ----------------------------------------------------------------------- */ + +.tok { cursor: help; border-bottom: 1px dotted var(--rule-2) } +a.tok { cursor: pointer } + +.hovercard { + /* the script positions this from getBoundingClientRect(), i.e. in viewport + coordinates, so the card must be fixed, not absolute */ + position: fixed; + z-index: 90; + max-width: min(360px, calc(100vw - 24px)); + background: var(--panel); + border: 1px solid var(--rule-2); + border-radius: var(--radius); + box-shadow: 0 8px 24px -12px rgba(0, 0, 0, .35); + padding: 11px 13px; + font-size: 13.5px; + cursor: pointer; +} +.hovercard:hover { border-color: var(--accent) } +.hc-top { display: flex; align-items: center; gap: 9px; margin-bottom: 6px } +.hc-name { font-family: var(--mono); font-size: 13.5px; color: var(--ink) } +.hc-sig { font-family: var(--mono); font-size: 12.5px; color: var(--muted); display: block; margin-bottom: 6px } +.hc-tip { color: var(--ink-2) } +.hc-foot { display: block; margin-top: 8px; font-size: 12px; color: var(--muted) } + +/* the kind badge is worn both by a card and by a reference page's title */ +.kind-badge { + font-family: var(--mono); + font-size: 11px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--muted); + border: 1px solid var(--rule-2); + border-radius: 3px; + padding: 2px 7px; + white-space: nowrap; +} +/* the accent is the only colour that distinguishes a kind, and only for the + two that are structural rather than callable */ +.kind-namespace, .kind-keyword { color: var(--accent); border-color: var(--accent) } + +/* ---------- header ---------- */ + +header.nav { + position: sticky; + top: 0; + z-index: 50; + background: color-mix(in srgb, var(--paper) 92%, transparent); + backdrop-filter: saturate(1.4) blur(8px); + border-bottom: 1px solid var(--rule); +} +.nav-in { display: flex; align-items: center; gap: 14px; min-height: var(--hdr) } + +.brand { + display: inline-flex; + align-items: center; + gap: 9px; + font-family: var(--serif); + font-weight: 600; + font-size: 18px; + color: var(--ink); + letter-spacing: -0.01em; +} +.brand:hover { text-decoration: none } +.logo { + width: 23px; height: 23px; + flex: none; + display: grid; + place-items: center; + border: 1px solid var(--rule-2); + border-radius: 4px; + background: var(--panel); + font-family: var(--mono); + font-size: 12px; + font-weight: 500; + color: var(--accent); +} + +.nav-toggle { + margin-left: auto; + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; height: 38px; + border: 1px solid var(--rule-2); + border-radius: var(--radius); + background: var(--panel); + color: var(--ink); + font-size: 17px; + line-height: 1; + cursor: pointer; +} + +.nav-links { + display: none; + position: absolute; + top: 100%; left: 0; right: 0; + flex-direction: column; + background: var(--paper); + border-bottom: 1px solid var(--rule); + padding: 0 24px 8px; +} +header.nav.open .nav-links { display: flex } +.nav-links a { + color: var(--ink-2); + font-size: 15px; + padding: 13px 2px; + border-top: 1px solid var(--rule); +} +.nav-links a:first-child { border-top: none } + +@media (min-width: 760px) { + .nav-toggle { display: none } + .nav-links { + display: flex; + position: static; + flex-direction: row; + align-items: center; + gap: 26px; + margin-left: auto; + padding: 0; + border: none; + background: none; + } + .nav-links a { + color: var(--muted); + font-size: 14.5px; + padding: 0; + border: none; + } + .nav-links a:hover { color: var(--ink); text-decoration: none } + .nav-links .nav-cta { color: var(--ink) } +} + +/* ---------- footer ---------- */ + +footer { + margin-top: 84px; + border-top: 1px solid var(--rule); + padding: 32px 0 44px; +} +.foot-in { + display: flex; + flex-wrap: wrap; + gap: 22px; + align-items: flex-start; + justify-content: space-between; +} +.foot-in .muted { font-size: 14px; max-width: 42ch } +.foot-links { display: flex; flex-wrap: wrap; gap: 20px } +.foot-links a { color: var(--muted); font-size: 14px } +.foot-links a:hover { color: var(--ink) } diff --git a/base64-decode.html b/base64-decode.html new file mode 100644 index 00000000..10017be5 --- /dev/null +++ b/base64-decode.html @@ -0,0 +1,28 @@ + + + + + +Base64.decode — Ludic + + + + + + +
+
API Reference › Base64 › Base64.decode
+
+ method +

Base64.decode

+
+ Base64.decode(src: string) -> string +

Decodes base64 src to the bytes it represents, returned as a NUL-terminated string. ASCII whitespace is ignored and = padding ends the stream. For binary payloads that may contain a NUL byte, decode into a sized buffer at the runtime level and track the returned length instead.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/base64-encode.html b/base64-encode.html new file mode 100644 index 00000000..66de85e0 --- /dev/null +++ b/base64-encode.html @@ -0,0 +1,28 @@ + + + + + +Base64.encode — Ludic + + + + + + +
+
API Reference › Base64 › Base64.encode
+
+ method +

Base64.encode

+
+ Base64.encode(src: string) -> string +

Encodes the bytes of src as standard base64 with = padding — the inverse of Base64.decode.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-add.html b/bigint-add.html new file mode 100644 index 00000000..647564c5 --- /dev/null +++ b/bigint-add.html @@ -0,0 +1,32 @@ + + + + + +BigInt.add — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.add
+
+ method +

BigInt.add

+
+ BigInt.add(a, b) -> BigInt +

Returns the exact sum a + b. There is no overflow — the result grows as many digits as it needs.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let total = BigInt.add(score, reward)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-cmp.html b/bigint-cmp.html new file mode 100644 index 00000000..c347e5aa --- /dev/null +++ b/bigint-cmp.html @@ -0,0 +1,32 @@ + + + + + +BigInt.cmp — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.cmp
+
+ method +

BigInt.cmp

+
+ BigInt.cmp(a, b) -> int +

Compares two big integers, returning -1, 0, or 1 as a is less than, equal to, or greater than b.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if BigInt.cmp(score, best) > 0 { best = score }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-div.html b/bigint-div.html new file mode 100644 index 00000000..aee8f0d6 --- /dev/null +++ b/bigint-div.html @@ -0,0 +1,32 @@ + + + + + +BigInt.div — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.div
+
+ method +

BigInt.div

+
+ BigInt.div(a, d) -> BigInt +

Divides a BigInt by an int divisor, truncating toward zero. Handy for splitting an exact total into equal shares.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let each = BigInt.div(pot, players)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-eq.html b/bigint-eq.html new file mode 100644 index 00000000..7727ca15 --- /dev/null +++ b/bigint-eq.html @@ -0,0 +1,32 @@ + + + + + +BigInt.eq — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.eq
+
+ method +

BigInt.eq

+
+ BigInt.eq(a, b) -> bool +

Returns true when two big integers have exactly the same value.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if BigInt.eq(score, target) { win() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-from.html b/bigint-from.html new file mode 100644 index 00000000..cba2ccca --- /dev/null +++ b/bigint-from.html @@ -0,0 +1,32 @@ + + + + + +BigInt.from — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.from
+
+ method +

BigInt.from

+
+ BigInt.from(value) -> BigInt +

Lifts a plain int into a BigInt, the starting point for exact arbitrary-precision arithmetic that would overflow an ordinary integer.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let n = BigInt.from(1000000)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-is_zero.html b/bigint-is_zero.html new file mode 100644 index 00000000..2ac8299f --- /dev/null +++ b/bigint-is_zero.html @@ -0,0 +1,32 @@ + + + + + +BigInt.is_zero — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.is_zero
+
+ method +

BigInt.is_zero

+
+ BigInt.is_zero(a) -> bool +

Returns true when the value is exactly zero — cheaper and clearer than comparing against BigInt.from(0).

+ +

Example

program Demo {
+  handler Step phase Update {
+    if BigInt.is_zero(balance) { gameOver() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-mod.html b/bigint-mod.html new file mode 100644 index 00000000..d555aea2 --- /dev/null +++ b/bigint-mod.html @@ -0,0 +1,32 @@ + + + + + +BigInt.mod — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.mod
+
+ method +

BigInt.mod

+
+ BigInt.mod(a, d) -> int +

Returns the remainder of a divided by an int divisor, carrying the sign of a. Pairs with BigInt.div.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let leftover = BigInt.mod(pot, players)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-mul.html b/bigint-mul.html new file mode 100644 index 00000000..ab269a5a --- /dev/null +++ b/bigint-mul.html @@ -0,0 +1,32 @@ + + + + + +BigInt.mul — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.mul
+
+ method +

BigInt.mul

+
+ BigInt.mul(a, b) -> BigInt +

Returns the exact product a * b. Multiplying two large counters never loses precision — the classic idle-game growth step.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let next = BigInt.mul(count, multiplier)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-neg.html b/bigint-neg.html new file mode 100644 index 00000000..a294e6bc --- /dev/null +++ b/bigint-neg.html @@ -0,0 +1,32 @@ + + + + + +BigInt.neg — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.neg
+
+ method +

BigInt.neg

+
+ BigInt.neg(a) -> BigInt +

Returns -a — the same magnitude with the opposite sign.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let debt = BigInt.neg(balance)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-parse.html b/bigint-parse.html new file mode 100644 index 00000000..6c13c685 --- /dev/null +++ b/bigint-parse.html @@ -0,0 +1,32 @@ + + + + + +BigInt.parse — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.parse
+
+ method +

BigInt.parse

+
+ BigInt.parse(text) -> BigInt +

Parses decimal text (an optional leading sign then digits) into a BigInt — the way to enter a value too large for an int literal.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let huge = BigInt.parse("123456789012345678901234567890")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-pow.html b/bigint-pow.html new file mode 100644 index 00000000..1a80d0e8 --- /dev/null +++ b/bigint-pow.html @@ -0,0 +1,32 @@ + + + + + +BigInt.pow — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.pow
+
+ method +

BigInt.pow

+
+ BigInt.pow(a, exp) -> BigInt +

Raises a to the (non-negative) integer power exp by binary exponentiation — an exact way to reach astronomically large magnitudes.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let googol = BigInt.pow(BigInt.from(10), 100)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-str.html b/bigint-str.html new file mode 100644 index 00000000..702ec302 --- /dev/null +++ b/bigint-str.html @@ -0,0 +1,32 @@ + + + + + +BigInt.str — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.str
+
+ method +

BigInt.str

+
+ BigInt.str(a) -> string +

Renders the full value as decimal text, with a leading - when negative. This is how you display or serialize a BigInt.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Screen.draw_text(8, 8, BigInt.str(score), Color.White, 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-sub.html b/bigint-sub.html new file mode 100644 index 00000000..1add0072 --- /dev/null +++ b/bigint-sub.html @@ -0,0 +1,32 @@ + + + + + +BigInt.sub — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.sub
+
+ method +

BigInt.sub

+
+ BigInt.sub(a, b) -> BigInt +

Returns the exact difference a - b; the result is negative when b is larger.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let remaining = BigInt.sub(bank, cost)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/bigint-to_int.html b/bigint-to_int.html new file mode 100644 index 00000000..21a8b853 --- /dev/null +++ b/bigint-to_int.html @@ -0,0 +1,32 @@ + + + + + +BigInt.to_int — Ludic + + + + + + +
+
API Reference › BigInt › BigInt.to_int
+
+ method +

BigInt.to_int

+
+ BigInt.to_int(a) -> int +

Returns the value as a plain int when it fits in 32 bits, clamping to the int min/max otherwise. Use it to feed a small big-integer back into ordinary code.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let n = BigInt.to_int(BigInt.from(2024))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/camera-follow.html b/camera-follow.html new file mode 100644 index 00000000..8dd22be8 --- /dev/null +++ b/camera-follow.html @@ -0,0 +1,35 @@ + + + + + +Camera.follow — Ludic + + + + + + +
+
API Reference › Camera › Camera.follow
+
+ method +

Camera.follow

+
+ Camera.follow(x, y, lerp) +

Moves the camera a fraction lerp of the way toward centring the world point (x, y) on screen. lerp is a fixed in 0.0..1.0: 0 holds still, a small value trails smoothly behind a moving target, 1.0 snaps it centred. Call it each frame with the target's position for a classic smooth-follow camera. Deterministic.

+

Parameters

lerphow far to move this frame (a fixed, 0..1)
+

Example

program Demo {
+  property Position { x: int = 0, y: int = 0 }
+  model Player { Position }
+  handler DrawWorld phase Render {
+    Camera.follow(Position.x, Position.y, fixed(1) / fixed(8))   # smooth trail
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/camera-set.html b/camera-set.html new file mode 100644 index 00000000..55c83012 --- /dev/null +++ b/camera-set.html @@ -0,0 +1,35 @@ + + + + + +Camera.set — Ludic + + + + + + +
+
API Reference › Camera › Camera.set
+
+ method +

Camera.set

+
+ Camera.set(x, y) +

Sets the camera offset directly: a world point (wx, wy) draws on screen at (wx - x, wy - y). The same control as Screen.camera, under the Camera namespace. Use it to snap the view, or as the base that Camera.follow and Camera.shake build on.

+ +

Example

program Demo {
+  property Position { x: int = 0, y: int = 0 }
+  model Player { Position }
+  handler DrawWorld phase Render {
+    Camera.set(Position.x - 160, Position.y - 120)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/camera-shake.html b/camera-shake.html new file mode 100644 index 00000000..b1a2f03d --- /dev/null +++ b/camera-shake.html @@ -0,0 +1,36 @@ + + + + + +Camera.shake — Ludic + + + + + + +
+
API Reference › Camera › Camera.shake
+
+ method +

Camera.shake

+
+ Camera.shake(amount) +

Adds a random screen shake of up to +/- amount pixels on top of the camera's base offset, drawn from the seeded RNG so a replay shakes identically. Call it each frame with a decaying amount for a hit or explosion; Camera.shake(0) clears it. It stacks on Camera.set / Camera.follow, so follow and shake compose.

+

Parameters

amountthe maximum shake magnitude in pixels (0 clears it)
+

Example

program Demo {
+  property Hit { timer: int = 0 }
+  model Cam { Hit }
+  handler DrawWorld phase Render {
+    if Hit.timer > 0 { Camera.shake(Hit.timer) }
+    else { Camera.shake(0) }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/camera-shake_for.html b/camera-shake_for.html new file mode 100644 index 00000000..0ce88eb7 --- /dev/null +++ b/camera-shake_for.html @@ -0,0 +1,29 @@ + + + + + +Camera.shake_for — Ludic + + + + + + +
+
API Reference › Camera › Camera.shake_for
+
+ method +

Camera.shake_for

+
+ Camera.shake_for(amount: px, frames: n) +

Shakes the camera by up to ±amount pixels for frames frames; the engine re-rolls the offset each frame and clears it when the time is up. A bigger amount replaces one in flight. Camera.shake(amount: 0) still cancels the offset for the rest of a frame — useful before a HUD pass that must stay steady.

+ +

Example

# doc-check: skip — illustrative
+@On(Damaged) handler Hurt { if target == player { Camera.shake_for(amount: 4, frames: 8) } }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/camera-zoom.html b/camera-zoom.html new file mode 100644 index 00000000..0f294d40 --- /dev/null +++ b/camera-zoom.html @@ -0,0 +1,36 @@ + + + + + +Camera.zoom — Ludic + + + + + + +
+
API Reference › Camera › Camera.zoom
+
+ method +

Camera.zoom

+
+ Camera.zoom(scale) +

Sets a render-time zoom: every draw is scaled about the screen centre by scale, a Q16.16 fixed factor. 1.0 is no zoom (and turns the zoom path back off, restoring the byte-identical un-zoomed blit); 2.0 is 2x in; 0.5 is 2x out. It composes with Camera.set / Camera.follow (which move the view) and Camera.shake.

Zoom is a *render-time transform*, not a change to the world coordinate types — those stay integer pixels + Q16.16 velocity so lockstep, replay and world_save hold (see the position-types RFC, #78). Because it rides on fixed-point, the zoom is itself deterministic: the same scale produces the same pixels on every machine and headless.

+

Parameters

scalethe zoom factor (fixed); 1.0 = no zoom, >1 zooms in, <1 zooms out
+

Example

program Demo {
+  property Cam { z: fixed = 1.0 }
+  model View { Cam }
+  handler DrawWorld phase Render {
+    Camera.zoom(2.0)
+    Screen.fill_rectangle(150, 110, 20, 20, 0xffcc00)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/clock-advance.html b/clock-advance.html new file mode 100644 index 00000000..b70c2628 --- /dev/null +++ b/clock-advance.html @@ -0,0 +1,32 @@ + + + + + +Clock.advance — Ludic + + + + + + +
+
API Reference › Clock › Clock.advance
+
+ method +

Clock.advance

+
+ Clock.advance(span) +

Moves the simulated clock forward by span seconds — pair it with the Duration.* constructors. Advance it by a fixed step each tick to drive a deterministic day/night or season cycle.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Clock.advance(Duration.minutes(1))   # one in-game minute per call
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/clock-now.html b/clock-now.html new file mode 100644 index 00000000..7a19c079 --- /dev/null +++ b/clock-now.html @@ -0,0 +1,33 @@ + + + + + +Clock.now — Ludic + + + + + + +
+
API Reference › Clock › Clock.now
+
+ method +

Clock.now

+
+ Clock.now() -> int +

The current value of the simulated clock, as a DateTime (seconds since 1970). Deterministic — it returns exactly what the game last set or advanced it to, never the wall clock.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let t = Clock.now()
+    let hour = DateTime.hour(t)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/clock-reset.html b/clock-reset.html new file mode 100644 index 00000000..a3c8b9e0 --- /dev/null +++ b/clock-reset.html @@ -0,0 +1,32 @@ + + + + + +Clock.reset — Ludic + + + + + + +
+
API Reference › Clock › Clock.reset
+
+ method +

Clock.reset

+
+ Clock.reset() +

Resets the simulated clock back to 0 (the 1970 epoch). Handy at the start of a new run or a replay.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Clock.reset()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/clock-set.html b/clock-set.html new file mode 100644 index 00000000..9bcbda39 --- /dev/null +++ b/clock-set.html @@ -0,0 +1,32 @@ + + + + + +Clock.set — Ludic + + + + + + +
+
API Reference › Clock › Clock.set
+
+ method +

Clock.set

+
+ Clock.set(t) +

Sets the simulated clock to the instant t. Typically called once on load (e.g. from a saved Time.now()), after which gameplay advances it deterministically.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Clock.set(DateTime.from(2026, 8, 30, 8, 0, 0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/collision-circles.html b/collision-circles.html new file mode 100644 index 00000000..ab419afb --- /dev/null +++ b/collision-circles.html @@ -0,0 +1,32 @@ + + + + + +Collision.circles — Ludic + + + + + + +
+
API Reference › Collision › Collision.circles
+
+ method +

Collision.circles

+
+ Collision.circles(ax, ay, ar, bx, by, br) -> bool +

Returns true when circles centred at (ax, ay) and (bx, by) with radii ar and br overlap — that is, when the distance between centres is at most ar + br. It compares squared distances internally, so there is no square root and no precision loss.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Collision.circles(px, py, 8, ex, ey, 8) { collide() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/collision-point_rect.html b/collision-point_rect.html new file mode 100644 index 00000000..ecd03776 --- /dev/null +++ b/collision-point_rect.html @@ -0,0 +1,32 @@ + + + + + +Collision.point_rect — Ludic + + + + + + +
+
API Reference › Collision › Collision.point_rect
+
+ method +

Collision.point_rect

+
+ Collision.point_rect(px, py, rx, ry, rw, rh) -> bool +

Returns true when the point (px, py) lies within the rectangle (rx, ry, rw, rh) — inclusive on the top-left edge, exclusive on the bottom-right. Use it for mouse or touch hit-testing against a button or a world region.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Collision.point_rect(mx, my, bx, by, bw, bh) { press() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/collision-rect_circle.html b/collision-rect_circle.html new file mode 100644 index 00000000..44886104 --- /dev/null +++ b/collision-rect_circle.html @@ -0,0 +1,32 @@ + + + + + +Collision.rect_circle — Ludic + + + + + + +
+
API Reference › Collision › Collision.rect_circle
+
+ method +

Collision.rect_circle

+
+ Collision.rect_circle(rx, ry, rw, rh, cx, cy, cr) -> bool +

Returns true when the rectangle (rx, ry, rw, rh) overlaps the circle centred at (cx, cy) with radius cr. It finds the point on the rectangle nearest the circle's centre and checks whether it falls within the radius — the correct test for a round actor against a blocky tile.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Collision.rect_circle(tx, ty, 16, 16, bx, by, 6) { block() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/collision-rects.html b/collision-rects.html new file mode 100644 index 00000000..b68a380a --- /dev/null +++ b/collision-rects.html @@ -0,0 +1,32 @@ + + + + + +Collision.rects — Ludic + + + + + + +
+
API Reference › Collision › Collision.rects
+
+ method +

Collision.rects

+
+ Collision.rects(ax, ay, aw, ah, bx, by, bw, bh) -> bool +

Returns true when the axis-aligned rectangles (ax, ay, aw, ah) and (bx, by, bw, bh) overlap. Edges that merely touch do not count as overlapping. This is the standard broad-phase test for two hitboxes, tiles, or UI regions.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Collision.rects(px, py, 16, 16, ex, ey, 16, 16) { hit() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-darken.html b/color-darken.html new file mode 100644 index 00000000..4b1b7a69 --- /dev/null +++ b/color-darken.html @@ -0,0 +1,34 @@ + + + + + +Color.darken — Ludic + + + + + + +
+
API Reference › Color functions › Color.darken
+
+ method +

Color.darken

+
+ Color.darken(c, amount) -> int +

Multiplies each channel of c by 1 - amount (amount a fixed 0..1), returning a darker color. Color.darken(c, 0.0) is unchanged and 1.0 is black. Handy for shadows and pressed-button states.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.darken(Color.Red, 0.3)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-lerp.html b/color-lerp.html new file mode 100644 index 00000000..e9279d62 --- /dev/null +++ b/color-lerp.html @@ -0,0 +1,34 @@ + + + + + +Color.lerp — Ludic + + + + + + +
+
API Reference › Color functions › Color.lerp
+
+ method +

Color.lerp

+
+ Color.lerp(c0, c1, t) -> int +

Interpolates each channel of c0 and c1 by the fixed amount t in 0.0..1.0, returning the blended color. Use it for gradients, fades between two tints, or a flash that returns to base.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.lerp(Color.Black, Color.White, 0.5)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-lighten.html b/color-lighten.html new file mode 100644 index 00000000..fbb0ad88 --- /dev/null +++ b/color-lighten.html @@ -0,0 +1,34 @@ + + + + + +Color.lighten — Ludic + + + + + + +
+
API Reference › Color functions › Color.lighten
+
+ method +

Color.lighten

+
+ Color.lighten(c, amount) -> int +

Moves each channel of c a fraction amount of the way to 255, returning a lighter color — the counterpart to Color.darken. Good for highlights and hover states.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.lighten(Color.Blue, 0.3)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-rgb.html b/color-rgb.html new file mode 100644 index 00000000..62f295d9 --- /dev/null +++ b/color-rgb.html @@ -0,0 +1,34 @@ + + + + + +Color.rgb — Ludic + + + + + + +
+
API Reference › Color functions › Color.rgb
+
+ method +

Color.rgb

+
+ Color.rgb(r, g, b) -> int +

Packs three 0..255 channel values into a single 0xRRGGBB color int. Use it to build a color from computed channels rather than a fixed literal or a palette name.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.rgb(255, 128, 64)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-rgba.html b/color-rgba.html new file mode 100644 index 00000000..273e36bf --- /dev/null +++ b/color-rgba.html @@ -0,0 +1,34 @@ + + + + + +Color.rgba — Ludic + + + + + + +
+
API Reference › Color functions › Color.rgba
+
+ method +

Color.rgba

+
+ Color.rgba(r, g, b, a) -> int +

Packs r, g, b, and an alpha a (0..255) into a 0xAARRGGBB int. The software renderer draws opaque, but the alpha byte is preserved for your own blending or storage.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.rgba(255, 128, 64, 200)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/color-with_alpha.html b/color-with_alpha.html new file mode 100644 index 00000000..a720e4ab --- /dev/null +++ b/color-with_alpha.html @@ -0,0 +1,34 @@ + + + + + +Color.with_alpha — Ludic + + + + + + +
+
API Reference › Color functions › Color.with_alpha
+
+ method +

Color.with_alpha

+
+ Color.with_alpha(c, a) -> int +

Returns c with its alpha byte set to a (0..255), leaving the RGB channels untouched.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let c = Color.with_alpha(Color.Green, 128)
+    Screen.clear(c)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-base64.html b/crypto-base64.html new file mode 100644 index 00000000..40e8bae3 --- /dev/null +++ b/crypto-base64.html @@ -0,0 +1,33 @@ + + + + + +Crypto.base64 — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.base64
+
+ method +

Crypto.base64

+
+ Crypto.base64(s) -> string +

Encodes the bytes of s as standard base64 (RFC 4648, the A–Z a–z 0–9 + / alphabet with = padding). Base64 turns arbitrary bytes into printable ASCII, which is what you want when a digest, key, or binary blob has to travel through a text-only channel — a JSON field, a URL-safe token wrapper, a config file, a log line. It is an *encoding*, not encryption: it hides nothing and adds no integrity. Pair it with Crypto.hmac_sha256 when the payload must also be tamper-evident.

The output length is always a multiple of four; a one- or two-byte remainder in the input is padded with =.

+

Parameters

sthe string whose bytes are encoded
+

Example

program Encode {
+  entry {
+    print(Crypto.base64("foobar"))   # Zm9vYmFy
+    print(Crypto.base64("f"))        # Zg==
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-ct_equal.html b/crypto-ct_equal.html new file mode 100644 index 00000000..e469f5da --- /dev/null +++ b/crypto-ct_equal.html @@ -0,0 +1,33 @@ + + + + + +Crypto.ct_equal — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.ct_equal
+
+ method +

Crypto.ct_equal

+
+ Crypto.ct_equal(a, b) -> bool +

Compares two strings for equality without short-circuiting: every character is examined even once a difference is found, so the time taken does not reveal where — or whether — the strings first diverged. Reach for it whenever you compare a secret, token, or MAC that an attacker might be probing. For the common case of checking a message tag, Crypto.verify_hmac already does this for you; use ct_equal directly when you hold both values yourself. Strings of different length return false at once (length is not secret). For ordinary, non-secret text just use == — the constant-time guarantee is not free.

+

Parameters

aone string
bthe other string
+

Example

program CompareToken {
+  entry {
+    if Crypto.ct_equal("abc", "abc") { print(1) } else { print(0) }   # 1
+    if Crypto.ct_equal("abc", "abd") { print(1) } else { print(0) }   # 0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-hex.html b/crypto-hex.html new file mode 100644 index 00000000..fe25acd2 --- /dev/null +++ b/crypto-hex.html @@ -0,0 +1,32 @@ + + + + + +Crypto.hex — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.hex
+
+ method +

Crypto.hex

+
+ Crypto.hex(s) -> string +

Encodes the bytes of s as a lowercase hex string — two characters per byte, so an n-byte input becomes a 2n-character result. It is the same encoding Crypto.sha256 already applies to a digest, exposed on its own so you can render arbitrary bytes (a key id, a small binary token) in a form that is safe to print, log, or embed in text.

+

Parameters

sthe string whose bytes are encoded
+

Example

program HexDump {
+  entry {
+    print(Crypto.hex("abc"))   # 616263
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-hmac_sha256.html b/crypto-hmac_sha256.html new file mode 100644 index 00000000..9423032e --- /dev/null +++ b/crypto-hmac_sha256.html @@ -0,0 +1,35 @@ + + + + + +Crypto.hmac_sha256 — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.hmac_sha256
+
+ method +

Crypto.hmac_sha256

+
+ Crypto.hmac_sha256(key, msg) -> string +

Computes HMAC-SHA256 over msg under the secret key (RFC 2104) and returns the 64-character lowercase hex tag. Unlike a bare hash, a MAC cannot be recomputed without the key, so it authenticates the message: attach the tag to a save file or a network packet, and a receiver who shares the key can tell whether the payload was altered or forged. To check the tag on the other side, pass it to Crypto.verify_hmac rather than comparing hex with ==.

+

Parameters

keythe shared secret; keep it out of the shipped client where you can
msgthe payload being signed
+

Example

program SignSave {
+  entry {
+    let key = "s3cret"
+    let payload = "score=9001;level=12"
+    let mac = Crypto.hmac_sha256(key, payload)
+    print(mac)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-random_bytes.html b/crypto-random_bytes.html new file mode 100644 index 00000000..c5a98b3f --- /dev/null +++ b/crypto-random_bytes.html @@ -0,0 +1,33 @@ + + + + + +Crypto.random_bytes — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.random_bytes
+
+ method +

Crypto.random_bytes

+
+ Crypto.random_bytes(n) -> string +

Draws n bytes from the operating system's cryptographically-secure random number generator and returns them as a 2n-character lowercase hex string. Use it for unguessable tokens, nonces, and session secrets — anything whose whole value is that an attacker cannot predict it. The result is hex rather than raw bytes for the same reason digests are: a str is null-terminated and raw random bytes can contain a zero byte, so hex is the form you can safely store and compare.

This is deliberately **non-deterministic** — it must never seed the lockstep simulation RNG (Random). Two calls return different values. On a platform without an OS CSPRNG (for example a bare wasm target) the draw degrades to zeroes rather than faulting; treat a real CSPRNG there as a platform-layer responsibility.

+

Parameters

nthe number of secure random bytes to draw
+

Example

program Token {
+  entry {
+    let session = Crypto.random_bytes(16)   # 32 hex chars, unguessable
+    print(len(session))                      # 32
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-random_hex.html b/crypto-random_hex.html new file mode 100644 index 00000000..c96d8769 --- /dev/null +++ b/crypto-random_hex.html @@ -0,0 +1,33 @@ + + + + + +Crypto.random_hex — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.random_hex
+
+ method +

Crypto.random_hex

+
+ Crypto.random_hex(n) -> string +

Identical to Crypto.random_bytes: draws n bytes from the OS CSPRNG and returns them as a 2n-character lowercase hex string. The two names are interchangeable; random_hex exists so call sites can be explicit that the return value is already hex text (not raw bytes) when that reads more clearly. The same determinism caveat applies — the result is unpredictable by design and must stay out of the reproducible simulation RNG.

+

Parameters

nthe number of secure random bytes to draw
+

Example

program Nonce {
+  entry {
+    let nonce = Crypto.random_hex(12)   # 24 hex chars
+    print(len(nonce))                    # 24
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-random_u32.html b/crypto-random_u32.html new file mode 100644 index 00000000..5489cda4 --- /dev/null +++ b/crypto-random_u32.html @@ -0,0 +1,34 @@ + + + + + +Crypto.random_u32 — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.random_u32
+
+ method +

Crypto.random_u32

+
+ Crypto.random_u32() -> int +

Draws four bytes from the OS CSPRNG and assembles them into one 32-bit integer. Use it when you need a single unpredictable number rather than a hex string — a random challenge value, a per-run identifier, or a non-deterministic seed to hand to a *fresh* Random stream at startup. Because the bytes come from the secure generator, the value prints as a signed integer and can be negative.

Like the other secure-random helpers this is **non-deterministic** and must not be called inside the lockstep simulation: doing so desyncs replays and networked peers. Draw it at the edges (startup, on connect) and, if you need reproducible gameplay randomness afterward, seed Random.seed with it once.

+ +

Example

program Challenge {
+  entry {
+    let c = Crypto.random_u32()
+    Random.seed(value: c)          # non-deterministic seed, chosen once at startup
+    print(Random.int(100))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-sha256.html b/crypto-sha256.html new file mode 100644 index 00000000..79c983c8 --- /dev/null +++ b/crypto-sha256.html @@ -0,0 +1,33 @@ + + + + + +Crypto.sha256 — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.sha256
+
+ method +

Crypto.sha256

+
+ Crypto.sha256(s) -> string +

Computes the SHA-256 digest of the bytes of s and returns it as a 64-character lowercase hex string. This is the standard, FIPS 180-4 algorithm — the same digest every other conforming implementation produces — so it is deterministic across platforms and runs and is backed by published known-answer vectors. Use it to fingerprint content, or as the building block under Crypto.hmac_sha256 for signing. On its own SHA-256 is not a message authentication code: anyone can recompute it, so it proves what the data is, not who produced it.

+

Parameters

sthe string whose bytes are hashed
+

Example

program Fingerprint {
+  entry {
+    let digest = Crypto.sha256("abc")
+    print(digest)   # ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/crypto-verify_hmac.html b/crypto-verify_hmac.html new file mode 100644 index 00000000..6dea19de --- /dev/null +++ b/crypto-verify_hmac.html @@ -0,0 +1,39 @@ + + + + + +Crypto.verify_hmac — Ludic + + + + + + +
+
API Reference › Crypto › Crypto.verify_hmac
+
+ method +

Crypto.verify_hmac

+
+ Crypto.verify_hmac(key, msg, mac) -> bool +

Recomputes HMAC-SHA256(key, msg) and compares it to the supplied mac hex string, returning true only if they match. The comparison is constant-time: it never stops early on the first differing character, so it does not leak — through how long the check took — how many leading bytes of a forged tag happened to be right. That leak is exactly what lets an attacker guess a MAC one byte at a time, which is why you should always verify with this and never with ==. A mismatched length returns false immediately (the length of a MAC is not a secret).

+

Parameters

keythe shared secret used to sign
msgthe payload as received
macthe hex tag to check, e.g. from Crypto.hmac_sha256
+

Example

program CheckSave {
+  entry {
+    let key = "s3cret"
+    let payload = "score=9001;level=12"
+    let mac = Crypto.hmac_sha256(key, payload)
+    if Crypto.verify_hmac(key, payload, mac) {
+      print(1)   # untampered
+    } else {
+      print(0)   # altered or forged
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-add_days.html b/date-add_days.html new file mode 100644 index 00000000..63b420c4 --- /dev/null +++ b/date-add_days.html @@ -0,0 +1,32 @@ + + + + + +Date.add_days — Ludic + + + + + + +
+
API Reference › Date › Date.add_days
+
+ method +

Date.add_days

+
+ Date.add_days(date, n) -> int +

The Date that is n days after date; pass a negative n to go backward. Rolls across month and year boundaries automatically.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let tomorrow = Date.add_days(Date.new(2026, 12, 31), 1)   # 2027-01-01
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-day.html b/date-day.html new file mode 100644 index 00000000..4596805e --- /dev/null +++ b/date-day.html @@ -0,0 +1,32 @@ + + + + + +Date.day — Ludic + + + + + + +
+
API Reference › Date › Date.day
+
+ method +

Date.day

+
+ Date.day(date) -> int +

The day of the month of a Date, from 1 to 31.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let d = Date.day(Date.new(2026, 8, 30))    # 30
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-days_in_month.html b/date-days_in_month.html new file mode 100644 index 00000000..ba1cbb51 --- /dev/null +++ b/date-days_in_month.html @@ -0,0 +1,32 @@ + + + + + +Date.days_in_month — Ludic + + + + + + +
+
API Reference › Date › Date.days_in_month
+
+ method +

Date.days_in_month

+
+ Date.days_in_month(year, month) -> int +

The number of days in month of year (28–31), accounting for leap Februaries.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let n = Date.days_in_month(2024, 2)   # 29
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-diff_days.html b/date-diff_days.html new file mode 100644 index 00000000..ff5a442a --- /dev/null +++ b/date-diff_days.html @@ -0,0 +1,32 @@ + + + + + +Date.diff_days — Ludic + + + + + + +
+
API Reference › Date › Date.diff_days
+
+ method +

Date.diff_days

+
+ Date.diff_days(a, b) -> int +

The number of whole days from b to a (that is, a - b). Negative when a precedes b.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let gap = Date.diff_days(Date.new(2026, 1, 1), Date.new(2025, 1, 1))   # 365
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-is_leap.html b/date-is_leap.html new file mode 100644 index 00000000..e93c12cc --- /dev/null +++ b/date-is_leap.html @@ -0,0 +1,32 @@ + + + + + +Date.is_leap — Ludic + + + + + + +
+
API Reference › Date › Date.is_leap
+
+ method +

Date.is_leap

+
+ Date.is_leap(year) -> bool +

Whether year is a leap year on the proleptic Gregorian calendar: divisible by 4, except centuries, which must be divisible by 400.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let leap = Date.is_leap(2024)   # true
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-month.html b/date-month.html new file mode 100644 index 00000000..857b331c --- /dev/null +++ b/date-month.html @@ -0,0 +1,32 @@ + + + + + +Date.month — Ludic + + + + + + +
+
API Reference › Date › Date.month
+
+ method +

Date.month

+
+ Date.month(date) -> int +

The calendar month of a Date, from 1 (January) to 12 (December).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let m = Date.month(Date.new(2026, 8, 30))  # 8
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-new.html b/date-new.html new file mode 100644 index 00000000..8176ffe6 --- /dev/null +++ b/date-new.html @@ -0,0 +1,32 @@ + + + + + +Date.new — Ludic + + + + + + +
+
API Reference › Date › Date.new
+
+ method +

Date.new

+
+ Date.new(year, month, day) -> int +

Builds a Date from a calendar year, month (1–12) and day (1–31), returning the count of days since 1970-01-01 (UTC). Read the parts back with Date.year/Date.month/Date.day.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let launch = Date.new(2026, 8, 30)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-to_epoch.html b/date-to_epoch.html new file mode 100644 index 00000000..ddeb3481 --- /dev/null +++ b/date-to_epoch.html @@ -0,0 +1,32 @@ + + + + + +Date.to_epoch — Ludic + + + + + + +
+
API Reference › Date › Date.to_epoch
+
+ method +

Date.to_epoch

+
+ Date.to_epoch(date) -> int +

The instant at midnight (00:00:00 UTC) of the given Date, as a DateTime (seconds since 1970). Bridges a calendar day back to the wall-clock timeline.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let t = Date.to_epoch(Date.new(2026, 8, 30))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-weekday.html b/date-weekday.html new file mode 100644 index 00000000..5d5285c8 --- /dev/null +++ b/date-weekday.html @@ -0,0 +1,32 @@ + + + + + +Date.weekday — Ludic + + + + + + +
+
API Reference › Date › Date.weekday
+
+ method +

Date.weekday

+
+ Date.weekday(date) -> int +

The day of the week of a Date, as 0 (Sunday) through 6 (Saturday). Useful for weekly events or daily-reward streaks.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let w = Date.weekday(Date.new(2000, 1, 1))  # 6 (Saturday)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/date-year.html b/date-year.html new file mode 100644 index 00000000..4297bc52 --- /dev/null +++ b/date-year.html @@ -0,0 +1,32 @@ + + + + + +Date.year — Ludic + + + + + + +
+
API Reference › Date › Date.year
+
+ method +

Date.year

+
+ Date.year(date) -> int +

The calendar year of a Date.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let y = Date.year(Date.new(2026, 8, 30))   # 2026
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-add.html b/datetime-add.html new file mode 100644 index 00000000..7e0ab8fb --- /dev/null +++ b/datetime-add.html @@ -0,0 +1,32 @@ + + + + + +DateTime.add — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.add
+
+ method +

DateTime.add

+
+ DateTime.add(dt, span) -> int +

The instant span seconds after dt. Pair it with the Duration constructors to express the offset in real units.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let ready_at = DateTime.add(Time.now(), Duration.hours(3))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-date.html b/datetime-date.html new file mode 100644 index 00000000..928c0e73 --- /dev/null +++ b/datetime-date.html @@ -0,0 +1,32 @@ + + + + + +DateTime.date — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.date
+
+ method +

DateTime.date

+
+ DateTime.date(dt) -> int +

The Date (days since 1970) that the instant dt falls on, dropping the time of day.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let today = DateTime.date(Time.now())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-day.html b/datetime-day.html new file mode 100644 index 00000000..01327181 --- /dev/null +++ b/datetime-day.html @@ -0,0 +1,32 @@ + + + + + +DateTime.day — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.day
+
+ method +

DateTime.day

+
+ DateTime.day(dt) -> int +

The day of the month of the instant dt (UTC), 1–31.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let d = DateTime.day(DateTime.from(2000, 3, 9, 0, 0, 0))   # 9
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-format.html b/datetime-format.html new file mode 100644 index 00000000..f025e08f --- /dev/null +++ b/datetime-format.html @@ -0,0 +1,33 @@ + + + + + +DateTime.format — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.format
+
+ method +

DateTime.format

+
+ DateTime.format(dt, pattern) -> string +

Renders the instant dt as a string, filling the tokens in pattern with zero-padded fields and copying every other character through verbatim. The pattern must be a string literal — it is expanded at compile time, so there is no runtime pattern scanner. Recognised tokens: YYYY (4-digit year), YY (2-digit year), MM (month), DD (day), HH (hour, 24h), mm (minute), ss (second).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let t = DateTime.from(2026, 8, 30, 7, 5, 9)
+    let stamp = DateTime.format(t, "YYYY-MM-DD HH:mm:ss")   # "2026-08-30 07:05:09"
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-from.html b/datetime-from.html new file mode 100644 index 00000000..1f7c4b59 --- /dev/null +++ b/datetime-from.html @@ -0,0 +1,32 @@ + + + + + +DateTime.from — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.from
+
+ method +

DateTime.from

+
+ DateTime.from(year, month, day, hour, minute, second) -> int +

Builds a DateTime (seconds since 1970, UTC) from calendar and clock parts. The inverse of the component readers below.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let t = DateTime.from(2000, 1, 1, 12, 30, 15)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-hour.html b/datetime-hour.html new file mode 100644 index 00000000..7390d7d1 --- /dev/null +++ b/datetime-hour.html @@ -0,0 +1,32 @@ + + + + + +DateTime.hour — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.hour
+
+ method +

DateTime.hour

+
+ DateTime.hour(dt) -> int +

The hour of the day of the instant dt (UTC), 0–23. Drives day/night cycles from a real clock.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let h = DateTime.hour(DateTime.from(2000, 1, 1, 12, 30, 15))   # 12
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-minute.html b/datetime-minute.html new file mode 100644 index 00000000..26b35798 --- /dev/null +++ b/datetime-minute.html @@ -0,0 +1,32 @@ + + + + + +DateTime.minute — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.minute
+
+ method +

DateTime.minute

+
+ DateTime.minute(dt) -> int +

The minute within the hour of the instant dt (UTC), 0–59.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let mi = DateTime.minute(DateTime.from(2000, 1, 1, 12, 30, 15))   # 30
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-month.html b/datetime-month.html new file mode 100644 index 00000000..5a83fc23 --- /dev/null +++ b/datetime-month.html @@ -0,0 +1,32 @@ + + + + + +DateTime.month — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.month
+
+ method +

DateTime.month

+
+ DateTime.month(dt) -> int +

The calendar month of the instant dt (UTC), 1–12.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let m = DateTime.month(DateTime.from(2000, 3, 9, 0, 0, 0))   # 3
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-parse.html b/datetime-parse.html new file mode 100644 index 00000000..7a88146d --- /dev/null +++ b/datetime-parse.html @@ -0,0 +1,35 @@ + + + + + +DateTime.parse — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.parse
+
+ method +

DateTime.parse

+
+ DateTime.parse(text, pattern) -> int +

Parses text into a DateTime by reading each token of pattern from the fixed position the pattern lays out; missing fields default to 1970-01-01 00:00:00. Returns -1 if a digit was expected but not found (so the caller can detect malformed input). The pattern must be a string literal and uses the same tokens as DateTime.format. text must match the pattern's fixed-width layout.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let t = DateTime.parse("2026-08-30 07:05:09", "YYYY-MM-DD HH:mm:ss")
+    if t < 0 {
+      # malformed input
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-second.html b/datetime-second.html new file mode 100644 index 00000000..e54ea790 --- /dev/null +++ b/datetime-second.html @@ -0,0 +1,32 @@ + + + + + +DateTime.second — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.second
+
+ method +

DateTime.second

+
+ DateTime.second(dt) -> int +

The second within the minute of the instant dt (UTC), 0–59.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let s = DateTime.second(DateTime.from(2000, 1, 1, 12, 30, 15))   # 15
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-weekday.html b/datetime-weekday.html new file mode 100644 index 00000000..33a2aeef --- /dev/null +++ b/datetime-weekday.html @@ -0,0 +1,32 @@ + + + + + +DateTime.weekday — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.weekday
+
+ method +

DateTime.weekday

+
+ DateTime.weekday(dt) -> int +

The day of the week of the instant dt (UTC), 0 (Sunday) through 6 (Saturday).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let w = DateTime.weekday(DateTime.from(2000, 1, 1, 12, 0, 0))   # 6
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/datetime-year.html b/datetime-year.html new file mode 100644 index 00000000..0de8c0dd --- /dev/null +++ b/datetime-year.html @@ -0,0 +1,32 @@ + + + + + +DateTime.year — Ludic + + + + + + +
+
API Reference › DateTime › DateTime.year
+
+ method +

DateTime.year

+
+ DateTime.year(dt) -> int +

The calendar year of the instant dt (UTC).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let y = DateTime.year(DateTime.from(2000, 1, 1, 12, 30, 15))   # 2000
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-add.html b/decimal-add.html new file mode 100644 index 00000000..06cb4fb4 --- /dev/null +++ b/decimal-add.html @@ -0,0 +1,32 @@ + + + + + +Decimal.add — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.add
+
+ method +

Decimal.add

+
+ Decimal.add(a, b) -> Decimal +

Returns the exact sum, aligning the two scales first so nothing is rounded. 0.10 + 0.20 is exactly 0.30.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let total = Decimal.add(subtotal, tax)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-cmp.html b/decimal-cmp.html new file mode 100644 index 00000000..fbb9406e --- /dev/null +++ b/decimal-cmp.html @@ -0,0 +1,32 @@ + + + + + +Decimal.cmp — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.cmp
+
+ method +

Decimal.cmp

+
+ Decimal.cmp(a, b) -> int +

Compares two decimals regardless of their scales, returning -1, 0, or 1.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Decimal.cmp(balance, price) < 0 { deny() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-eq.html b/decimal-eq.html new file mode 100644 index 00000000..e3d86b05 --- /dev/null +++ b/decimal-eq.html @@ -0,0 +1,32 @@ + + + + + +Decimal.eq — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.eq
+
+ method +

Decimal.eq

+
+ Decimal.eq(a, b) -> bool +

Returns true when two decimals are equal in value even if written at different scales — 1.5 equals 1.50.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Decimal.eq(paid, price) { accept() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-from.html b/decimal-from.html new file mode 100644 index 00000000..cad004b9 --- /dev/null +++ b/decimal-from.html @@ -0,0 +1,32 @@ + + + + + +Decimal.from — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.from
+
+ method +

Decimal.from

+
+ Decimal.from(value) -> Decimal +

Lifts a whole int into a Decimal with scale zero — a starting point for exact money arithmetic.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let qty = Decimal.from(3)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-mul.html b/decimal-mul.html new file mode 100644 index 00000000..b2d166e4 --- /dev/null +++ b/decimal-mul.html @@ -0,0 +1,32 @@ + + + + + +Decimal.mul — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.mul
+
+ method +

Decimal.mul

+
+ Decimal.mul(a, b) -> Decimal +

Returns the exact product; the result's scale is the sum of the operands' scales, so no precision is lost.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let line = Decimal.mul(price, quantity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-neg.html b/decimal-neg.html new file mode 100644 index 00000000..fb082444 --- /dev/null +++ b/decimal-neg.html @@ -0,0 +1,32 @@ + + + + + +Decimal.neg — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.neg
+
+ method +

Decimal.neg

+
+ Decimal.neg(a) -> Decimal +

Returns -a at the same scale — the same magnitude with the opposite sign.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let refund = Decimal.neg(charge)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-parse.html b/decimal-parse.html new file mode 100644 index 00000000..7732c963 --- /dev/null +++ b/decimal-parse.html @@ -0,0 +1,32 @@ + + + + + +Decimal.parse — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.parse
+
+ method +

Decimal.parse

+
+ Decimal.parse(text) -> Decimal +

Parses text like "19.99" or "0.10" into an exact Decimal, remembering how many digits followed the point. This is how you enter a price the way binary floating point never could.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let price = Decimal.parse("19.99")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-rescale.html b/decimal-rescale.html new file mode 100644 index 00000000..cb3ee800 --- /dev/null +++ b/decimal-rescale.html @@ -0,0 +1,32 @@ + + + + + +Decimal.rescale — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.rescale
+
+ method +

Decimal.rescale

+
+ Decimal.rescale(d, places) -> Decimal +

Changes the number of fractional digits. Increasing precision is exact; decreasing it truncates toward zero — how you round a computed price down to whole cents.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let cents = Decimal.rescale(raw, 2)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-scale.html b/decimal-scale.html new file mode 100644 index 00000000..fb8882ad --- /dev/null +++ b/decimal-scale.html @@ -0,0 +1,32 @@ + + + + + +Decimal.scale — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.scale
+
+ method +

Decimal.scale

+
+ Decimal.scale(d) -> int +

Returns the current scale — how many digits sit after the decimal point.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let places = Decimal.scale(price)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-str.html b/decimal-str.html new file mode 100644 index 00000000..1749431a --- /dev/null +++ b/decimal-str.html @@ -0,0 +1,32 @@ + + + + + +Decimal.str — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.str
+
+ method +

Decimal.str

+
+ Decimal.str(d) -> string +

Renders the value as text with its decimal point in place (a leading - when negative). This is how you display or serialize money.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Screen.draw_text(8, 8, Decimal.str(balance), Color.White, 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/decimal-sub.html b/decimal-sub.html new file mode 100644 index 00000000..2b4b5000 --- /dev/null +++ b/decimal-sub.html @@ -0,0 +1,32 @@ + + + + + +Decimal.sub — Ludic + + + + + + +
+
API Reference › Decimal › Decimal.sub
+
+ method +

Decimal.sub

+
+ Decimal.sub(a, b) -> Decimal +

Returns the exact difference a - b, aligning scales so balances stay penny-accurate.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let change = Decimal.sub(paid, price)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-clear.html b/dict-clear.html new file mode 100644 index 00000000..e67ae1ed --- /dev/null +++ b/dict-clear.html @@ -0,0 +1,32 @@ + + + + + +Dict.clear — Ludic + + + + + + +
+
API Reference › Dict › Dict.clear
+
+ method +

Dict.clear

+
+ Dict.clear(d) +

Empties the map, removing every key while keeping its allocated capacity.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Dict.clear(bank)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-get.html b/dict-get.html new file mode 100644 index 00000000..5b58329e --- /dev/null +++ b/dict-get.html @@ -0,0 +1,32 @@ + + + + + +Dict.get — Ludic + + + + + + +
+
API Reference › Dict › Dict.get
+
+ method +

Dict.get

+
+ Dict.get(d, key) -> int +

Returns the value stored for key, or 0 when the key is absent. Use Dict.get_or when 0 is a meaningful stored value.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let gold = Dict.get(bank, "gold")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-get_or.html b/dict-get_or.html new file mode 100644 index 00000000..cf98197c --- /dev/null +++ b/dict-get_or.html @@ -0,0 +1,32 @@ + + + + + +Dict.get_or — Ludic + + + + + + +
+
API Reference › Dict › Dict.get_or
+
+ method +

Dict.get_or

+
+ Dict.get_or(d, key, fallback) -> int +

Returns the value for key, or fallback when the key is absent — the unambiguous lookup when 0 could be a real value.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let n = Dict.get_or(bank, "stone", 0 - 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-has.html b/dict-has.html new file mode 100644 index 00000000..c5b451f0 --- /dev/null +++ b/dict-has.html @@ -0,0 +1,32 @@ + + + + + +Dict.has — Ludic + + + + + + +
+
API Reference › Dict › Dict.has
+
+ method +

Dict.has

+
+ Dict.has(d, key) -> bool +

Returns true when key is present in the map.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Dict.has(bank, "gold") { spend() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-keys.html b/dict-keys.html new file mode 100644 index 00000000..dc1f18b3 --- /dev/null +++ b/dict-keys.html @@ -0,0 +1,32 @@ + + + + + +Dict.keys — Ludic + + + + + + +
+
API Reference › Dict › Dict.keys
+
+ method +

Dict.keys

+
+ Dict.keys(d) -> []pointer +

Returns every live key as a slice of strings, in unspecified order — iterate it with len and indexing to walk the whole map.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let names = Dict.keys(bank)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-new.html b/dict-new.html new file mode 100644 index 00000000..a6e3082c --- /dev/null +++ b/dict-new.html @@ -0,0 +1,32 @@ + + + + + +Dict.new — Ludic + + + + + + +
+
API Reference › Dict › Dict.new
+
+ method +

Dict.new

+
+ Dict.new() -> Dict +

Creates an empty Dict mapping string keys to int values. Fill it with Dict.set and read it back with Dict.get.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let bank = Dict.new()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-remove.html b/dict-remove.html new file mode 100644 index 00000000..18d5fa47 --- /dev/null +++ b/dict-remove.html @@ -0,0 +1,32 @@ + + + + + +Dict.remove — Ludic + + + + + + +
+
API Reference › Dict › Dict.remove
+
+ method +

Dict.remove

+
+ Dict.remove(d, key) +

Removes key from the map (a no-op if it is absent). Later lookups of other keys are unaffected.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Dict.remove(bank, "wood")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-set.html b/dict-set.html new file mode 100644 index 00000000..bf07ea26 --- /dev/null +++ b/dict-set.html @@ -0,0 +1,32 @@ + + + + + +Dict.set — Ludic + + + + + + +
+
API Reference › Dict › Dict.set
+
+ method +

Dict.set

+
+ Dict.set(d, key, value) +

Associates value with key, inserting it or overwriting the previous value. The table grows automatically as it fills.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Dict.set(bank, "gold", 100)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/dict-size.html b/dict-size.html new file mode 100644 index 00000000..753236dc --- /dev/null +++ b/dict-size.html @@ -0,0 +1,32 @@ + + + + + +Dict.size — Ludic + + + + + + +
+
API Reference › Dict › Dict.size
+
+ method +

Dict.size

+
+ Dict.size(d) -> int +

Returns the number of keys currently in the map.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let kinds = Dict.size(bank)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/docs.css b/docs.css new file mode 100644 index 00000000..ca664822 --- /dev/null +++ b/docs.css @@ -0,0 +1,215 @@ +/* docs.css — every reference page: a symbol page, a namespace overview, the + * color palette, and the API index. Loaded after base.css, which owns the + * tokens. (This replaces the old item.css/api.css pair; api.css was never + * referenced by the generator at all.) + * + * A reference page is a document, so it is set as one: a measured column, a + * serif heading, a signature that reads as code, and rules instead of cards. */ + +main { padding: 34px 0 0 } + +/* ---------- breadcrumbs ---------- */ + +.crumbs { + font-size: 13px; + color: var(--muted); + display: flex; + flex-wrap: wrap; + gap: 7px; + margin-bottom: 20px; +} +.crumbs a { color: var(--muted) } +.crumbs a:hover { color: var(--ink) } +.crumbs .here { color: var(--ink) } + +/* ---------- symbol head ---------- */ + +.item { max-width: 78ch } +.item-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap } +.item-head h1 { + font-size: clamp(27px, 4vw, 36px); + letter-spacing: -0.02em; + word-break: break-word; +} + +.sig { + display: block; + margin: 18px 0 0; + padding: 11px 14px; + font-size: 14px; + background: var(--code-bg); + border: 1px solid var(--code-rule); + border-left: 2px solid var(--rule-2); + border-radius: var(--radius); + color: var(--ink); + overflow-x: auto; + white-space: pre; +} + +.desc { margin-top: 20px } +.desc p { margin: 0 0 14px } +.desc p:last-child { margin-bottom: 0 } + +.ns-blurb { margin: 20px 0 0; color: var(--ink-2) } + +/* ---------- sections within a page ---------- */ + +.params h2, .examples h2, .related h2, .swatch-group h3 { + font-family: var(--sans); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + margin: 34px 0 12px; + padding-bottom: 7px; + border-bottom: 1px solid var(--rule); +} + +.param { + display: grid; + gap: 3px 14px; + padding: 9px 0; + border-bottom: 1px solid var(--rule); +} +@media (min-width: 620px) { + .param { grid-template-columns: minmax(90px, 22%) 1fr; align-items: baseline } +} +.param:last-child { border-bottom: none } +.pname { color: var(--c-arg); font-size: 13.5px } +.pdesc { color: var(--ink-2); font-size: 15px } + +.examples pre { margin-top: 0 } +.examples pre + pre { margin-top: 12px } + +.rel-row { display: flex; flex-wrap: wrap; gap: 8px } +.rel { + font-family: var(--mono); + font-size: 13px; + color: var(--ink-2); + border: 1px solid var(--rule-2); + border-radius: var(--radius); + padding: 4px 10px; +} +.rel:hover { color: var(--accent); border-color: var(--accent); text-decoration: none } + +.back { + display: inline-block; + margin: 40px 0 0; + font-size: 14px; + color: var(--muted); +} +.back:hover { color: var(--ink) } + +/* ---------- namespace overview ---------- */ + +.ns-methods { margin-top: 26px; border-top: 1px solid var(--rule) } +.ns-method { + display: grid; + gap: 2px; + padding: 12px 2px; + border-bottom: 1px solid var(--rule); + color: inherit; +} +@media (min-width: 720px) { + .ns-method { grid-template-columns: minmax(0, 46%) 1fr; gap: 18px; align-items: baseline } +} +.ns-method:hover { background: var(--paper-2); text-decoration: none } +.nm-sig { font-size: 13.5px; color: var(--c-fn) } +.nm-tip { color: var(--muted); font-size: 14.5px } + +/* ---------- color palette ---------- */ + +.swatch-row { + display: grid; + border-top: 1px solid var(--rule); + border-left: 1px solid var(--rule); + border-radius: var(--radius); + overflow: hidden; + grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); +} +.swatch { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 11px; + font-size: 13px; + border-right: 1px solid var(--rule); + border-bottom: 1px solid var(--rule); +} +.swatch.flash { background: var(--accent-bg) } +.chip { + width: 17px; height: 17px; + flex: none; + border-radius: 3px; + border: 1px solid rgba(0, 0, 0, .18); +} +.cname { font-family: var(--mono); color: var(--ink); overflow: hidden; text-overflow: ellipsis } +.chex { font-family: var(--mono); color: var(--muted); margin-left: auto; font-size: 12px } + +/* ---------- API index ---------- */ + +.ref { max-width: var(--max) } +.ref-intro { max-width: 68ch; margin-bottom: 40px } +.ref-intro h1 { font-size: clamp(28px, 4vw, 38px); letter-spacing: -0.02em; margin: 0 0 12px } +.ref-intro p { color: var(--muted); margin: 0 0 22px } + +.search { + width: 100%; + font-family: var(--sans); + font-size: 15px; + color: var(--ink); + background: var(--panel); + border: 1px solid var(--rule-2); + border-radius: var(--radius); + padding: 11px 14px; +} +.search::placeholder { color: var(--muted) } +.search:focus { outline: none; border-color: var(--accent) } + +.noresults { margin-top: 14px; color: var(--muted); font-size: 14.5px } + +.idx-sec { margin-bottom: 44px } +.idx-sec h2 { + font-size: 20px; + margin: 0 0 6px; + padding-bottom: 9px; + border-bottom: 1px solid var(--rule); +} +.sec-blurb { color: var(--muted); font-size: 14.5px; margin: 0 0 16px; max-width: 74ch } + +.idx-grid { + display: grid; + border-top: 1px solid var(--rule); + border-left: 1px solid var(--rule); + border-radius: var(--radius); + overflow: hidden; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); +} +.idx-item { + display: grid; + gap: 1px; + padding: 11px 14px; + color: inherit; + border-right: 1px solid var(--rule); + border-bottom: 1px solid var(--rule); +} +.idx-item:hover { background: var(--paper-2); text-decoration: none } +.idx-item code { font-size: 13.5px; color: var(--c-fn) } +.idx-item span { color: var(--muted); font-size: 13.5px } + +/* ---------- deep-link flash (from ludic-highlight.js) -------------------- * + * The token cards themselves are styled in base.css, which every page links. */ + +:target { scroll-margin-top: calc(var(--hdr) + 16px) } +.flash { animation: flash 1.1s ease-out } +@keyframes flash { + from { background: var(--accent-bg) } + to { background: transparent } +} +/* No standing tint here: `animation: none` plus a background would leave the + highlight on the element forever, which is exactly what a reader who asked + for less motion did not ask for. */ +@media (prefers-reduced-motion: reduce) { + .flash { animation: none } +} diff --git a/duration-as_days.html b/duration-as_days.html new file mode 100644 index 00000000..a51f5643 --- /dev/null +++ b/duration-as_days.html @@ -0,0 +1,32 @@ + + + + + +Duration.as_days — Ludic + + + + + + +
+
API Reference › Duration › Duration.as_days
+
+ method +

Duration.as_days

+
+ Duration.as_days(span) -> int +

The span expressed in whole days, truncated toward zero (span / 86400).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let d = Duration.as_days(Duration.hours(50))       # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-as_hours.html b/duration-as_hours.html new file mode 100644 index 00000000..2bf202b1 --- /dev/null +++ b/duration-as_hours.html @@ -0,0 +1,32 @@ + + + + + +Duration.as_hours — Ludic + + + + + + +
+
API Reference › Duration › Duration.as_hours
+
+ method +

Duration.as_hours

+
+ Duration.as_hours(span) -> int +

The span expressed in whole hours, truncated toward zero (span / 3600). Useful for turning an elapsed-time gap into a reward count.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let h = Duration.as_hours(Duration.days(3))        # 72
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-as_minutes.html b/duration-as_minutes.html new file mode 100644 index 00000000..a3aca793 --- /dev/null +++ b/duration-as_minutes.html @@ -0,0 +1,32 @@ + + + + + +Duration.as_minutes — Ludic + + + + + + +
+
API Reference › Duration › Duration.as_minutes
+
+ method +

Duration.as_minutes

+
+ Duration.as_minutes(span) -> int +

The span expressed in whole minutes, truncated toward zero (span / 60).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let m = Duration.as_minutes(Duration.hours(2))     # 120
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-as_seconds.html b/duration-as_seconds.html new file mode 100644 index 00000000..8ff5e71a --- /dev/null +++ b/duration-as_seconds.html @@ -0,0 +1,32 @@ + + + + + +Duration.as_seconds — Ludic + + + + + + +
+
API Reference › Duration › Duration.as_seconds
+
+ method +

Duration.as_seconds

+
+ Duration.as_seconds(span) -> int +

The span expressed in whole seconds. The inverse of Duration.seconds; also the identity, provided for symmetry with the other readers.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let s = Duration.as_seconds(Duration.minutes(3))   # 180
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-days.html b/duration-days.html new file mode 100644 index 00000000..c7df6612 --- /dev/null +++ b/duration-days.html @@ -0,0 +1,32 @@ + + + + + +Duration.days — Ludic + + + + + + +
+
API Reference › Duration › Duration.days
+
+ method +

Duration.days

+
+ Duration.days(n) -> int +

A span of n days, returned in seconds (n * 86400).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let week = Duration.days(7)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-hours.html b/duration-hours.html new file mode 100644 index 00000000..5fa81c49 --- /dev/null +++ b/duration-hours.html @@ -0,0 +1,32 @@ + + + + + +Duration.hours — Ludic + + + + + + +
+
API Reference › Duration › Duration.hours
+
+ method +

Duration.hours

+
+ Duration.hours(n) -> int +

A span of n hours, returned in seconds (n * 3600). Handy as a threshold for offline-progress checks.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let full_day = Duration.hours(24)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-minutes.html b/duration-minutes.html new file mode 100644 index 00000000..e49eaa48 --- /dev/null +++ b/duration-minutes.html @@ -0,0 +1,32 @@ + + + + + +Duration.minutes — Ludic + + + + + + +
+
API Reference › Duration › Duration.minutes
+
+ method +

Duration.minutes

+
+ Duration.minutes(n) -> int +

A span of n minutes, returned in seconds (n * 60).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let respawn = Duration.minutes(2)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/duration-seconds.html b/duration-seconds.html new file mode 100644 index 00000000..ee170e44 --- /dev/null +++ b/duration-seconds.html @@ -0,0 +1,32 @@ + + + + + +Duration.seconds — Ludic + + + + + + +
+
API Reference › Duration › Duration.seconds
+
+ method +

Duration.seconds

+
+ Duration.seconds(n) -> int +

A span of n whole seconds. Since a duration is just an int count of seconds, this is the identity — it exists so the unit is explicit at the call site.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let cooldown = Duration.seconds(45)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-back.html b/ease-back.html new file mode 100644 index 00000000..4e44afe8 --- /dev/null +++ b/ease-back.html @@ -0,0 +1,32 @@ + + + + + +Ease.back — Ludic + + + + + + +
+
API Reference › Ease › Ease.back
+
+ method +

Ease.back

+
+ Ease.back(t) -> fixed +

Returns an ease-in that first dips slightly below 0 before shooting forward — the anticipation windup that gives a motion snap. Because it undershoots, the eased value can go negative near the start, so use it where a little overshoot reads as lively rather than wrong.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let s = Math.lerp(rest, poke, Ease.back(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-bounce.html b/ease-bounce.html new file mode 100644 index 00000000..aa4a0eba --- /dev/null +++ b/ease-bounce.html @@ -0,0 +1,32 @@ + + + + + +Ease.bounce — Ludic + + + + + + +
+
API Reference › Ease › Ease.bounce
+
+ method +

Ease.bounce

+
+ Ease.bounce(t) -> fixed +

Returns an ease-out that overshoots and bounces a few times before settling at 1.0, like a ball dropping to the floor. Great for coins landing, badges popping, or a menu that lands with character.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let y = Math.lerp(top, floor_y, Ease.bounce(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-elastic.html b/ease-elastic.html new file mode 100644 index 00000000..0bc52f7e --- /dev/null +++ b/ease-elastic.html @@ -0,0 +1,32 @@ + + + + + +Ease.elastic — Ludic + + + + + + +
+
API Reference › Ease › Ease.elastic
+
+ method +

Ease.elastic

+
+ Ease.elastic(t) -> fixed +

Returns an ease-out that shoots past 1.0 and wobbles around it a few times with shrinking amplitude before settling — a spring released under tension. The overshoot comes from a decaying 2-10t envelope on a sine, all in Q16.16, so it is deterministic and bit-identical on every platform. Great for UI that snaps in with personality: a panel that springs open, a picked-up item that jiggles, a selection that bounces to place.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let scale = Math.lerp(0.0, 1.0, Ease.elastic(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-in.html b/ease-in.html new file mode 100644 index 00000000..0e257658 --- /dev/null +++ b/ease-in.html @@ -0,0 +1,32 @@ + + + + + +Ease.in — Ludic + + + + + + +
+
API Reference › Ease › Ease.in
+
+ method +

Ease.in

+
+ Ease.in(t) -> fixed +

Returns t * t — motion that starts slow and speeds up. Feed the result to Math.lerp as the blend amount so a value accelerates into its target. The input t is expected in 0.0..1.0.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let x = Math.lerp(start_x, end_x, Ease.in(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-in_out.html b/ease-in_out.html new file mode 100644 index 00000000..500ee2df --- /dev/null +++ b/ease-in_out.html @@ -0,0 +1,32 @@ + + + + + +Ease.in_out — Ludic + + + + + + +
+
API Reference › Ease › Ease.in_out
+
+ method +

Ease.in_out

+
+ Ease.in_out(t) -> fixed +

Returns the smooth 3t^2 - 2t^3 S-curve: slow at both ends, fastest in the middle. Use it for a polished there-and-back or point-to-point move that neither jerks off the mark nor slams into the target.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let a = Math.lerp(0.0, 1.0, Ease.in_out(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ease-out.html b/ease-out.html new file mode 100644 index 00000000..18e4c30b --- /dev/null +++ b/ease-out.html @@ -0,0 +1,32 @@ + + + + + +Ease.out — Ludic + + + + + + +
+
API Reference › Ease › Ease.out
+
+ method +

Ease.out

+
+ Ease.out(t) -> fixed +

Returns t * (2 - t) — motion that starts fast and slows as it arrives. The most natural-feeling easing for things settling into place, like a panel sliding in or a camera coming to rest.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let y = Math.lerp(top, rest_y, Ease.out(progress))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-close.html b/file-close.html new file mode 100644 index 00000000..c12bd3d4 --- /dev/null +++ b/file-close.html @@ -0,0 +1,29 @@ + + + + + +File.close — Ludic + + + + + + +
+
API Reference › File › File.close
+
+ method +

File.close

+
+ File.close(file: f) +

Flushes and releases the handle. Every successful File.open pairs with one File.close.

+ +

Example

# doc-check: skip — illustrative
+File.close(file: f)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-open.html b/file-open.html new file mode 100644 index 00000000..f04cda2e --- /dev/null +++ b/file-open.html @@ -0,0 +1,30 @@ + + + + + +File.open — Ludic + + + + + + +
+
API Reference › File › File.open
+
+ method +

File.open

+
+ File.open(path: p, mode: m) -> pointer +

Opens path with a C fopen mode string ("rb", "wb", "ab"). Returns the handle, or null when the file cannot be opened — always test for it.

+ +

Example

# doc-check: skip — illustrative
+let f = File.open(path: "save/best.txt", mode: "rb")
+if f == null { return }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-read.html b/file-read.html new file mode 100644 index 00000000..18f11c89 --- /dev/null +++ b/file-read.html @@ -0,0 +1,30 @@ + + + + + +File.read — Ludic + + + + + + +
+
API Reference › File › File.read
+
+ method +

File.read

+
+ File.read(file: f, buffer: b, count: n) -> int +

Reads at most count bytes from the handle into buffer (a bytes allocation) and returns how many arrived; fewer than asked means the end of the file.

+ +

Example

# doc-check: skip — illustrative
+let buf = bytes(size + 1)
+let got = File.read(file: f, buffer: buf, count: size)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-seek.html b/file-seek.html new file mode 100644 index 00000000..d71b0a4e --- /dev/null +++ b/file-seek.html @@ -0,0 +1,31 @@ + + + + + +File.seek — Ludic + + + + + + +
+
API Reference › File › File.seek
+
+ method +

File.seek

+
+ File.seek(file: f, offset: o, whence: w) +

Positions the cursor at offset bytes from whence: 0 the start, 1 the current position, 2 the end. Seeking to the end and reading File.tell measures a file.

+ +

Example

# doc-check: skip — illustrative
+File.seek(file: f, offset: 0, whence: 2)
+let size = File.tell(file: f)
+File.seek(file: f, offset: 0, whence: 0)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-tell.html b/file-tell.html new file mode 100644 index 00000000..f1ea88d0 --- /dev/null +++ b/file-tell.html @@ -0,0 +1,29 @@ + + + + + +File.tell — Ludic + + + + + + +
+
API Reference › File › File.tell
+
+ method +

File.tell

+
+ File.tell(file: f) -> int +

Returns the current cursor position in bytes from the start of the file.

+ +

Example

# doc-check: skip — illustrative
+let size = File.tell(file: f)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/file-write.html b/file-write.html new file mode 100644 index 00000000..5ee1f2e0 --- /dev/null +++ b/file-write.html @@ -0,0 +1,30 @@ + + + + + +File.write — Ludic + + + + + + +
+
API Reference › File › File.write
+
+ method +

File.write

+
+ File.write(file: f, buffer: b, count: n) -> int +

Writes count bytes of buffer (a string or a bytes allocation) to the handle and returns how many were written.

+ +

Example

# doc-check: skip — illustrative
+let line = "best_floor=3\n"
+File.write(file: f, buffer: line, count: len(line))
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-abs.html b/fn-abs.html new file mode 100644 index 00000000..5bb6fc21 --- /dev/null +++ b/fn-abs.html @@ -0,0 +1,47 @@ + + + + + +abs — Ludic + + + + + + +
+ +
+ function +

abs

+
+ abs(a) -> int +

Returns the magnitude of an integer, dropping its sign — so abs(-7) and abs(7) both give 7. It is handy for distance-style checks where direction does not matter, such as measuring how far two grid cells are apart along one axis before deciding whether something is "close enough". Combine two axis distances to build a simple proximity test. It takes a single integer and returns an integer.

+

Parameters

athe integer whose magnitude you want
+

Example

program Proximity {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+  model Enemy { Position }
+
+  handler SpawnActors phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+    spawn Enemy { Position { column: 8, row: 6 } }
+  }
+
+  handler CheckAdjacency phase Update {
+    for (PlayerPosition) in query [Position, {Player}] {
+      for (EnemyPosition) in query [Position, {Enemy}] {
+        let column_gap = abs(PlayerPosition.column - EnemyPosition.column)
+        let row_gap = abs(PlayerPosition.row - EnemyPosition.row)
+        if column_gap + row_gap <= 1 { print(1) }
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-arg.html b/fn-arg.html new file mode 100644 index 00000000..b8994278 --- /dev/null +++ b/fn-arg.html @@ -0,0 +1,36 @@ + + + + + +arg — Ludic + + + + + + +
+ +
+ function +

arg

+
+ arg(i) -> string +

Returns the command-line argument at index i as a string. Index 0 is conventionally the program's own name or path, so the first user-supplied argument is at index 1. Use it together with arg_count to write configurable command-line tools in Ludic — reading an input filename, a mode flag, or a numeric option. Guard the index against arg_count() before reading so you never ask for an argument that was not passed.

+

Parameters

ithe argument index (0 is the program name)
+

Example

program EchoFirstArg {
+  handler ShowArg phase Start {
+    if arg_count() > 1 {
+      print(arg(1))
+    } else {
+      print("no argument")
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-arg_count.html b/fn-arg_count.html new file mode 100644 index 00000000..7127f608 --- /dev/null +++ b/fn-arg_count.html @@ -0,0 +1,35 @@ + + + + + +arg_count — Ludic + + + + + + +
+ +
+ function +

arg_count

+
+ arg_count() -> int +

Returns how many command-line arguments the program received, including the program name at index 0. So a program run with no user arguments reports 1, and each added argument raises the count by one. Use it to validate that the required arguments were supplied and to bound the index you pass to arg. It takes no arguments and is the natural companion to arg when building command-line tools.

+ +

Example

program PrintAllArgs {
+  handler ListArgs phase Start {
+    print(arg_count())
+    for index in 0 .. arg_count() {
+      print(arg(index))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-assert.html b/fn-assert.html new file mode 100644 index 00000000..aa9a2703 --- /dev/null +++ b/fn-assert.html @@ -0,0 +1,38 @@ + + + + + +assert — Ludic + + + + + + +
+ +
+ function +

assert

+
+ assert(cond: bool, msg: string) +

When cond is false, prints file:line: assertion failed: <msg> to standard error and aborts (exit code 1); when it is true, execution continues. It is the guarded form of panic — for the programmer-bug cases the language should catch loudly rather than let corrupt the world: an index that must be in range, a value that must be non-negative, a state that must hold before a step. The location is baked in at compile time, so a failure points at the exact assertion. Assertions document and enforce the invariants a system relies on; keep them for "this must be true" checks, not for recoverable runtime conditions.

+

Parameters

condthe invariant that must hold (a bool)
msga message describing the invariant (a string)
+

Example

program Demo {
+  function withdraw(balance: int, amount: int) -> int {
+    assert(amount >= 0, "amount must be non-negative")
+    assert(amount <= balance, "cannot overdraw")
+    return balance - amount
+  }
+  entry {
+    print(withdraw(100, 30))   # 70
+    print(withdraw(100, 250))  # aborts: Demo.ludic:4: assertion failed: cannot overdraw
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-bytes.html b/fn-bytes.html new file mode 100644 index 00000000..1c79923a --- /dev/null +++ b/fn-bytes.html @@ -0,0 +1,38 @@ + + + + + +bytes — Ludic + + + + + + +
+ +
+ function +

bytes

+
+ bytes(n) -> pointer +

Allocates a raw, byte-addressable buffer of n bytes and returns a pointer to its start. It is a low-level primitive used for I/O and networking scratch space — for example a buffer to serialize the world into, or to receive bytes from a socket. Size it to what you need; a common pattern is bytes(world_size()) so the buffer is exactly large enough for a full world snapshot. For a buffer of 32-bit words rather than individual bytes, use words instead.

+

Parameters

nthe number of bytes to allocate
+

Example

program SnapshotBuffer {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Snapshot phase Start {
+    spawn Player { Position { column: 3, row: 4 } }
+    let buffer = bytes(world_size())
+    let written = world_save(buffer)
+    print(written)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-clamp.html b/fn-clamp.html new file mode 100644 index 00000000..e2c16ad0 --- /dev/null +++ b/fn-clamp.html @@ -0,0 +1,61 @@ + + + + + +clamp — Ludic + + + + + + +
+ +
+ function +

clamp

+
+ clamp(v, lo, hi) -> int +

Constrains a value to a range: it returns lo if v is below the range, hi if it is above, and v unchanged when it already lies within [lo, hi]. The classic use is keeping a moving object on screen or inside a grid, so its position can never run past the walls no matter how fast it moves. It saves you writing a pair of if checks by hand. Make sure lo is not greater than hi.

+

Parameters

vthe value to constrain
lothe lowest allowed value (inclusive)
hithe highest allowed value (inclusive)
+

Example

program ClampToGrid {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const GRID_WIDTH: int = 20
+  const GRID_HEIGHT: int = 15
+  const TILE_SIZE: int = 16
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+  }
+
+  handler ReadInput phase Input {
+    let pressed = Input.key()
+    for (Position) in query [Position, {Player}] {
+      if pressed == 'd' { Position.column = Position.column + 1 }
+      if pressed == 'a' { Position.column = Position.column - 1 }
+      Position.column = clamp(Position.column, 0, GRID_WIDTH - 1)
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-err.html b/fn-err.html new file mode 100644 index 00000000..854ba5ab --- /dev/null +++ b/fn-err.html @@ -0,0 +1,37 @@ + + + + + +err — Ludic + + + + + + +
+ +
+ function +

err

+
+ err(msg: string) -> result +

Wraps a failure in a result, carrying a message that says what went wrong. It is the counterpart to ok: a fallible function (declared -> result) returns err("…") on the sad path instead of crashing or returning a magic sentinel. The caller recovers with try/else — the else block sees the message as error — or tests it with is_err. Unlike panic, which aborts, err is a value: the program keeps running and chooses a fallback.

+

Parameters

msga message describing the failure (a string)
+

Example

program Config {
+  function volume(pct: int) -> result {
+    if pct < 0 or pct > 100 { return err("volume out of range") }
+    return ok(pct)
+  }
+  test "err drives the fallback" {
+    let v = try volume(150) else { 100 }    # clamp to a safe default
+    expect_eq(v, 100)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-exit.html b/fn-exit.html new file mode 100644 index 00000000..e7c51448 --- /dev/null +++ b/fn-exit.html @@ -0,0 +1,37 @@ + + + + + +exit — Ludic + + + + + + +
+ +
+ function +

exit

+
+ exit(code) +

Terminates the process at once with the given status code, without waiting for the current frame to finish. This is a harder stop than quit, which ends the game loop cleanly after the frame completes; use exit for command-line tools and tests that need to signal success or failure to the shell, where 0 conventionally means success and any non-zero value means an error. Because it stops immediately, drawing queued for the current frame is not presented.

+

Parameters

codethe process exit status (0 for success, non-zero for failure)
+

Example

program RequireArgument {
+  handler CheckArgs phase Start {
+    if arg_count() < 2 {
+      print(0)
+      exit(1)
+    }
+    print(1)
+    exit(0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-file_stderr.html b/fn-file_stderr.html new file mode 100644 index 00000000..c751893b --- /dev/null +++ b/fn-file_stderr.html @@ -0,0 +1,38 @@ + + + + + +file_stderr — Ludic + + + + + + +
+
API Reference › Builtin functions › file_stderr
+
+ function +

file_stderr

+
+ file_stderr() -> pointer +

Returns the handle for the standard-error stream, which you pass to file_write to emit raw bytes. Writing diagnostics and error messages here keeps them separate from normal program output on standard out, so a tool's real results are not mixed with its warnings. It takes no arguments and always refers to the process's standard error. Use it for the error side of command-line tools written in Ludic.

+ +

Example

program WarnToStderr {
+  handler CheckArgs phase Start {
+    if arg_count() < 2 {
+      let err = file_stderr()
+      let message = "missing argument\n"
+      file_write(err, message, len(message))
+      exit(1)
+    }
+    print("ok")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-file_stdout.html b/fn-file_stdout.html new file mode 100644 index 00000000..be088a2c --- /dev/null +++ b/fn-file_stdout.html @@ -0,0 +1,34 @@ + + + + + +file_stdout — Ludic + + + + + + +
+
API Reference › Builtin functions › file_stdout
+
+ function +

file_stdout

+
+ file_stdout() -> pointer +

Returns the handle for the standard-output stream, which you pass to file_write to emit raw bytes. Unlike print, which always appends a newline and handles conversion, writing through this handle gives you exact control over what bytes go out and when — useful for tooling and code generators that must emit precise text. It takes no arguments and always refers to the process's standard output. Pair it with file_stderr when you want to separate normal output from diagnostics.

+ +

Example

program WriteLine {
+  handler Emit phase Start {
+    let out = file_stdout()
+    let message = "built\n"
+    file_write(out, message, len(message))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-file_write.html b/fn-file_write.html new file mode 100644 index 00000000..41760126 --- /dev/null +++ b/fn-file_write.html @@ -0,0 +1,36 @@ + + + + + +file_write — Ludic + + + + + + +
+
API Reference › Builtin functions › file_write
+
+ function +

file_write

+
+ file_write(handle, buf, len) +

Writes len bytes from buf to the given stream handle. Get the handle from file_stdout or file_stderr; the buffer is typically a string (whose length you pass with len(buf)) or a raw buffer from bytes. This is the precise, no-frills output primitive that underpins Ludic's own tooling and code generators, where exact bytes matter and the automatic newline of print would get in the way. Pass a byte count that does not exceed the buffer's size.

+

Parameters

handlea stream handle from file_stdout or file_stderr
bufthe bytes to write (a string or a raw buffer)
lenhow many bytes to write
+

Example

program WriteTwice {
+  handler Emit phase Start {
+    let out = file_stdout()
+    let heading = "SCORE: "
+    let value = "250\n"
+    file_write(out, heading, len(heading))
+    file_write(out, value, len(value))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-fixed.html b/fn-fixed.html new file mode 100644 index 00000000..195c6891 --- /dev/null +++ b/fn-fixed.html @@ -0,0 +1,35 @@ + + + + + +fixed — Ludic + + + + + + +
+ +
+ function +

fixed

+
+ fixed(x) -> fixed +

Converts an integer into a fixed value (Ludic's Q16.16 fixed-point type), so it can take part in fractional arithmetic. fixed is how you carry deterministic sub-pixel precision for smooth movement and physics-like accumulation; a float or double converts too, truncated toward zero to Q16.16. Use fixed when you need to combine an int with fixed-point values or divide to get a fraction — for example fixed(1) / fixed(4) is 0.25. Convert back to a whole number for drawing with flr.

+

Parameters

xthe integer (or float) to convert
+

Example

program SmoothAccumulate {
+  handler ComputeStep phase Start {
+    let full_speed = fixed(3)
+    let half_speed = full_speed / fixed(2)
+    let two_steps = half_speed + half_speed
+    print(floor(two_steps))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-float.html b/fn-float.html new file mode 100644 index 00000000..574a5912 --- /dev/null +++ b/fn-float.html @@ -0,0 +1,34 @@ + + + + + +float — Ludic + + + + + + +
+ +
+ function +

float

+
+ float(x) -> float / double(x) -> double +

Converts an int, long, fixed, float or double to a float (or, with double(x), to a double). It is the one conversion that crosses from fixed-point to floating point: float(fixed(1)) / 4.0 is 0.25. A decimal literal converts exactly, so float(0.1) is the nearest float to 0.1, not the nearest Q16.16 value.

+

Parameters

xthe number to convert
+

Example

program Convert {
+  entry {
+    let half = fixed(1) / 2
+    print(float(half) * 3.0)           # 1.5
+    print(double(7) / 2)               # 3.5
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-float_bits.html b/fn-float_bits.html new file mode 100644 index 00000000..0e42ba90 --- /dev/null +++ b/fn-float_bits.html @@ -0,0 +1,33 @@ + + + + + +float_bits — Ludic + + + + + + +
+
API Reference › Builtin functions › float_bits
+
+ function +

float_bits

+
+ float_bits(x) -> int / float_from_bits(bits) -> float +

float_bits(x) returns the 32 bits of a float as an int, and float_from_bits(bits) reads them back. double_bits / double_from_bits do the same for a double with a long. Use them where a float has to travel as raw data: a file format, a network packet, or a words buffer shared with code that still stores bit patterns.

+

Parameters

xthe float to encode (float_bits), or the bits to decode (float_from_bits)
+

Example

program Bits {
+  entry {
+    print(float_bits(1.0))             # 1065353216 (0x3F800000)
+    print(float_from_bits(0x40000000)) # 2.0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-floor.html b/fn-floor.html new file mode 100644 index 00000000..09df4255 --- /dev/null +++ b/fn-floor.html @@ -0,0 +1,38 @@ + + + + + +floor — Ludic + + + + + + +
+ +
+ function +

floor

+
+ floor(x) -> int +

Converts a fixed (Q16.16) value back to an int by discarding the fractional part, rounding toward negative infinity. It is the counterpart to fx: you accumulate motion in fixed-point for sub-pixel smoothness, then floor the result to get the whole-pixel column or row to draw at. Because it floors rather than rounds, floor(fixed(3) / fixed(2)) is 1, not 2. Use it wherever a fixed value must become an integer coordinate, count, or index.

+

Parameters

xthe fixed-point value to floor
+

Example

program FixedToPixels {
+  const TILE_SIZE: int = 16
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    let smooth_column = fixed(5) + fixed(1) / fixed(2)
+    let pixel_x = floor(smooth_column) * TILE_SIZE
+    Screen.fill_rectangle(x: pixel_x, y: 32, width: TILE_SIZE, height: TILE_SIZE, color: Color.LimeGreen)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-getenv.html b/fn-getenv.html new file mode 100644 index 00000000..e405600b --- /dev/null +++ b/fn-getenv.html @@ -0,0 +1,37 @@ + + + + + +getenv — Ludic + + + + + + +
+ +
+ function +

getenv

+
+ getenv(name) -> string +

Returns the value of the named environment variable, or an empty string when that variable is not set. Use it to make command-line tools and headless runs configurable without recompiling — reading a data directory, a log level, or a seed from the environment. Check for the empty string to detect an unset variable and fall back to a default. Like the other system builtins, it is aimed at tooling rather than a shipped game loop.

+

Parameters

namethe environment variable name to read
+

Example

program ReadConfig {
+  handler ShowConfig phase Start {
+    let level = getenv("LUDIC_LEVEL")
+    if len(level) == 0 {
+      print("default")
+    } else {
+      print(level)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-int.html b/fn-int.html new file mode 100644 index 00000000..ce3c1d3d --- /dev/null +++ b/fn-int.html @@ -0,0 +1,34 @@ + + + + + +int — Ludic + + + + + + +
+ +
+ function +

int

+
+ int(x) -> int / long(x) -> long +

Converts a float, double, fixed or long to an int (or, with long(x), to a long). Floating-point values truncate toward zero, as in C: int(2.9) is 2 and int(-2.9) is -2. A fixed value rounds down, like floor. For other rounding use Math.floor, Math.ceil or Math.round.

+

Parameters

xthe number to convert
+

Example

program Whole {
+  entry {
+    let pixels: float = 12.75
+    print(int(pixels))                 # 12
+    print(long(pixels * 1000.0))       # 12750
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-is_err.html b/fn-is_err.html new file mode 100644 index 00000000..706eca2f --- /dev/null +++ b/fn-is_err.html @@ -0,0 +1,37 @@ + + + + + +is_err — Ludic + + + + + + +
+ +
+ function +

is_err

+
+ is_err(r: result) -> bool +

Tests a result's tag: true when it was built with err, false when it was built with ok. Use it to detect the failure case explicitly — for example to count or log failures — while try/else handles recovering a value. It is the exact negation of is_ok.

+

Parameters

rthe result to test
+

Example

program Retry {
+  function step(n: int) -> result {
+    if n < 3 { return err("not ready") }
+    return ok(n)
+  }
+  test "is_err detects the failure case" {
+    expect(is_err(step(1)))
+    expect(not is_err(step(5)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-is_none.html b/fn-is_none.html new file mode 100644 index 00000000..f62eae26 --- /dev/null +++ b/fn-is_none.html @@ -0,0 +1,33 @@ + + + + + +is_none — Ludic + + + + + + +
+ +
+ function +

is_none

+
+ is_none(o: option) -> bool +

Returns true when an option is empty (it is none). The complement of is_some.

+

Parameters

othe option to test
+

Example

program AI {
+  handler Step phase Update {
+    let target = none()
+    if is_none(target) { wander() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-is_ok.html b/fn-is_ok.html new file mode 100644 index 00000000..cac5f23e --- /dev/null +++ b/fn-is_ok.html @@ -0,0 +1,37 @@ + + + + + +is_ok — Ludic + + + + + + +
+ +
+ function +

is_ok

+
+ is_ok(r: result) -> bool +

Tests a result's tag: true when it was built with ok, false when it was built with err. Use it when you want to branch on success without unwrapping the payload — the payload itself is recovered with try/else. It is the exact negation of is_err.

+

Parameters

rthe result to test
+

Example

program Check {
+  function find(x: int) -> result {
+    if x == 7 { return ok(x) }
+    return err("missing")
+  }
+  test "is_ok classifies without unwrapping" {
+    expect(is_ok(find(7)))
+    expect(not is_ok(find(3)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-is_some.html b/fn-is_some.html new file mode 100644 index 00000000..100287fe --- /dev/null +++ b/fn-is_some.html @@ -0,0 +1,33 @@ + + + + + +is_some — Ludic + + + + + + +
+ +
+ function +

is_some

+
+ is_some(o: option) -> bool +

Returns true when an option holds a value (it is some, not none) — the guard before acting on an optional target or slot.

+

Parameters

othe option to test
+

Example

program AI {
+  handler Step phase Update {
+    let target = none()
+    if is_some(target) { chase(unwrap_or(target, 0)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-len.html b/fn-len.html new file mode 100644 index 00000000..5f742453 --- /dev/null +++ b/fn-len.html @@ -0,0 +1,36 @@ + + + + + +len — Ludic + + + + + + +
+ +
+ function +

len

+
+ len(x) -> int +

Returns the length of its argument: the element count of a slice, or the byte length of a string. Use it to iterate a slice with for index in 0 .. len(items), to check whether a collection is empty, or to compute string slice bounds such as trimming a suffix. It reads the current length, so after you push onto a slice the value it returns grows accordingly. For strings, note it counts bytes, not visual characters.

+

Parameters

xa slice or string to measure
+

Example

program CountEnemies {
+  handler ReportCount phase Start {
+    let wave_sizes = new []int
+    push(wave_sizes, 3)
+    push(wave_sizes, 5)
+    push(wave_sizes, 8)
+    print(len(wave_sizes))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-load.html b/fn-load.html new file mode 100644 index 00000000..bee2b035 --- /dev/null +++ b/fn-load.html @@ -0,0 +1,48 @@ + + + + + +load — Ludic + + + + + + +
+ +
+ function +

load

+
+ load() -> bool +

Restores the entire world from the snapshot most recently written by save() — every model instance, property, and program var returns to its saved state. It returns a bool: true when a snapshot existed and was restored, false when there was nothing to load, so you can guard the call and avoid clobbering the current world by mistake. Use it for quick-load, respawning at a checkpoint, or resetting a test to a known state. It takes no arguments.

+ +

Example

program QuickLoad {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+    save()
+  }
+
+  handler ReadInput phase Input {
+    if Input.key() == 'r' {
+      let restored = load()
+      if restored { print(1) }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-max.html b/fn-max.html new file mode 100644 index 00000000..d8b25128 --- /dev/null +++ b/fn-max.html @@ -0,0 +1,43 @@ + + + + + +max — Ludic + + + + + + +
+ +
+ function +

max

+
+ max(a, b) -> int +

Returns whichever of its two integer arguments is larger. Use it to enforce a lower bound (for example clamping a countdown so it never drops below zero with max(remaining, 0)), to track a running high score, or to pick the greater of two candidate values. For the smaller of two values use min, and to bound a value on both sides at once use clamp.

+

Parameters

athe first value
bthe second value
+

Example

program HighScore {
+  var score: int = 0
+  var best_score: int = 0
+
+  handler EarnPoints phase Update {
+    score = score + 5
+    best_score = max(best_score, score)
+    if score >= 20 { quit() }
+  }
+
+  handler ReportBest phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 8, y: 8, value: best_score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-min.html b/fn-min.html new file mode 100644 index 00000000..b04a22aa --- /dev/null +++ b/fn-min.html @@ -0,0 +1,49 @@ + + + + + +min — Ludic + + + + + + +
+ +
+ function +

min

+
+ min(a, b) -> int +

Returns whichever of its two integer arguments is smaller. Use it to enforce an upper bound — for instance capping healing so it never exceeds a maximum with min(current + heal, maximum) — or to pick the lesser of two candidate values. For the larger of two values use max, and to bound a value between a low and a high at once use clamp.

+

Parameters

athe first value
bthe second value
+

Example

program CappedHealing {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Player { Health }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Health { current: 90, maximum: 100 } }
+  }
+
+  handler ApplyHeal phase Update {
+    for (Health) in query [Health, {Player}] {
+      Health.current = min(Health.current + 25, Health.maximum)
+    }
+  }
+
+  handler ReportHealth phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Health) in query [Health, {Player}] {
+      Screen.draw_number(x: 8, y: 8, value: Health.current, color: Color.LimeGreen, scale: 1)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-none.html b/fn-none.html new file mode 100644 index 00000000..c6c93b5d --- /dev/null +++ b/fn-none.html @@ -0,0 +1,36 @@ + + + + + +none — Ludic + + + + + + +
+ +
+ function +

none

+
+ none() -> option +

The empty option — a missing value with no sentinel. Return it when there is nothing to give: no target, an empty slot, a lookup that missed. The caller distinguishes it from some with is_none / is_some, or supplies a default with unwrap_or.

+ +

Example

program Slots {
+  function held(slot: int) -> option {
+    if slot < 0 { return none() }
+    return some(slot)
+  }
+  test "none is empty" {
+    expect_eq(is_none(held(0 - 1)), true)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-ok.html b/fn-ok.html new file mode 100644 index 00000000..bd647bca --- /dev/null +++ b/fn-ok.html @@ -0,0 +1,37 @@ + + + + + +ok — Ludic + + + + + + +
+ +
+ function +

ok

+
+ ok(v) -> result +

Wraps a success value in a result — the value type a fallible function returns. A result carries either a success payload (this) or a failure message (from err), and a caller recovers it with try/else or classifies it with is_ok / is_err. A function that can fail declares -> result and returns ok(value) on the happy path.

The payload is any i32-width scalar — int, fixed, bool, or entity.

+

Parameters

vthe success payload
+

Example

program Parse {
+  function to_digit(c: int) -> result {
+    if c >= 48 and c <= 57 { return ok(c - 48) }   # '0'..'9'
+    return err("not a digit")
+  }
+  test "ok carries the parsed value" {
+    let d = try to_digit(55) else { 0 }            # '7' -> 7
+    expect_eq(d, 7)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-panic.html b/fn-panic.html new file mode 100644 index 00000000..85bc3dac --- /dev/null +++ b/fn-panic.html @@ -0,0 +1,38 @@ + + + + + +panic — Ludic + + + + + + +
+ +
+ function +

panic

+
+ panic(msg: string) +

Prints file:line: panic: <msg> to standard error and aborts the process with exit code 1. It turns "something went wrong" into a clear, located message a non-expert can act on — the opposite of a raw segfault or a silent wrong result. Use it for the unrecoverable case: a corrupt asset, a broken invariant, a branch that should be impossible. The source location is baked in at compile time, so the message always points at the exact call. For a guarded check that only aborts when a condition fails, use assert.

Recoverable failures — a missing save, a bad network packet — are a different story: those should be values a game can fall back from rather than a panic. That value-carrying path is try/else over a result; panic covers the unrecoverable programmer-bug half.

+

Parameters

msga message describing what went wrong (a string)
+

Example

program Demo {
+  function pick(options: int, n: int) -> int {
+    if n < 0 { panic("choice index is negative") }
+    if n >= options { panic("choice index out of range") }
+    return n
+  }
+  entry {
+    print(pick(3, 1))     # 1
+    print(pick(3, 9))     # aborts: Demo.ludic:6: panic: choice index out of range
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-print.html b/fn-print.html new file mode 100644 index 00000000..6d5dadd3 --- /dev/null +++ b/fn-print.html @@ -0,0 +1,36 @@ + + + + + +print — Ludic + + + + + + +
+ +
+ function +

print

+
+ print(x) +

Writes its argument to standard output followed by a newline. It accepts either an integer or a string, so it is the go-to tool for quick debugging and for headless test programs that emit numbers a test harness can check. It is a diagnostic channel, separate from anything drawn on screen with the Screen API, and is most useful in Start- or Update-phase handlers while you are iterating. For interpolated messages, build the string first with a backtick template and pass that.

+

Parameters

xthe value to print, an int or a string
+

Example

program PrintScore {
+  var score: int = 0
+
+  handler CountUp phase Update {
+    score = score + 10
+    print(score)
+    if score >= 30 { quit() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-push.html b/fn-push.html new file mode 100644 index 00000000..76deb7a1 --- /dev/null +++ b/fn-push.html @@ -0,0 +1,37 @@ + + + + + +push — Ludic + + + + + + +
+ +
+ function +

push

+
+ push(slice, x) +

Appends a value to the end of a slice, growing it by one element. Create the slice first with new []T, then push items onto it; afterward len reflects the new count and the element is reachable by index. Use it to build up lists at runtime — a queue of spawn requests, collected scores, parsed tokens — where you don't know the size in advance. The value's type must match the slice's element type.

+

Parameters

slicethe growable slice to append to
xthe element to append
+

Example

program BuildWaveList {
+  handler PlanWaves phase Start {
+    let wave_sizes = new []int
+    push(wave_sizes, 4)
+    push(wave_sizes, 6)
+    for index in 0 .. len(wave_sizes) {
+      print(wave_sizes[index])
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-quit.html b/fn-quit.html new file mode 100644 index 00000000..97d0d5f5 --- /dev/null +++ b/fn-quit.html @@ -0,0 +1,38 @@ + + + + + +quit — Ludic + + + + + + +
+ +
+ function +

quit

+
+ quit() +

Signals the runtime to stop the game loop after the current frame completes, ending the program in an orderly way. Because it lets the in-progress frame finish, any drawing you have already queued for this frame is still presented. Use it for a "quit to desktop" action, to end a headless test once its work is done, or to stop on a win/lose condition. For an immediate, mid-frame stop with a status code instead, use exit. It takes no arguments.

+ +

Example

program QuitOnKey {
+  handler ReadInput phase Input {
+    if Input.key() == 'q' { quit() }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_text(x: 8, y: 8, text: "PRESS Q TO QUIT", color: Color.White, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-read_char.html b/fn-read_char.html new file mode 100644 index 00000000..7e8b5a14 --- /dev/null +++ b/fn-read_char.html @@ -0,0 +1,38 @@ + + + + + +read_char — Ludic + + + + + + +
+ +
+ function +

read_char

+
+ read_char() -> int +

Reads a single byte from standard input and returns its value, or -1 when the input has ended. It is the foundation for headless, pipe-driven runs: a test or tool can feed the program a stream of bytes and process them one at a time, looping until read_char returns -1. Compare the byte against character literals such as 'a' to interpret it. It takes no arguments and advances through the input on each call.

+ +

Example

program CountInputBytes {
+  handler DrainInput phase Start {
+    var byte_count: int = 0
+    var next_byte = read_char()
+    while next_byte != -1 {
+      byte_count = byte_count + 1
+      next_byte = read_char()
+    }
+    print(byte_count)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-run.html b/fn-run.html new file mode 100644 index 00000000..64017704 --- /dev/null +++ b/fn-run.html @@ -0,0 +1,33 @@ + + + + + +run — Ludic + + + + + + +
+ +
+ function +

run

+
+ run(cmd) +

Executes its string argument as a shell command on the host. It exists so build steps and developer tooling can be written in Ludic itself — invoking the compiler, moving files, or chaining tools — rather than in a separate shell script. It is a tooling and scripting facility, not something a shipped game loop would normally use. Treat the command string carefully, since it runs with the same privileges as the program.

+

Parameters

cmdthe shell command line to run
+

Example

program BuildStep {
+  handler RunTool phase Start {
+    run("mkdir -p build")
+    run("echo built")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-save.html b/fn-save.html new file mode 100644 index 00000000..8acb2a2a --- /dev/null +++ b/fn-save.html @@ -0,0 +1,46 @@ + + + + + +save — Ludic + + + + + + +
+ +
+ function +

save

+
+ save() +

Captures a complete snapshot of the running world in a single call: every spawned model instance and its properties, plus every program-level var. The compiler generates the serialization for you from your declarations, so you never write it by hand. Pair it with load to implement quick-save / quick-load, checkpoints, or deterministic test fixtures. It takes no arguments and writes to the runtime's snapshot slot; calling it again overwrites the previous snapshot.

+ +

Example

program QuickSave {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+  }
+
+  handler ReadInput phase Input {
+    let pressed = Input.key()
+    if pressed == 's' { save() }
+    if pressed == 'l' { load() }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-some.html b/fn-some.html new file mode 100644 index 00000000..6e06d8fc --- /dev/null +++ b/fn-some.html @@ -0,0 +1,37 @@ + + + + + +some — Ludic + + + + + + +
+ +
+ function +

some

+
+ some(v) -> option +

Wraps a present value in an option — the value type that models maybe a value without a magic -1 sentinel. An option is either some(value) (this) or none (empty); test it with is_some / is_none and read it with unwrap_or. The payload is any i32-width scalar — int, fixed, bool, or entity.

+

Parameters

vthe present value
+

Example

program Targeting {
+  function nearest(count: int) -> option {
+    if count == 0 { return none() }
+    return some(count - 1)          # the nearest target index
+  }
+  test "some carries the value" {
+    let t = unwrap_or(nearest(3), 0 - 1)
+    expect_eq(t, 2)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-string.html b/fn-string.html new file mode 100644 index 00000000..0f85df50 --- /dev/null +++ b/fn-string.html @@ -0,0 +1,35 @@ + + + + + +string — Ludic + + + + + + +
+ +
+ function +

string

+
+ string(x) -> string +

Converts a value to its textual form: an int, bool, or fixed becomes a string, and a value that is already a string is returned unchanged. This is what backtick {…} interpolation calls under the hood, so most of the time you can interpolate directly instead of calling string yourself. Reach for the explicit form when you need to store or pass around the text, or build a string in pieces. The result can be printed, drawn with Screen.draw_text, or concatenated.

+

Parameters

xthe value to convert to text
+

Example

program LabelValue {
+  var score: int = 250
+
+  handler ReportScore phase Start {
+    let label = string(score)
+    print(label)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-ui_build.html b/fn-ui_build.html new file mode 100644 index 00000000..0d6510c6 --- /dev/null +++ b/fn-ui_build.html @@ -0,0 +1,55 @@ + + + + + +ui_build — Ludic + + + + + + +
+ +
+ function +

ui_build

+
+ ui_build() +

Constructs the retained-UI tree you declared in a ui block, laying out its panels, labels, and buttons and loading any skins or images they reference. Call it once during a Start-phase handler, after loading any fonts the UI needs, and before you open a screen with ui_open or draw it with ui_render. Because the UI is declared as data and built in one step, the game code only has to build it, open it, and read clicks. It takes no arguments.

+ +

Example

program TitleMenu {
+  var title_font: int = 0
+
+  ui MainMenu {
+    panel id: Root w: 240 pad: 16 gap: 6 bg: 0x1a1a2c align: center {
+      label text: "CHRONORIFT" font: title_font size: 24 fg: 0xffe060 align: center
+      button id: NewGame text: "New Game" font: title_font size: 16 w: 200
+      button id: Quit    text: "Quit"     font: title_font size: 16 w: 200
+    }
+  }
+
+  handler Boot phase Start {
+    title_font = Font.load("/System/Library/Fonts/Supplemental/Arial.ttf")
+    ui_build()
+    Ui.open(UI_MainMenu)
+  }
+
+  handler Navigate phase Update {
+    Ui.tick(Input.key())
+    if Ui.clicked(UI_Quit) { quit() }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Ui.render()
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-unwrap_or.html b/fn-unwrap_or.html new file mode 100644 index 00000000..4372a87b --- /dev/null +++ b/fn-unwrap_or.html @@ -0,0 +1,32 @@ + + + + + +unwrap_or — Ludic + + + + + + +
+ +
+ function +

unwrap_or

+
+ unwrap_or(o: option, fallback) -> int +

Reads an option: returns its value when it is some, or fallback when it is none. The safe, one-line way to get a usable value out of an option without a separate branch.

+

Parameters

othe option to read
fallbackthe value to use when the option is empty
+

Example

program Config {
+  handler Step phase Update {
+    let volume = unwrap_or(none(), 100)     # default to 100
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-words.html b/fn-words.html new file mode 100644 index 00000000..b82b8d54 --- /dev/null +++ b/fn-words.html @@ -0,0 +1,40 @@ + + + + + +words — Ludic + + + + + + +
+ +
+ function +

words

+
+ words(n) -> words +

Allocates a raw buffer of n 32-bit words and returns a words value you can index with buffer[i] to read or write each word as an int. Use it when you want a flat, fixed-size numeric scratch array — a lookup table, a small ring buffer, or per-slot counters — without declaring a model. It is lower-level than a growable []int slice: the size is chosen up front and there is no push. For byte-granular storage use bytes instead.

+

Parameters

nthe number of 32-bit words to allocate
+

Example

program WordScratch {
+  handler SumScores phase Start {
+    let scores = words(3)
+    scores[0] = 10
+    scores[1] = 20
+    scores[2] = 30
+    var total: int = 0
+    for index in 0 .. 3 {
+      total = total + scores[index]
+    }
+    print(total)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_attach_dyn.html b/fn-world_attach_dyn.html new file mode 100644 index 00000000..fbaa81f4 --- /dev/null +++ b/fn-world_attach_dyn.html @@ -0,0 +1,39 @@ + + + + + +world_attach_dyn — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_attach_dyn
+
+ function +

world_attach_dyn

+
+ world_attach_dyn(target, property) +

world_attach_dyn adds a property to a live instance by numeric id, the reflection-ABI counterpart of the static attach Prop on instance. It is the tool a mod uses to grant a runtime-registered property (from world_register_prop) — or any property known only by id — to an instance, after which the instance's fields can be written with world_set. It returns nothing and sets the instance's has-flag for that property. Pair it with world_detach_dyn to remove. In ordinary compiled code, prefer the attach statement, which also fires @OnAttach hooks and seeds field overrides.

+

Parameters

targetthe model instance to modify, an entity handle
propertythe property id to attach, from world_prop_id or world_register_prop
+

Example

program GrantComponent {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 4, row: 4 } }
+    let stamina_property = world_register_prop("Stamina", 1)
+    for (position) in query [Position, {Player}] {
+      world_attach_dyn(self(), stamina_property)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_count.html b/fn-world_count.html new file mode 100644 index 00000000..718a6422 --- /dev/null +++ b/fn-world_count.html @@ -0,0 +1,43 @@ + + + + + +world_count — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_count
+
+ function +

world_count

+
+ world_count() -> int +

world_count returns the total number of live model instances in the world, as an int — a quick census without walking the world yourself. It takes no arguments and counts every spawned instance regardless of which properties it carries, so it is handy for a mod or debug overlay that wants a running tally of everything alive using the reflection ABI rather than a query loop. To resolve names to numeric ids for the other reflection calls, see world_prop_id; to read a specific instance's fields by id, see world_get.

+ +

Example

program Census {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  handler SpawnEnemies phase Start {
+    spawn Enemy { Position { column: 3, row: 3 } }
+    spawn Enemy { Position { column: 9, row: 3 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    let alive = world_count()
+    Screen.draw_number(x: 8, y: 8, value: alive, color: Color.Gold, scale: 2)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_detach_dyn.html b/fn-world_detach_dyn.html new file mode 100644 index 00000000..dfdc2f00 --- /dev/null +++ b/fn-world_detach_dyn.html @@ -0,0 +1,44 @@ + + + + + +world_detach_dyn — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_detach_dyn
+
+ function +

world_detach_dyn

+
+ world_detach_dyn(target, property) +

world_detach_dyn removes a property from a live instance by numeric id, the reflection-ABI counterpart of the static detach Prop on instance. It clears the instance's has-flag for that property so queries and world_has no longer see it, and returns nothing. Use it to take back a runtime-registered or id-only property a mod previously granted with world_attach_dyn. In ordinary compiled code, prefer the detach statement, which also fires the @OnDetach hook before the flag clears.

+

Parameters

targetthe model instance to modify, an entity handle
propertythe property id to detach, from world_prop_id or world_register_prop
+

Example

program RevokeComponent {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 4, row: 4 } }
+  }
+
+  handler DropStamina phase Update {
+    let stamina_property = world_prop_id("Stamina")
+    if stamina_property >= 0 {
+      for (position) in query [Position, {Player}] {
+        world_detach_dyn(self(), stamina_property)
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_field_id.html b/fn-world_field_id.html new file mode 100644 index 00000000..89b0e823 --- /dev/null +++ b/fn-world_field_id.html @@ -0,0 +1,41 @@ + + + + + +world_field_id — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_field_id
+
+ function +

world_field_id

+
+ world_field_id(property, name) -> int +

world_field_id resolves a field name within a given property to its numeric index, which is what world_get and world_set use to pick which field to read or write. You pass a property id (from world_prop_id) and the field's name, and get back its index (negative if the property has no such field). Together the trio world_prop_id → world_field_id → world_get/world_set lets a mod or tool touch any field on any instance entirely by name, without the property being known at compile time.

+

Parameters

propertythe property id, from world_prop_id
namethe field's name within that property, e.g. "column"
+

Example

program ResolveField {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 3, row: 3 } }
+  }
+
+  handler Inspect phase Update {
+    let position_property = world_prop_id("Position")
+    let column_field = world_field_id(position_property, "column")
+    Screen.status(string(column_field))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_get.html b/fn-world_get.html new file mode 100644 index 00000000..a347303d --- /dev/null +++ b/fn-world_get.html @@ -0,0 +1,44 @@ + + + + + +world_get — Ludic + + + + + + +
+ +
+ function +

world_get

+
+ world_get(target, property, field) -> int +

world_get reads a single field of a model instance entirely by numeric id, returning its value as an int. It is the id-based counterpart to the ordinary position.column field access that queries give you — used when the property is not known at compile time, as in a mod or a generic tool. You supply the target instance (an entity), the property id (from world_prop_id), and the field id (from world_field_id); the result is the stored value. Pair it with world_set to write. Reading a field of an instance that lacks the property is not meaningful, so guard with world_has first if unsure.

+

Parameters

targetthe model instance to read from, an entity handle
propertythe property id, from world_prop_id
fieldthe field id within that property, from world_field_id
+

Example

program ReadField {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 7, row: 2 } }
+  }
+
+  handler Inspect phase Update {
+    let position_property = world_prop_id("Position")
+    let column_field = world_field_id(position_property, "column")
+    for (position) in query [Position, {Player}] {
+      let value = world_get(self(), position_property, column_field)
+      Screen.status(string(value))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_has.html b/fn-world_has.html new file mode 100644 index 00000000..7164945d --- /dev/null +++ b/fn-world_has.html @@ -0,0 +1,43 @@ + + + + + +world_has — Ludic + + + + + + +
+ +
+ function +

world_has

+
+ world_has(target, property) -> bool +

world_has tests whether a model instance currently carries a given property, returning a boolean. It is the reflection-ABI equivalent of the membership test a query performs implicitly, and the safe guard to run before a world_get/world_set on a property an instance might not have. A property that has been disabled reads as absent here (its has-flag is clear), while one that is merely present with default values reads as present. Pass the target instance and the property id from world_prop_id.

+

Parameters

targetthe model instance to test, an entity handle
propertythe property id, from world_prop_id
+

Example

program CheckProperty {
+  property Position { column: int = 0, row: int = 0 }
+  property Shield   { amount: int = 0 }
+  model Player { Position, Shield }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 1, row: 1 }; Shield { amount: 2 } }
+  }
+
+  handler Inspect phase Update {
+    let shield_property = world_prop_id("Shield")
+    for (position) in query [Position, {Player}] {
+      if world_has(self(), shield_property) { Screen.status("shielded") }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_kind.html b/fn-world_kind.html new file mode 100644 index 00000000..8e2a8f6a --- /dev/null +++ b/fn-world_kind.html @@ -0,0 +1,44 @@ + + + + + +world_kind — Ludic + + + + + + +
+ +
+ function +

world_kind

+
+ world_kind(target) -> int +

world_kind returns the model id of a given instance — that is, which kind of thing it is — as an int. Compare the result against ids from world_model_id to branch generically on an instance's kind without knowing it at compile time, the reflection-ABI stand-in for the {Model} tag filter in a static query. It is the natural partner of world_query_next when you walk instances by id and need to tell players from enemies. Pass the instance handle you want to classify.

+

Parameters

targetthe model instance to classify, an entity handle
+

Example

program Classify {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+  model Enemy  { Position }
+
+  handler Boot phase Start {
+    spawn Hero  { Position { column: 2, row: 2 } }
+    spawn Grunt { Position { column: 8, row: 2 } }
+  }
+
+  handler Inspect phase Update {
+    let enemy_model = world_model_id("Enemy")
+    for (position) in query [Position] {
+      if world_kind(self()) == enemy_model { Screen.status("found an enemy") }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_load.html b/fn-world_load.html new file mode 100644 index 00000000..ab19f089 --- /dev/null +++ b/fn-world_load.html @@ -0,0 +1,43 @@ + + + + + +world_load — Ludic + + + + + + +
+ +
+ function +

world_load

+
+ world_load(buf, len) +

world_load restores the ECS world from a byte buffer previously produced by world_save, replacing the current world with the snapshot's instances and their fields. You pass the buffer and the number of valid bytes in it (the count world_save returned), and the world is rebuilt in place; it returns nothing. This is the low-level, buffer-based counterpart of world_save — the pair a mod or tool uses to move a whole world across the ABI or between storage. For a simple whole-game load from the default slot, the higher-level load() builtin is usually enough.

+

Parameters

bufthe source byte buffer holding a snapshot
lenthe number of valid bytes in buf
+

Example

program SnapshotRoundTrip {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  var snapshot_length: int = 0
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 3, row: 3 } }
+  }
+
+  handler RoundTrip phase Update {
+    let buffer = bytes(4096)
+    if Input.key() == 's' { snapshot_length = world_save(buffer) }
+    if Input.key() == 'l' { world_load(buffer, snapshot_length) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_model_id.html b/fn-world_model_id.html new file mode 100644 index 00000000..6a7de428 --- /dev/null +++ b/fn-world_model_id.html @@ -0,0 +1,40 @@ + + + + + +world_model_id — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_model_id
+
+ function +

world_model_id

+
+ world_model_id(name) -> int +

world_model_id looks up a model by name and returns its stable numeric id, or a negative value if no model of that name exists. It is part of Ludic's reflection ABI — the dynamic, id-based view of the world that mods and tools use alongside the static spawn/query forms. You typically resolve a name to an id once and then feed that id to id-based calls such as world_spawn or world_kind. Because the id is stable for a given build, it is safe to cache in a var.

+

Parameters

namethe model's name, as a string, e.g. "Player"
+

Example

program ResolveModel {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 3, row: 3 } }
+  }
+
+  handler Inspect phase Update {
+    let player_model = world_model_id("Player")
+    Screen.status(string(player_model))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_prop_id.html b/fn-world_prop_id.html new file mode 100644 index 00000000..ddbdd746 --- /dev/null +++ b/fn-world_prop_id.html @@ -0,0 +1,40 @@ + + + + + +world_prop_id — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_prop_id
+
+ function +

world_prop_id

+
+ world_prop_id(name) -> int +

world_prop_id resolves a property by name to its stable numeric id, returning a negative value when no such property exists. It is the entry point to the reflection ABI's field access: pass the returned property id to world_has, world_count, world_get/world_set (together with a field id from world_field_id), and the dynamic attach/detach and query calls. Resolve the id once and cache it in a var rather than looking it up every frame. This id-based view complements the static query [...], which is how ordinary handlers reach properties by name.

+

Parameters

namethe property's name, as a string, e.g. "Position"
+

Example

program ResolveProperty {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 3, row: 3 } }
+  }
+
+  handler Inspect phase Update {
+    let position_property = world_prop_id("Position")
+    Screen.status(string(position_property))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_query_next.html b/fn-world_query_next.html new file mode 100644 index 00000000..13a7c729 --- /dev/null +++ b/fn-world_query_next.html @@ -0,0 +1,46 @@ + + + + + +world_query_next — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_query_next
+
+ function +

world_query_next

+
+ world_query_next(property, cursor) -> entity +

world_query_next walks the world by ascending instance id, returning the next live instance at or after the cursor that carries the given property, or a negative value when there are no more. It is the reflection-ABI equivalent of a for (…) in query [Property] loop, expressed as an explicit step so a mod or tool can iterate matching instances by id. Drive it with a loop: start the cursor at 0, and after each hit advance the cursor to one past the returned id before calling again. Pass the property id from world_prop_id.

+

Parameters

propertythe property id to match, from world_prop_id
cursorthe id to start scanning from; advance it past each hit to continue
+

Example

program WalkInstances {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  handler Boot phase Start {
+    spawn GruntA { Position { column: 2, row: 2 } }
+    spawn GruntB { Position { column: 5, row: 2 } }
+  }
+
+  handler Sweep phase Update {
+    let position_property = world_prop_id("Position")
+    var cursor = 0
+    var found = world_query_next(position_property, cursor)
+    while found >= 0 {
+      cursor = found + 1
+      found = world_query_next(position_property, cursor)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_register_prop.html b/fn-world_register_prop.html new file mode 100644 index 00000000..2075f131 --- /dev/null +++ b/fn-world_register_prop.html @@ -0,0 +1,37 @@ + + + + + +world_register_prop — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_register_prop
+
+ function +

world_register_prop

+
+ world_register_prop(name, fields) -> int +

world_register_prop declares a brand-new property at runtime — one not present in the compiled program — and returns its property id. This is the dynamic-ECS entry point for mods: a mod can add its own component to the world, then world_attach_dyn it onto instances and read and write its fields with world_get/world_set. You give the property a name and its number of fields; the returned id then works everywhere a compiled property id does. Compiled programs that need no runtime-defined properties never call this — they declare property blocks at compile time instead.

+

Parameters

namethe new property's name, as a string
fieldshow many integer fields it has
+

Example

program ModComponent {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 4, row: 4 } }
+    let stamina_property = world_register_prop("Stamina", 1)
+    Screen.status(string(stamina_property))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_save.html b/fn-world_save.html new file mode 100644 index 00000000..3449a83f --- /dev/null +++ b/fn-world_save.html @@ -0,0 +1,43 @@ + + + + + +world_save — Ludic + + + + + + +
+ +
+ function +

world_save

+
+ world_save(buf) -> int +

world_save serializes the entire ECS world — every live instance, its kind, and all its property fields — into a byte buffer you provide, and returns the number of bytes written. It is the low-level, buffer-based form of a save: allocate a buffer with bytes(n) large enough to hold the snapshot, pass it in, and persist the written bytes (or hand them across the mod ABI). The exact count comes back so you know how much of the buffer is meaningful. Restore a snapshot later with world_load. For a simple whole-game save to the default slot, the higher-level save() builtin is usually enough.

+

Parameters

bufthe destination byte buffer, e.g. from bytes(n)
+

Example

program SnapshotOut {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 3, row: 3 } }
+  }
+
+  handler Persist phase Update {
+    if Input.key() == 's' {
+      let buffer = bytes(4096)
+      let written = world_save(buffer)
+      Screen.status(string(written))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_set.html b/fn-world_set.html new file mode 100644 index 00000000..341d8adb --- /dev/null +++ b/fn-world_set.html @@ -0,0 +1,43 @@ + + + + + +world_set — Ludic + + + + + + +
+ +
+ function +

world_set

+
+ world_set(target, property, field, value) +

world_set writes a single field of a model instance by numeric id — the id-based counterpart to an ordinary position.column = … assignment, used when the property is not known at compile time. You give the target instance, the property id (from world_prop_id), the field id (from world_field_id), and the new value; the value is stored just as a direct assignment would. It returns nothing. Together with world_get, world_has, and the resolver functions, it lets a mod or tool mutate any field on any instance generically, which is how the reflection ABI mirrors the compiled query/field access at runtime.

+

Parameters

targetthe model instance to write to, an entity handle
propertythe property id, from world_prop_id
fieldthe field id within that property, from world_field_id
valuethe new integer value to store
+

Example

program WriteField {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 0, row: 0 } }
+  }
+
+  handler Nudge phase Update {
+    let position_property = world_prop_id("Position")
+    let column_field = world_field_id(position_property, "column")
+    for (position) in query [Position, {Player}] {
+      world_set(self(), position_property, column_field, position.column + 1)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_size.html b/fn-world_size.html new file mode 100644 index 00000000..4d8fecb5 --- /dev/null +++ b/fn-world_size.html @@ -0,0 +1,44 @@ + + + + + +world_size — Ludic + + + + + + +
+ +
+ function +

world_size

+
+ world_size() -> int +

world_size returns the total number of live model instances in the world, across every kind, as an int. It takes no arguments and is the cheapest way to answer "how big is the world right now?" — useful for a debug readout, a capacity check before a big spawn, or a mod inspecting the running game through the reflection ABI. For a count restricted to instances carrying a particular property, use world_count instead.

+ +

Example

program WorldSize {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  handler Boot phase Start {
+    spawn GruntA { Position { column: 4, row: 4 } }
+    spawn GruntB { Position { column: 6, row: 4 } }
+    spawn GruntC { Position { column: 8, row: 4 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    let total = world_size()
+    Screen.draw_number(x: 8, y: 8, value: total, color: Color.White, scale: 2)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fn-world_spawn.html b/fn-world_spawn.html new file mode 100644 index 00000000..646e69ed --- /dev/null +++ b/fn-world_spawn.html @@ -0,0 +1,39 @@ + + + + + +world_spawn — Ludic + + + + + + +
+
API Reference › Entities & the ECS › world_spawn
+
+ function +

world_spawn

+
+ world_spawn(model) -> entity +

world_spawn creates a new instance of a model chosen by numeric id and returns its entity handle — the reflection-ABI counterpart of the static spawn Label { … }. Because the model is given as an id (from world_model_id) rather than a name written in source, a mod or tool can decide at runtime which kind to create. The new instance is seeded exactly as the static spawn would seed it — every property in the model attached with default field values — after which you can set fields with world_set. Use the ordinary spawn statement in normal handler code; reach for world_spawn when the kind is data, not a literal.

+

Parameters

modelthe model id to instantiate, from world_model_id
+

Example

program Reinforcements {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  handler Reinforce phase Update {
+    if Input.key() == ' ' {
+      let enemy_model = world_model_id("Enemy")
+      let new_enemy = world_spawn(enemy_model)
+      Screen.status(string(new_enemy))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/font-load.html b/font-load.html new file mode 100644 index 00000000..9217f018 --- /dev/null +++ b/font-load.html @@ -0,0 +1,33 @@ + + + + + +Font.load — Ludic + + + + + + +
+
API Reference › Font › Font.load
+
+ method +

Font.load

+
+ Font.load(path: p) -> int +

Loads a TrueType font file and returns its handle. Load it before Ui.build so the menus can measure their text; the handle is what a ui block's font: reads.

+ +

Example

# doc-check: skip — illustrative
+var menu_font: int = 0
+handler Boot phase Start {
+  menu_font = Font.load(path: "assets/fonts/PixelOperator.ttf")
+  Ui.build()
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-append_text.html b/fs-append_text.html new file mode 100644 index 00000000..c93d4d18 --- /dev/null +++ b/fs-append_text.html @@ -0,0 +1,32 @@ + + + + + +Fs.append_text — Ludic + + + + + + +
+
API Reference › Fs › Fs.append_text
+
+ method +

Fs.append_text

+
+ Fs.append_text(p, s) -> bool +

Appends a string to the end of a file, creating it if necessary. Returns true on success. Handy for logs and event journals.

+

Parameters

pthe path
sthe string to append
+

Example

program AppendText {
+  entry {
+    if Fs.append_text("run.log", "started\n") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-copy.html b/fs-copy.html new file mode 100644 index 00000000..87e7c34b --- /dev/null +++ b/fs-copy.html @@ -0,0 +1,32 @@ + + + + + +Fs.copy — Ludic + + + + + + +
+
API Reference › Fs › Fs.copy
+
+ method +

Fs.copy

+
+ Fs.copy(src, dst) -> bool +

Copies a file's bytes to a new path (atomically at the destination), returning true on success. Binary-safe, so it works for assets as well as text. Copying directories is a follow-up.

+

Parameters

srcthe source path
dstthe destination path
+

Example

program Copy {
+  entry {
+    if Fs.copy("saves/slot1.json", "saves/slot1.bak") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-exists.html b/fs-exists.html new file mode 100644 index 00000000..8adcc235 --- /dev/null +++ b/fs-exists.html @@ -0,0 +1,32 @@ + + + + + +Fs.exists — Ludic + + + + + + +
+
API Reference › Fs › Fs.exists
+
+ method +

Fs.exists

+
+ Fs.exists(p) -> bool +

Returns whether anything (file or directory) exists at the path. A cheap existence check before reading or writing.

+

Parameters

pthe path to test
+

Example

program Exists {
+  entry {
+    if Fs.exists("settings.ini") { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-is_dir.html b/fs-is_dir.html new file mode 100644 index 00000000..32198537 --- /dev/null +++ b/fs-is_dir.html @@ -0,0 +1,32 @@ + + + + + +Fs.is_dir — Ludic + + + + + + +
+
API Reference › Fs › Fs.is_dir
+
+ method +

Fs.is_dir

+
+ Fs.is_dir(p) -> bool +

Returns whether the path exists and is a directory (as opposed to a file). Useful when walking a tree or deciding whether to recurse.

+

Parameters

pthe path to test
+

Example

program IsDir {
+  entry {
+    if Fs.is_dir("mods") { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-list.html b/fs-list.html new file mode 100644 index 00000000..e7554968 --- /dev/null +++ b/fs-list.html @@ -0,0 +1,34 @@ + + + + + +Fs.list — Ludic + + + + + + +
+
API Reference › Fs › Fs.list
+
+ method +

Fs.list

+
+ Fs.list(dir) -> []string +

Returns the names of the entries in a directory (excluding . and ..), sorted ascending for a stable, reproducible order — important for deterministic mod loading. Names are relative; combine with Path.join for full paths.

+

Parameters

dirthe directory to list
+

Example

program List {
+  entry {
+    let names = Fs.list("mods")
+    var i = 0
+    while i < len(names) { print(len(names[i])); i = i + 1 }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-mkdir.html b/fs-mkdir.html new file mode 100644 index 00000000..f1a45fa1 --- /dev/null +++ b/fs-mkdir.html @@ -0,0 +1,32 @@ + + + + + +Fs.mkdir — Ludic + + + + + + +
+
API Reference › Fs › Fs.mkdir
+
+ method +

Fs.mkdir

+
+ Fs.mkdir(p) -> bool +

Creates a directory, including any missing parent directories (like mkdir -p). Returns true if the directory exists afterwards, so calling it on an existing directory is fine.

+

Parameters

pthe directory path to create
+

Example

program Mkdir {
+  entry {
+    if Fs.mkdir("saves/campaign") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-read_text.html b/fs-read_text.html new file mode 100644 index 00000000..b0eee849 --- /dev/null +++ b/fs-read_text.html @@ -0,0 +1,35 @@ + + + + + +Fs.read_text — Ludic + + + + + + +
+
API Reference › Fs › Fs.read_text
+
+ method +

Fs.read_text

+
+ Fs.read_text(p) -> string +

Reads an entire file into a string (UTF-8 text), or returns null when it cannot be opened — check for null rather than assuming success. For a missing or unreadable save, fall back to a fresh default.

+

Parameters

pthe path to read
+

Example

program ReadText {
+  entry {
+    if Fs.exists("saves/slot1.json") {
+      let text = Fs.read_text("saves/slot1.json")
+      print(len(text))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-remove.html b/fs-remove.html new file mode 100644 index 00000000..b4bfbc3d --- /dev/null +++ b/fs-remove.html @@ -0,0 +1,32 @@ + + + + + +Fs.remove — Ludic + + + + + + +
+
API Reference › Fs › Fs.remove
+
+ method +

Fs.remove

+
+ Fs.remove(p) -> bool +

Deletes a file, returning true on success. Use it to clear a save slot or a temporary export.

+

Parameters

pthe path to delete
+

Example

program Remove {
+  entry {
+    if Fs.remove("saves/slot1.json") { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-size.html b/fs-size.html new file mode 100644 index 00000000..0eb2a8c2 --- /dev/null +++ b/fs-size.html @@ -0,0 +1,33 @@ + + + + + +Fs.size — Ludic + + + + + + +
+
API Reference › Fs › Fs.size
+
+ method +

Fs.size

+
+ Fs.size(p) -> int +

Returns the size of a file in bytes, or -1 if it cannot be opened. Useful for progress bars or sanity checks before loading.

+

Parameters

pthe path
+

Example

program Size {
+  entry {
+    let n = Fs.size("assets/level1.dat")
+    if n >= 0 { print(n) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fs-write_text.html b/fs-write_text.html new file mode 100644 index 00000000..3acbf8f5 --- /dev/null +++ b/fs-write_text.html @@ -0,0 +1,32 @@ + + + + + +Fs.write_text — Ludic + + + + + + +
+
API Reference › Fs › Fs.write_text
+
+ method +

Fs.write_text

+
+ Fs.write_text(p, s) -> bool +

Writes a string to a file, replacing any existing contents. The write is atomic — the bytes go to a temporary file that is renamed into place — so an interrupted write never corrupts the previous file. Returns true on success.

+

Parameters

pthe path to write
sthe string contents
+

Example

program WriteText {
+  entry {
+    if Fs.write_text("settings.ini", "fullscreen=1") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fx-clear.html b/fx-clear.html new file mode 100644 index 00000000..a929233e --- /dev/null +++ b/fx-clear.html @@ -0,0 +1,32 @@ + + + + + +Fx.clear — Ludic + + + + + + +
+
API Reference › Fx › Fx.clear
+
+ method +

Fx.clear

+
+ Fx.clear() +

Removes every effect in flight — call it when the world changes under them (a room change, a new run).

+ +

Example

# doc-check: skip — illustrative
+function clear_room() -> void {
+  for (p) in query [Position] { despawn self() }
+  Fx.clear()
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fx-number.html b/fx-number.html new file mode 100644 index 00000000..032ee00d --- /dev/null +++ b/fx-number.html @@ -0,0 +1,29 @@ + + + + + +Fx.number — Ludic + + + + + + +
+
API Reference › Fx › Fx.number
+
+ method +

Fx.number

+
+ Fx.number(x: px, y: px, value: n, color: c) +

Shows value above (x, y) for 30 frames, rising one pixel every other frame — the damage number of every action game.

+ +

Example

# doc-check: skip — illustrative
+@On(Damaged) handler ShowHit { Fx.number(x: center(target).x, y: center(target).y, value: amount, color: Color.Lemon) }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/fx-sparks.html b/fx-sparks.html new file mode 100644 index 00000000..c6dabf7c --- /dev/null +++ b/fx-sparks.html @@ -0,0 +1,29 @@ + + + + + +Fx.sparks — Ludic + + + + + + +
+
API Reference › Fx › Fx.sparks
+
+ method +

Fx.sparks

+
+ Fx.sparks(x: px, y: px, color: c, count: n) +

Spawns count sparks at (x, y), each with a random velocity of up to 3px per frame on each axis and a lifetime of 18 to 26 frames; they are drawn as 1 to 3 pixel squares in color.

+ +

Example

# doc-check: skip — illustrative
+@On(Died) handler Burst { Fx.sparks(x: center(e).x, y: center(e).y, color: Color.Crimson, count: 10) }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/grid-a_star.html b/grid-a_star.html new file mode 100644 index 00000000..7b7523a6 --- /dev/null +++ b/grid-a_star.html @@ -0,0 +1,33 @@ + + + + + +Grid.a_star — Ludic + + + + + + +
+
API Reference › Grid › Grid.a_star
+
+ method +

Grid.a_star

+
+ Grid.a_star(x0, y0, x1, y1, wall) -> []Cell +

Returns the shortest path from (x0, y0) to (x1, y1) over passable (non-wall) cells, 4-connected with uniform step cost, as a Cell slice from start to goal inclusive — an A* search with a Manhattan heuristic. Empty if the goal is unreachable (or start/goal is a wall). (Named under Grid rather than Path, which is the filesystem-paths library.)

+

Parameters

wallthe impassable tile char, e.g. '#'
+

Example

program Demo {
+  handler H phase Update {
+    let path = Grid.a_star(1, 1, 20, 12, '#')
+    if len(path) > 0 { print(len(path)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/grid-blocked.html b/grid-blocked.html new file mode 100644 index 00000000..4d08b64f --- /dev/null +++ b/grid-blocked.html @@ -0,0 +1,32 @@ + + + + + +Grid.blocked — Ludic + + + + + + +
+
API Reference › Grid › Grid.blocked
+
+ method +

Grid.blocked

+
+ Grid.blocked(x, y, wall) -> bool +

Whether (x, y) blocks movement — true if it is out of bounds or its tile equals wall. The passability test the other Grid functions share.

+

Parameters

wallthe impassable tile char, e.g. '#'
+

Example

program Demo {
+  handler H phase Update {
+    if Grid.blocked(0, 0, '#') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/grid-flood.html b/grid-flood.html new file mode 100644 index 00000000..e90ea527 --- /dev/null +++ b/grid-flood.html @@ -0,0 +1,33 @@ + + + + + +Grid.flood — Ludic + + + + + + +
+
API Reference › Grid › Grid.flood
+
+ method +

Grid.flood

+
+ Grid.flood(x, y, wall) -> []Cell +

Returns every passable cell reachable from (x, y) by 4-connected steps (up/down/left/right over non-wall cells), in breadth-first order — for connectivity checks, filling a room, or measuring an enclosed area. Empty if the start itself is blocked.

+

Parameters

wallthe impassable tile char
+

Example

program Demo {
+  handler H phase Update {
+    let region = Grid.flood(2, 2, '#')
+    print(len(region))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/grid-line.html b/grid-line.html new file mode 100644 index 00000000..90c24f17 --- /dev/null +++ b/grid-line.html @@ -0,0 +1,33 @@ + + + + + +Grid.line — Ludic + + + + + + +
+
API Reference › Grid › Grid.line
+
+ method +

Grid.line

+
+ Grid.line(x0, y0, x1, y1) -> []Cell +

Returns the cells a straight line from (x0, y0) to (x1, y1) passes through, endpoints included, using integer Bresenham — the basis of line-of-sight, ray-casting, and drawing on the grid.

+ +

Example

program Demo {
+  handler H phase Update {
+    let cells = Grid.line(0, 0, 5, 3)
+    print(len(cells))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/grid-line_of_sight.html b/grid-line_of_sight.html new file mode 100644 index 00000000..305a2a54 --- /dev/null +++ b/grid-line_of_sight.html @@ -0,0 +1,32 @@ + + + + + +Grid.line_of_sight — Ludic + + + + + + +
+
API Reference › Grid › Grid.line_of_sight
+
+ method +

Grid.line_of_sight

+
+ Grid.line_of_sight(x0, y0, x1, y1, wall) -> bool +

Whether an unobstructed straight line connects (x0, y0) and (x1, y1) — true when no cell it crosses (endpoints included) is a wall. Use it for visibility, aggro checks, and cover.

+

Parameters

wallthe impassable tile char
+

Example

program Demo {
+  handler H phase Update {
+    if Grid.line_of_sight(1, 1, 8, 1, '#') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-combine.html b/hash-combine.html new file mode 100644 index 00000000..25f294b4 --- /dev/null +++ b/hash-combine.html @@ -0,0 +1,37 @@ + + + + + +Hash.combine — Ludic + + + + + + +
+
API Reference › Hash › Hash.combine
+
+ method +

Hash.combine

+
+ Hash.combine(...) -> int +

Mixes any number of integers into a single 32-bit value. It folds the arguments left to right with the classic hash-combine step (each value is spread with the golden-ratio constant 0x9e3779b9 and stirred into the running seed), so the result depends on every input and on their order — Hash.combine(1, 2, 3) and Hash.combine(3, 2, 1) differ. The whole point is a per-thing deterministic seed: hash a world seed with a chunk's coordinates to drive procedural generation, so the same chunk always regenerates identically. Pair it with the noise and random libraries, which take a seed.

+ +

Example

program ChunkSeed {
+  entry {
+    let world_seed = 1337
+    let cx = 4
+    let cy = -2
+    let seed = Hash.combine(world_seed, cx, cy)  # this chunk's stable seed
+    print(seed)
+    print(Hash.combine(world_seed, cx, cy) == seed)  # 1 — reproducible
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-crc32.html b/hash-crc32.html new file mode 100644 index 00000000..869d5396 --- /dev/null +++ b/hash-crc32.html @@ -0,0 +1,35 @@ + + + + + +Hash.crc32 — Ludic + + + + + + +
+
API Reference › Hash › Hash.crc32
+
+ method +

Hash.crc32

+
+ Hash.crc32(s) -> int +

The CRC-32 checksum of a string's bytes — the standard IEEE 802.3 variant (reflected polynomial 0xEDB88320), the same one used by zip, gzip and PNG. Its well-known check value: Hash.crc32("123456789") is 0xCBF43926. Use it to catch accidental corruption in a save file, downloaded asset, or config blob: store the checksum alongside the data and compare on load. It detects damage, not tampering — an attacker can trivially forge a matching CRC, so it is not a security check (see the section note).

+

Parameters

sthe string (bytes) to checksum
+

Example

program Checksum {
+  entry {
+    let data = "save:level=3;coins=120"
+    let ck = Hash.crc32(data)     # store this next to the save; recompute on load
+    print(ck)
+    print(Hash.crc32(data) == ck)  # 1 — unchanged data checks out
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-fnv1a.html b/hash-fnv1a.html new file mode 100644 index 00000000..e427cd7c --- /dev/null +++ b/hash-fnv1a.html @@ -0,0 +1,33 @@ + + + + + +Hash.fnv1a — Ludic + + + + + + +
+
API Reference › Hash › Hash.fnv1a
+
+ method +

Hash.fnv1a

+
+ Hash.fnv1a(s) -> int +

The FNV-1a hash of a string's bytes, as an explicit 32-bit algorithm. It starts from the standard offset basis 0x811c9dc5 and, for each byte, XORs the byte in and multiplies by the FNV prime 0x01000193. Small, allocation-free, and well-distributed for short strings — ideal for map keys and content ids. This is the same function Hash.of currently delegates to; call Hash.fnv1a when you want to pin the algorithm by name so the value stays fixed even if the default changes.

+

Parameters

sthe string to hash
+

Example

program Fnv {
+  entry {
+    print(Hash.fnv1a(""))            # the empty string -> the offset basis
+    print(Hash.fnv1a("grass_tile"))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-fnv1a_64.html b/hash-fnv1a_64.html new file mode 100644 index 00000000..07680efd --- /dev/null +++ b/hash-fnv1a_64.html @@ -0,0 +1,33 @@ + + + + + +Hash.fnv1a_64 — Ludic + + + + + + +
+
API Reference › Hash › Hash.fnv1a_64
+
+ method +

Hash.fnv1a_64

+
+ Hash.fnv1a_64(s) -> long +

The 64-bit FNV-1a hash of a string's bytes, returned as a long. It uses the standard 64-bit offset basis 0xcbf29ce484222325 and prime 0x100000001b3: for each byte, XOR it in and multiply by the prime. Its wider output makes accidental collisions vanishingly unlikely for the id and registry use cases games hit. This is the function Hash.of64 delegates to; call Hash.fnv1a_64 when you want the algorithm pinned by name.

+

Parameters

sthe string to hash
+

Example

program Fnv64 {
+  entry {
+    print(Hash.fnv1a_64(""))            # the empty string -> the 64-bit offset basis
+    print(Hash.fnv1a_64("grass_tile"))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-mix.html b/hash-mix.html new file mode 100644 index 00000000..5265c004 --- /dev/null +++ b/hash-mix.html @@ -0,0 +1,36 @@ + + + + + +Hash.mix — Ludic + + + + + + +
+
API Reference › Hash › Hash.mix
+
+ method +

Hash.mix

+
+ Hash.mix(x) -> int +

Scrambles a single integer so its bits are thoroughly mixed — the MurmurHash3 fmix32 finalizer (shift/multiply/shift). Nearby inputs like 0, 1, 2 map to values that look unrelated, which is exactly what you want when turning a counter, entity id, or frame number into a random-looking seed. It is deterministic and self-inverse-free: the same input always gives the same output. Hash.mix(0) is 0. For folding several numbers together use Hash.combine.

+

Parameters

xthe integer to avalanche
+

Example

program Seeds {
+  entry {
+    var i = 0
+    while i < 4 {
+      print(Hash.mix(i))   # consecutive counters -> unrelated-looking seeds
+      i = i + 1
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-mix64.html b/hash-mix64.html new file mode 100644 index 00000000..eae8bce1 --- /dev/null +++ b/hash-mix64.html @@ -0,0 +1,34 @@ + + + + + +Hash.mix64 — Ludic + + + + + + +
+
API Reference › Hash › Hash.mix64
+
+ method +

Hash.mix64

+
+ Hash.mix64(x) -> long +

The 64-bit counterpart of Hash.mix: the MurmurHash3 fmix64 finalizer (shift by 33, multiply, repeat). It thoroughly scrambles a long, so consecutive or structured inputs map to unrelated-looking outputs — ideal for turning a large counter, packed coordinate, or 64-bit id into a random-looking seed. An int argument widens to 64 bits first. Deterministic; Hash.mix64(0) is 0.

+

Parameters

xthe integer to avalanche (widened to long)
+

Example

program Seeds64 {
+  entry {
+    let base: long = Hash.of64("chunk")
+    print(Hash.mix64(base))
+    print(Hash.mix64(base + 1))   # a neighbouring input -> an unrelated seed
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-of.html b/hash-of.html new file mode 100644 index 00000000..8a5530ce --- /dev/null +++ b/hash-of.html @@ -0,0 +1,35 @@ + + + + + +Hash.of — Ludic + + + + + + +
+
API Reference › Hash › Hash.of
+
+ method +

Hash.of

+
+ Hash.of(s) -> int +

The go-to hash for a string: turns text like "grass_tile" into a fast, stable 32-bit integer handle. Hash.of is the default when you don't care which algorithm is used — currently it is Hash.fnv1a, FNV-1a over the bytes. Reach for it to key a lookup table by name, give content a compact id, or detect that a config string changed. The result is deterministic: the same string always produces the same value, on every platform and run. It is not for security — see the section note.

+

Parameters

sthe string to hash
+

Example

program TileIds {
+  entry {
+    let grass = Hash.of("grass_tile")   # a stable integer id for this tile name
+    let water = Hash.of("water_tile")
+    print(grass)
+    print(water)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/hash-of64.html b/hash-of64.html new file mode 100644 index 00000000..a36fd557 --- /dev/null +++ b/hash-of64.html @@ -0,0 +1,35 @@ + + + + + +Hash.of64 — Ludic + + + + + + +
+
API Reference › Hash › Hash.of64
+
+ method +

Hash.of64

+
+ Hash.of64(s) -> long +

The 64-bit counterpart of Hash.of: hashes a string to a long instead of a 32-bit int. Use it when you want far fewer collisions than 32 bits can give — large content-id spaces, asset registries, or network identifiers where two different strings sharing a hash would be a real risk. Currently it is Hash.fnv1a_64, FNV-1a over the bytes. Deterministic like every hash here: the same string always gives the same 64-bit value.

+

Parameters

sthe string to hash
+

Example

program Ids64 {
+  entry {
+    let grass: long = Hash.of64("grass_tile")
+    let water: long = Hash.of64("water_tile")
+    print(grass)
+    print(water)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-body.html b/http-body.html new file mode 100644 index 00000000..20fcfc72 --- /dev/null +++ b/http-body.html @@ -0,0 +1,34 @@ + + + + + +Http.body — Ludic + + + + + + +
+
API Reference › Http › Http.body
+
+ method +

Http.body

+
+ Http.body(handle, body) -> void +

Sets a NUL-terminated string request body on an opened request.

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("POST", "https://example.com/api")
+    Http.body(h, "{\"hi\":1}")
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-body_bytes.html b/http-body_bytes.html new file mode 100644 index 00000000..e67d45ad --- /dev/null +++ b/http-body_bytes.html @@ -0,0 +1,35 @@ + + + + + +Http.body_bytes — Ludic + + + + + + +
+
API Reference › Http › Http.body_bytes
+
+ method +

Http.body_bytes

+
+ Http.body_bytes(handle, bytes, len) -> void +

Sets a raw request body of len bytes (for binary uploads).

+ +

Example

program Demo {
+  entry {
+    let buf = bytes(4)
+    let h = Http.open("POST", "https://example.com/upload")
+    Http.body_bytes(h, buf, 4)
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-body_len.html b/http-body_len.html new file mode 100644 index 00000000..bb6ed9d9 --- /dev/null +++ b/http-body_len.html @@ -0,0 +1,33 @@ + + + + + +Http.body_len — Ludic + + + + + + +
+
API Reference › Http › Http.body_len
+
+ method +

Http.body_len

+
+ Http.body_len(handle) -> int +

Returns the length of the response body in bytes.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { print(Http.body_len(h)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-expected.html b/http-expected.html new file mode 100644 index 00000000..9350d6c7 --- /dev/null +++ b/http-expected.html @@ -0,0 +1,35 @@ + + + + + +Http.expected — Ludic + + + + + + +
+
API Reference › Http › Http.expected
+
+ method +

Http.expected

+
+ Http.expected(handle) -> int +

Returns the length of the response body as the server announced it (Content-Length), or -1 while that is not known. For a request given Http.save_to it is known as soon as the headers arrive, and stays -1 for a reply sent without a length (chunked) — draw an indeterminate bar then. For an ordinary in-memory request it is -1 until the reply lands, then the body length.

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("GET", "https://example.com/big.bin")
+    Http.save_to(h, "big.bin.part")
+    Http.send(h)
+    print(Http.expected(h))   # -1: nothing has arrived yet
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-free.html b/http-free.html new file mode 100644 index 00000000..8ade2198 --- /dev/null +++ b/http-free.html @@ -0,0 +1,33 @@ + + + + + +Http.free — Ludic + + + + + + +
+
API Reference › Http › Http.free
+
+ method +

Http.free

+
+ Http.free(handle) -> void +

Releases a request handle and the resources it owns (the body copy and any retained response). Free each request once you are done reading it.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { Http.free(h) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-get.html b/http-get.html new file mode 100644 index 00000000..7fcc8ab8 --- /dev/null +++ b/http-get.html @@ -0,0 +1,32 @@ + + + + + +Http.get — Ludic + + + + + + +
+
API Reference › Http › Http.get
+
+ method +

Http.get

+
+ Http.get(url) -> int +

Starts an asynchronous GET and returns a request handle. Poll it each frame with Http.poll; the frame never blocks.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com/scores")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-header.html b/http-header.html new file mode 100644 index 00000000..54d2be58 --- /dev/null +++ b/http-header.html @@ -0,0 +1,33 @@ + + + + + +Http.header — Ludic + + + + + + +
+
API Reference › Http › Http.header
+
+ method +

Http.header

+
+ Http.header(handle, name) -> str +

Returns a response header value by name (case-insensitive), or an empty result if absent.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { print(Http.header(h, "Content-Type")) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-ok.html b/http-ok.html new file mode 100644 index 00000000..b24fafa8 --- /dev/null +++ b/http-ok.html @@ -0,0 +1,33 @@ + + + + + +Http.ok — Ludic + + + + + + +
+
API Reference › Http › Http.ok
+
+ method +

Http.ok

+
+ Http.ok(handle) -> bool +

Returns whether the response status is in the 2xx success range.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { if Http.ok(h) { print(1) } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-open.html b/http-open.html new file mode 100644 index 00000000..c363900b --- /dev/null +++ b/http-open.html @@ -0,0 +1,35 @@ + + + + + +Http.open — Ludic + + + + + + +
+
API Reference › Http › Http.open
+
+ method +

Http.open

+
+ Http.open(method, url) -> int +

Opens a request without sending it, so headers and a body can be added first with Http.set / Http.body, then dispatched with Http.send.

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("POST", "https://example.com/api")
+    Http.set(h, "Authorization", "Bearer token")
+    Http.body(h, "{}")
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-parse.html b/http-parse.html new file mode 100644 index 00000000..b8448609 --- /dev/null +++ b/http-parse.html @@ -0,0 +1,34 @@ + + + + + +Http.parse — Ludic + + + + + + +
+
API Reference › Http › Http.parse
+
+ method +

Http.parse

+
+ Http.parse(bytes, len) -> int +

Parses a raw HTTP/1.1 response (status line + headers + body) into a ready handle — useful for caches, custom transports, and tests. Transport-independent and pure.

+ +

Example

program Demo {
+  entry {
+    let raw = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhi"
+    let h = Http.parse(raw, Text.length(raw))
+    print(Http.status(h))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-poll.html b/http-poll.html new file mode 100644 index 00000000..5653f87a --- /dev/null +++ b/http-poll.html @@ -0,0 +1,34 @@ + + + + + +Http.poll — Ludic + + + + + + +
+
API Reference › Http › Http.poll
+
+ method +

Http.poll

+
+ Http.poll(handle) -> int +

Polls a request without blocking: returns -1 while pending, 0 on a transport error, else the HTTP status code once the reply lands.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    let st = Http.poll(h)
+    if st > 0 { print(Http.status(h)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-post.html b/http-post.html new file mode 100644 index 00000000..748fa560 --- /dev/null +++ b/http-post.html @@ -0,0 +1,32 @@ + + + + + +Http.post — Ludic + + + + + + +
+
API Reference › Http › Http.post
+
+ method +

Http.post

+
+ Http.post(url, body) -> int +

Starts an asynchronous POST with a string body, returning a request handle.

+ +

Example

program Demo {
+  entry {
+    let h = Http.post("https://example.com/score", "{\"score\":42}")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-received.html b/http-received.html new file mode 100644 index 00000000..7ad730fc --- /dev/null +++ b/http-received.html @@ -0,0 +1,35 @@ + + + + + +Http.received — Ludic + + + + + + +
+
API Reference › Http › Http.received
+
+ method +

Http.received

+
+ Http.received(handle) -> int +

Returns how many bytes of the response body have arrived. For a request given Http.save_to it is updated live while the request is pending — the bytes already written to the file — which is what a progress bar reads. For an ordinary in-memory request it is 0 until the reply lands, then the body length. 0 before Http.send.

+ +

Example

program Demo {
+  var dl: int = 0
+  handler Bar phase Render {
+    let got = Http.received(handle: dl)
+    let all = Http.expected(handle: dl)
+    if all > 0 { Screen.bar(x: 8, y: 8, width: 200, height: 6, value: got / 1024, max: all / 1024, color: 0x55cc77, back: 0x222222) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-request.html b/http-request.html new file mode 100644 index 00000000..73856216 --- /dev/null +++ b/http-request.html @@ -0,0 +1,32 @@ + + + + + +Http.request — Ludic + + + + + + +
+
API Reference › Http › Http.request
+
+ method +

Http.request

+
+ Http.request(method, url, body) -> int +

Starts a request with an explicit method ("GET", "PUT", "DELETE", …) and optional body.

+ +

Example

program Demo {
+  entry {
+    let h = Http.request("PUT", "https://example.com/item/1", "payload")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-save_to.html b/http-save_to.html new file mode 100644 index 00000000..5f3b6e06 --- /dev/null +++ b/http-save_to.html @@ -0,0 +1,34 @@ + + + + + +Http.save_to — Ludic + + + + + + +
+
API Reference › Http › Http.save_to
+
+ method +

Http.save_to

+
+ Http.save_to(handle, path) -> void +

Makes the response body of an Http.opened request go straight to the file at path as it arrives, instead of into memory — the way to download something large (an update package of hundreds of megabytes) with a progress bar. Call it before Http.send; afterwards it does nothing.

The file is written directly at path, with no temporary name: Http.send creates it (or truncates an existing one), and a failed or cancelled download leaves whatever had arrived. So download to a name of your own such as "x.nupkg.part" and rename it once Http.poll reports a good status — a non-2xx reply's body (an error page) is written to the file too. If the file cannot be created the request finishes with status 0, as does one whose write fails midway (a full disk).

For such a request Http.text is empty (null) and Http.body_len is the number of bytes written. Follow progress with Http.received and Http.expected. Freeing the request while it is pending cancels the download. The transfer asks for no compression (Accept-Encoding: identity) unless the request set that header, so the bytes counted are the bytes on disk. Files are limited to 2 GB.

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("GET", "https://example.com/big.bin")
+    Http.save_to(h, "big.bin.part")
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-send.html b/http-send.html new file mode 100644 index 00000000..a24b2753 --- /dev/null +++ b/http-send.html @@ -0,0 +1,33 @@ + + + + + +Http.send — Ludic + + + + + + +
+
API Reference › Http › Http.send
+
+ method +

Http.send

+
+ Http.send(handle) -> void +

Dispatches an opened request onto the background worker. After this the request is in flight; poll it.

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("GET", "https://example.com")
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-set.html b/http-set.html new file mode 100644 index 00000000..3504e37e --- /dev/null +++ b/http-set.html @@ -0,0 +1,34 @@ + + + + + +Http.set — Ludic + + + + + + +
+
API Reference › Http › Http.set
+
+ method +

Http.set

+
+ Http.set(handle, name, value) -> void +

Sets a request header on an opened request (before Http.send).

+ +

Example

program Demo {
+  entry {
+    let h = Http.open("GET", "https://example.com")
+    Http.set(h, "Accept", "application/json")
+    Http.send(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-status.html b/http-status.html new file mode 100644 index 00000000..269e717b --- /dev/null +++ b/http-status.html @@ -0,0 +1,33 @@ + + + + + +Http.status — Ludic + + + + + + +
+
API Reference › Http › Http.status
+
+ method +

Http.status

+
+ Http.status(handle) -> int +

Returns the cached HTTP status code (0 until the reply lands or on error).

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { print(Http.status(h)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/http-text.html b/http-text.html new file mode 100644 index 00000000..62fcc7e4 --- /dev/null +++ b/http-text.html @@ -0,0 +1,33 @@ + + + + + +Http.text — Ludic + + + + + + +
+
API Reference › Http › Http.text
+
+ method +

Http.text

+
+ Http.text(handle) -> str +

Returns the response body as a NUL-terminated string. Pair with Json.parse (#44) for JSON APIs.

+ +

Example

program Demo {
+  entry {
+    let h = Http.get("https://example.com")
+    if Http.poll(h) > 0 { print(Http.text(h)) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-add.html b/huge-add.html new file mode 100644 index 00000000..2d05f446 --- /dev/null +++ b/huge-add.html @@ -0,0 +1,32 @@ + + + + + +Huge.add — Ludic + + + + + + +
+
API Reference › Huge › Huge.add
+
+ method +

Huge.add

+
+ Huge.add(a, b) -> Huge +

Adds two big numbers, aligning their exponents; a term too small to matter at display precision is dropped.

+ +

Example

program Demo {
+  handler Step phase Update {
+    gold = Huge.add(gold, income)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-cmp.html b/huge-cmp.html new file mode 100644 index 00000000..01479f19 --- /dev/null +++ b/huge-cmp.html @@ -0,0 +1,32 @@ + + + + + +Huge.cmp — Ludic + + + + + + +
+
API Reference › Huge › Huge.cmp
+
+ method +

Huge.cmp

+
+ Huge.cmp(a, b) -> int +

Compares two big numbers, returning -1, 0, or 1 as a is less than, equal to, or greater than b.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Huge.cmp(gold, price) >= 0 { buy() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-exp.html b/huge-exp.html new file mode 100644 index 00000000..a9bb68bc --- /dev/null +++ b/huge-exp.html @@ -0,0 +1,32 @@ + + + + + +Huge.exp — Ludic + + + + + + +
+
API Reference › Huge › Huge.exp
+
+ method +

Huge.exp

+
+ Huge.exp(a) -> int +

Returns the base-10 exponent — how many orders of magnitude the value spans.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let order = Huge.exp(gold)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-from.html b/huge-from.html new file mode 100644 index 00000000..d4ad34cf --- /dev/null +++ b/huge-from.html @@ -0,0 +1,32 @@ + + + + + +Huge.from — Ludic + + + + + + +
+
API Reference › Huge › Huge.from
+
+ method +

Huge.from

+
+ Huge.from(value) -> Huge +

Lifts a plain int into a Huge, the starting point for idle-game magnitudes that grow past the integer range.

+ +

Example

program Demo {
+  handler Step phase Update {
+    var gold = Huge.from(1000)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-mantissa.html b/huge-mantissa.html new file mode 100644 index 00000000..640b5f32 --- /dev/null +++ b/huge-mantissa.html @@ -0,0 +1,32 @@ + + + + + +Huge.mantissa — Ludic + + + + + + +
+
API Reference › Huge › Huge.mantissa
+
+ method +

Huge.mantissa

+
+ Huge.mantissa(a) -> fixed +

Returns the normalized mantissa, a fixed in [1, 10) (or 0). Pair it with Huge.exp to format a value your own way.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let m = Huge.mantissa(gold)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-mul.html b/huge-mul.html new file mode 100644 index 00000000..84ce21f2 --- /dev/null +++ b/huge-mul.html @@ -0,0 +1,32 @@ + + + + + +Huge.mul — Ludic + + + + + + +
+
API Reference › Huge › Huge.mul
+
+ method +

Huge.mul

+
+ Huge.mul(a, b) -> Huge +

Multiplies two big numbers (mantissas multiply, exponents add) — the exponential growth step at the heart of an idle game.

+ +

Example

program Demo {
+  handler Step phase Update {
+    gold = Huge.mul(gold, multiplier)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-neg.html b/huge-neg.html new file mode 100644 index 00000000..9535541d --- /dev/null +++ b/huge-neg.html @@ -0,0 +1,32 @@ + + + + + +Huge.neg — Ludic + + + + + + +
+
API Reference › Huge › Huge.neg
+
+ method +

Huge.neg

+
+ Huge.neg(a) -> Huge +

Returns -a, the same magnitude with the opposite sign.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let owed = Huge.neg(balance)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-sign.html b/huge-sign.html new file mode 100644 index 00000000..abb824da --- /dev/null +++ b/huge-sign.html @@ -0,0 +1,32 @@ + + + + + +Huge.sign — Ludic + + + + + + +
+
API Reference › Huge › Huge.sign
+
+ method +

Huge.sign

+
+ Huge.sign(a) -> int +

Returns the sign of the value: -1, 0, or 1.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Huge.sign(balance) < 0 { warn() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-str.html b/huge-str.html new file mode 100644 index 00000000..1007bef3 --- /dev/null +++ b/huge-str.html @@ -0,0 +1,32 @@ + + + + + +Huge.str — Ludic + + + + + + +
+
API Reference › Huge › Huge.str
+
+ method +

Huge.str

+
+ Huge.str(a) -> string +

Renders the value in scientific form, like 1.23e45 (a two-decimal mantissa). This is how you show an idle magnitude on the HUD.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Screen.draw_text(8, 8, Huge.str(gold), Color.White, 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/huge-sub.html b/huge-sub.html new file mode 100644 index 00000000..8573a049 --- /dev/null +++ b/huge-sub.html @@ -0,0 +1,32 @@ + + + + + +Huge.sub — Ludic + + + + + + +
+
API Reference › Huge › Huge.sub
+
+ method +

Huge.sub

+
+ Huge.sub(a, b) -> Huge +

Subtracts b from a.

+ +

Example

program Demo {
+  handler Step phase Update {
+    gold = Huge.sub(gold, cost)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..f3d0ff03 --- /dev/null +++ b/index.html @@ -0,0 +1,201 @@ + + + + + +Ludic — a compiled game language + + + + + + + + + + + +
+
+
+

A compiled language for 2D games.

+

Ludic compiles ahead of time to a native binary — no engine to install, no interpreter, nothing shipped beside the executable. The entity-component system is part of the syntax, the maths is deterministic fixed-point, and ludicc is itself written in Ludic: no C is generated, compiled or linked in a build.

+ +
+
+
hello.ludic
+
# the smallest program that exercises the whole ECS pipeline
+program Hello {
+
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+
+  handler SpawnEnemies phase Start {
+    spawn Enemy { Position { column: 3, row: 4 },   Velocity { delta_x: 1, delta_y: 0 } }
+    spawn Enemy { Position { column: 10, row: 2 },  Velocity { delta_x: 0, delta_y: 1 } }
+  }
+
+  # a handler declares the entities it touches; the body
+  # runs once per match, each property bound by name.
+  @Queries(these: [Position, Velocity])
+  handler AdvancePositions phase FixedUpdate {
+    Position.column += Velocity.delta_x
+    Position.row    = Position.row + Velocity.delta_y
+  }
+
+  handler ReportPositions phase Update {
+    for (moving) in query [Position] {
+      print(moving.column)
+      print(moving.row)
+    }
+    quit()
+  }
+}
+
+
+
+
.ludic→ludicc→LLVM IR→native binary
+

ludicc lowers straight to LLVM IR and links a native binary. There is no C step in between and no runtime to ship alongside — the game is the executable.

+
+
+ +
+
+
+
Why Ludic
+

A language shaped around the game.

+

The things you normally bolt on — an entity-component system, a renderer, a deterministic clock, save/load — are primitives of the language itself. One way to do each, so there is little to decide and nothing to wire up.

+
+

ECS in the syntax

property, model, and handler are keywords. Query entities with query [A, B, {Tag}] and iterate matches directly — no framework to wire up.

Deterministic runtime

Q16.16 fixed-point math and a seeded RNG mean the same inputs produce the same frame — byte-for-byte — every run. Ideal for replays and lockstep netcode.

Drawing & named colors

The Screen API draws rectangles, text and pixels with named arguments; Color.Crimson and 220 more names read like English and cost nothing at runtime.

One self-contained binary

A game compiles to a single native executable — no engine to install, no interpreter, no runtime shipped beside it. Build, and run the file.

Snapshot save/load

save() and load() serialize the entire ECS world — every entity, property and program var — in one call.

Scenes & state machines

scene/layer/become model mutually-exclusive game states with enter/exit hooks; match/machine/state handle dispatch and per-entity FSMs.

Retained UI & assets

Declare a widget tree as data with ui — panels, labels, buttons, 9-slice skins, keyboard focus. Sprites decode from PNG at runtime; text is real TrueType.

Modules & native calls

module + @export fn builds a shared library of plain native symbols; extern fn … = "symbol" reaches out to any native library when you need the platform.

+
+
+ +
+
+
+
Show, don't tell
+

Real programs, one toolchain.

+

The same ludicc that builds a JRPG builds a from-scratch Snake and a scene demo. Nothing is hardcoded to a genre — and every token below links into the reference.

+
+
+
+
↳
+
+
+ +
+
+ +
+
+ +
+
+
+
Get started
+

One command to install. One to play.

+

Install the toolchain with a single command — it brings the compiler, the <code>ludic</code> CLI, the engine runtime and the editor tooling, and needs nothing else on your machine but a C toolchain to link with. Then create a project and run it.

+
+
+
1

Install

The installer downloads a verified toolchain for your platform into ~/.ludic and puts it on your PATH. Nothing else is touched; uninstalling is rm -rf ~/.ludic. On a platform with no prebuilt toolchain it bootstraps from the compiler's own IR seed instead — same result, a few seconds longer.

2

Create a project

ludic new mygame writes a manifest, a program that already moves something on screen, a test, and an assets/ directory. There is no scaffolding to choose and no build file to write.

3

Run it

ludic run compiles src/main.ludic to a native binary and launches it. ludic build stops at the binary — one self-contained executable, with nothing to ship beside it.

4

Test it, headlessly

ludic test compiles and runs every test block in the project. --headless renders frames to a .ppm from piped input, so a game is deterministic enough to diff in CI.

+
+
zsh — ludic
+
# install the toolchain (macOS, Linux)
+$ curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh
+→ installed ludic 0.4.0 → ~/.ludic
+
+# a project that builds and plays as it stands
+$ ludic new mygame
+$ cd mygame
+$ ludic run
+→ a native window, running your game
+
+# tests, and a deterministic headless render for CI
+$ ludic test
+$ ludic build --headless
+$ printf 'ddddwww' | ./build/mygame_headless
+→ writes build/out.ppm
+
+
+
+
+
+ +
+
+
+
Editor experience
+

One language server, every editor.

+

ludic-lsp speaks LSP 3.17 over stdio: context-aware completion, diagnostics from the compiler itself, go-to-definition and rename across imported files, and comment-preserving formatting. It even understands ```ludic fences in Markdown.

+
+
◆ VS Code
◆ JetBrains IDEs
◆ Neovim
◆ Helix
◆ Emacs
◆ Sublime Text
◆ Zed
+

Editors spawn ludic lsp — it ships with the toolchain, so there is nothing extra to build or install. The same formatter runs as ludic fmt for pre-commit hooks and CI.

+
+
+ + + + + + + diff --git a/input-action.html b/input-action.html new file mode 100644 index 00000000..ca284e3a --- /dev/null +++ b/input-action.html @@ -0,0 +1,33 @@ + + + + + +Input.action — Ludic + + + + + + +
+
API Reference › Input › Input.action
+
+ method +

Input.action

+
+ Input.action(action: str, key: int) -> void +

Registers a default keyboard binding: it binds key to the action only if the action has no key bound yet. Ship your defaults with Input.action in a Boot handler; a player's later Input.rebind (or a loaded key-map) is not clobbered, and re-running the defaults is idempotent. Combine with Input.bind_pad for a device-agnostic action, and read it with Input.active.

+ +

Example

program Defaults {
+  entry {
+    Input.action("jump", ' ')
+    if Input.active("jump") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-active.html b/input-active.html new file mode 100644 index 00000000..53dbd038 --- /dev/null +++ b/input-active.html @@ -0,0 +1,33 @@ + + + + + +Input.active — Ludic + + + + + + +
+
API Reference › Input › Input.active
+
+ method +

Input.active

+
+ Input.active(action: str) -> bool +

Returns whether a named action is active this frame, reading the whole device layer: true if any of the action's bound keyboard keys is in the multi-key held-set or any bound gamepad button is down on pad 0. Unlike Input.down (which reads only the single per-frame key), this sees simultaneous holds (move and jump) and is device-agnostic. The frame loop commits the device layer automatically, so it reads live without a manual Input.poll.

+ +

Example

program Active {
+  entry {
+    Input.action("left", 'a')
+    if Input.active("left") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-axis.html b/input-axis.html new file mode 100644 index 00000000..6d9b028b --- /dev/null +++ b/input-axis.html @@ -0,0 +1,34 @@ + + + + + +Input.axis — Ludic + + + + + + +
+
API Reference › Input › Input.axis
+
+ method +

Input.axis

+
+ Input.axis(neg, pos) -> fixed +

Returns a digital axis from two keys: +1.0 when the positive key is held, -1.0 when the negative one is, 0 otherwise (both or neither). The building block for keyboard movement; a gamepad stick feeds the analog value through the same read.

+ +

Example

program Demo {
+  entry {
+    Input.press('d')
+    Input.poll()
+    print(Math.round(Input.axis('a', 'd')))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-axis_i.html b/input-axis_i.html new file mode 100644 index 00000000..d6842e9b --- /dev/null +++ b/input-axis_i.html @@ -0,0 +1,34 @@ + + + + + +Input.axis_i — Ludic + + + + + + +
+
API Reference › Input › Input.axis_i
+
+ method +

Input.axis_i

+
+ Input.axis_i(neg, pos) -> int +

Returns a directional intent as a plain int: +1 when the positive key is held, -1 when the negative one is, 0 for neither or both. It reads the multi-key device set, so it replaces the ki(key_down('d')) - ki(key_down('a')) boolean-to-int boilerplate and feeds an int mover directly — dx = Input.axis_i('a', 'd'), dy = Input.axis_i('w', 's'). For an analog / normalized value use Input.axis (fixed) or Input.vector.

+ +

Example

program Demo {
+  entry {
+    Input.press('d')
+    Input.poll()
+    print(Input.axis_i('a', 'd'))     # 1
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-bind.html b/input-bind.html new file mode 100644 index 00000000..5a6b1c3e --- /dev/null +++ b/input-bind.html @@ -0,0 +1,35 @@ + + + + + +Input.bind — Ludic + + + + + + +
+
API Reference › Input › Input.bind
+
+ method +

Input.bind

+
+ Input.bind(action: str, key: int) -> void +

Binds a physical key (a character code such as 'w' or ' ') to a named action, creating the action the first time it is named. Gameplay then reads the action with Input.down / Input.pressed instead of a raw key, which is what makes rebinding and alternate control schemes clean. Call it more than once with the same action to bind several keys to it; binding a key already on the action is a no-op. Actions are advanced once per frame by Input.poll.

+ +

Example

program Bindings {
+  entry {
+    Input.bind("jump", ' ')
+    Input.bind("up", 'w')
+    Input.poll()
+    if Input.down("jump") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-bind_pad.html b/input-bind_pad.html new file mode 100644 index 00000000..380c0b15 --- /dev/null +++ b/input-bind_pad.html @@ -0,0 +1,34 @@ + + + + + +Input.bind_pad — Ludic + + + + + + +
+
API Reference › Input › Input.bind_pad
+
+ method +

Input.bind_pad

+
+ Input.bind_pad(action: str, button: int) -> void +

Maps a gamepad button (on pad 0) to a named action, so the action fires from the keyboard or the pad. The same action can carry both keyboard keys (Input.bind / Input.action) and pad buttons; Input.active and its edges fire on either device — one action, any input.

+ +

Example

program Pad {
+  entry {
+    Input.action("fire", 'j')
+    Input.bind_pad("fire", 0)
+    if Input.active("fire") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-cursor_mode.html b/input-cursor_mode.html new file mode 100644 index 00000000..f36ec307 --- /dev/null +++ b/input-cursor_mode.html @@ -0,0 +1,40 @@ + + + + + +Input.cursor_mode — Ludic + + + + + + +
+
API Reference › Input › Input.cursor_mode
+
+ method +

Input.cursor_mode

+
+ Input.cursor_mode(mode) +

Sets how a windowed game captures the OS mouse cursor:

- 0 normal (default) — the cursor is visible and free. +- 1 hidden — the cursor is hidden while the window is focused, so a game can draw its own reticle. +- 2 locked — hidden and dissociated from the mouse; the mouse feeds relative motion through Input.mouse_dx / mouse_dy, and Input.mouse_x / mouse_y is a clamped virtual cursor — the FPS / twin-stick aim mode. +- 3 confined — dissociated but visible; the mouse cannot leave the window.

The platform **auto-releases** (shows the cursor and reconnects it) while the window is not the key window (Cmd-Tab), and restores it on window close, so the cursor is never left captured. On a headless / non-windowed build it is a no-op.

+ +

Example

program Aim {
+  property Tag { n: int = 0 }
+  model P { Tag }
+  @OnStart handler Boot { Input.cursor_mode(2) }     # lock + hide for aiming
+  handler Draw phase Render {
+    Screen.clear(0x101018)
+    Screen.fill_rectangle(Input.mouse_x() - 2, Input.mouse_y() - 2, 4, 4, 0xffcc00)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-down.html b/input-down.html new file mode 100644 index 00000000..d146e51b --- /dev/null +++ b/input-down.html @@ -0,0 +1,34 @@ + + + + + +Input.down — Ludic + + + + + + +
+
API Reference › Input › Input.down
+
+ method +

Input.down

+
+ Input.down(action: str) -> bool +

Returns whether a named action is held on the frame last read by Input.poll — true when the polled key is one of the keys bound to the action. Use it for continuous input (move while held); for a one-shot press use Input.pressed. Reading an action rather than a key is what lets the same handler serve any binding.

+ +

Example

program Held {
+  entry {
+    Input.bind("up", 'w')
+    Input.poll()
+    if Input.down("up") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-just_pressed.html b/input-just_pressed.html new file mode 100644 index 00000000..e49afdfa --- /dev/null +++ b/input-just_pressed.html @@ -0,0 +1,33 @@ + + + + + +Input.just_pressed — Ludic + + + + + + +
+
API Reference › Input › Input.just_pressed
+
+ method +

Input.just_pressed

+
+ Input.just_pressed(action: str) -> bool +

Returns whether a named action went active this frame — active now, not active last frame. This is the deterministic on-press edge over the device layer (keyboard or pad), the event-style "just pressed" without a callback: a handler polls it each frame and reacts. Pair with Input.just_released for the release edge and Input.active for the held state.

+ +

Example

program Press {
+  entry {
+    Input.action("jump", ' ')
+    if Input.just_pressed("jump") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-just_released.html b/input-just_released.html new file mode 100644 index 00000000..1d8cf649 --- /dev/null +++ b/input-just_released.html @@ -0,0 +1,33 @@ + + + + + +Input.just_released — Ludic + + + + + + +
+
API Reference › Input › Input.just_released
+
+ method +

Input.just_released

+
+ Input.just_released(action: str) -> bool +

Returns whether a named action went inactive this frame — not active now, active last frame. The on-release edge over the device layer (keyboard or pad), symmetric with Input.just_pressed. Use it for release-triggered actions (variable-height jump cut-off, charge-and-release). Deterministic, so a replay fires the same edges.

+ +

Example

program Release {
+  entry {
+    Input.action("charge", ' ')
+    if Input.just_released("charge") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-key.html b/input-key.html new file mode 100644 index 00000000..17e0b5b2 --- /dev/null +++ b/input-key.html @@ -0,0 +1,60 @@ + + + + + +Input.key — Ludic + + + + + + +
+
API Reference › Input › Input.key
+
+ method +

Input.key

+
+ Input.key() -> int +

Returns the key currently pressed this frame as a character code, or 0 when no key is down. Compare the result against character literals such as 'w', 'a', 's', 'd', or ' ' to drive movement and actions. Read it in an Input-phase handler so input is handled once per frame before the world updates. Because it reports the key held on this frame rather than a one-shot press event, a common pattern for "press to confirm" is to wait until Input.key() returns 0 (key released) before accepting the next press.

+ +

Example

program MoveByKey {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+  }
+
+  handler ReadInput phase Input {
+    let pressed = Input.key()
+    for (Position) in query [Position, {Player}] {
+      if pressed == 'w' { Position.row = Position.row - 1 }
+      if pressed == 's' { Position.row = Position.row + 1 }
+      if pressed == 'a' { Position.column = Position.column - 1 }
+      if pressed == 'd' { Position.column = Position.column + 1 }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-key_down.html b/input-key_down.html new file mode 100644 index 00000000..bba2d053 --- /dev/null +++ b/input-key_down.html @@ -0,0 +1,34 @@ + + + + + +Input.key_down — Ludic + + + + + + +
+
API Reference › Input › Input.key_down
+
+ method +

Input.key_down

+
+ Input.key_down(key) -> bool +

Returns whether a physical key is held this frame — from the platform when windowed, from the polled key when headless, and from Input.press injection on any target. Unlike the single-key Input.down, several keys can be held at once (move left and jump). Every key on the keyboard has a code: letters, digits and punctuation are their US-layout character, Space, Enter, Esc, Tab, Backspace, Shift, Ctrl and Alt their usual ones, the arrows 128-131, and the keys that type nothing 132-166 - F1-F12 at 132, Home, End, PageUp, PageDown, Insert and Delete at 144, Caps Lock at 150, the numpad digits at 152 and its *, +, -, . and / at 162. Key.F1, Key.Home and Key.Numpad0 name them.

+ +

Example

program Demo {
+  entry {
+    Input.press('a')
+    Input.poll()
+    if Input.key_down('a') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-key_label.html b/input-key_label.html new file mode 100644 index 00000000..3b7bf34e --- /dev/null +++ b/input-key_label.html @@ -0,0 +1,34 @@ + + + + + +Input.key_label — Ludic + + + + + + +
+
API Reference › Input › Input.key_label
+
+ method +

Input.key_label

+
+ Input.key_label(key) -> string +

Returns the name to show for key. The letter, digit and punctuation codes that Input.key_down reads are physical keys, named for what they type on a US layout: 'w' is the key above 's' on every keyboard, whatever layout, language, input method or Caps Lock state the player has. So WASD is always under the left hand, and the label is what that key types in the player's layout - "W" on QWERTY, "Z" on AZERTY. Space, Enter, Esc, Tab, Backspace, Shift, Ctrl, Alt and the arrows get words, and a code with no name returns "". The keys that type no character - F1-F12, Home, End, PageUp, PageDown, Insert, Delete, Caps Lock and the numpad, codes 132-166 - are named "F1", "Home", "CapsLock", "Num 0", "Num /" and so on, and the layout is not asked: a key that types nothing is called the same thing on every layout.

On Windows the label is read from the active layout. Headless, on macOS and in the browser it is the US character, upper case.

+ +

Example

program Demo {
+  entry {
+    print(Input.key_label('w'))
+    print(Input.key_label(32))
+    print(Input.key_label(Key.Left))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-key_pressed.html b/input-key_pressed.html new file mode 100644 index 00000000..856b2717 --- /dev/null +++ b/input-key_pressed.html @@ -0,0 +1,34 @@ + + + + + +Input.key_pressed — Ludic + + + + + + +
+
API Reference › Input › Input.key_pressed
+
+ method +

Input.key_pressed

+
+ Input.key_pressed(key) -> bool +

Returns whether a key went down this frame — held now, not held last frame. A clean rising edge for one-shot actions (fire, confirm), distinct from the continuous Input.key_down.

+ +

Example

program Demo {
+  entry {
+    Input.press(' ')
+    Input.poll()
+    if Input.key_pressed(' ') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-key_released.html b/input-key_released.html new file mode 100644 index 00000000..bff3353f --- /dev/null +++ b/input-key_released.html @@ -0,0 +1,36 @@ + + + + + +Input.key_released — Ludic + + + + + + +
+
API Reference › Input › Input.key_released
+
+ method +

Input.key_released

+
+ Input.key_released(key) -> bool +

Returns whether a key went up this frame — held last frame, not held now. The falling edge, for release-triggered actions (charge-and-release, letting go of a grip).

+ +

Example

program Demo {
+  entry {
+    Input.press('a')
+    Input.poll()
+    Input.release('a')
+    Input.poll()
+    if Input.key_released('a') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-mouse_down.html b/input-mouse_down.html new file mode 100644 index 00000000..4d7bd68e --- /dev/null +++ b/input-mouse_down.html @@ -0,0 +1,34 @@ + + + + + +Input.mouse_down — Ludic + + + + + + +
+
API Reference › Input › Input.mouse_down
+
+ method +

Input.mouse_down

+
+ Input.mouse_down(button) -> bool +

Returns whether a mouse button is held this frame — button 0 left, 1 right, 2 middle/other.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(0, 0, 1, 0)
+    Input.poll()
+    if Input.mouse_down(0) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-mouse_dx.html b/input-mouse_dx.html new file mode 100644 index 00000000..93013520 --- /dev/null +++ b/input-mouse_dx.html @@ -0,0 +1,36 @@ + + + + + +Input.mouse_dx — Ludic + + + + + + +
+
API Reference › Input › Input.mouse_dx
+
+ method +

Input.mouse_dx

+
+ Input.mouse_dx() -> int +

The change in the mouse's x since the previous Input.poll — the relative motion for dragging, panning, or a mouse-look camera.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(10, 0, 0, 0)
+    Input.poll()
+    Input.set_mouse(15, 0, 0, 0)
+    Input.poll()
+    print(Input.mouse_dx())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-mouse_dy.html b/input-mouse_dy.html new file mode 100644 index 00000000..e99b3341 --- /dev/null +++ b/input-mouse_dy.html @@ -0,0 +1,36 @@ + + + + + +Input.mouse_dy — Ludic + + + + + + +
+
API Reference › Input › Input.mouse_dy
+
+ method +

Input.mouse_dy

+
+ Input.mouse_dy() -> int +

The change in the mouse's y since the previous Input.poll — the vertical companion to Input.mouse_dx.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(0, 10, 0, 0)
+    Input.poll()
+    Input.set_mouse(0, 18, 0, 0)
+    Input.poll()
+    print(Input.mouse_dy())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-mouse_x.html b/input-mouse_x.html new file mode 100644 index 00000000..8d9a8529 --- /dev/null +++ b/input-mouse_x.html @@ -0,0 +1,34 @@ + + + + + +Input.mouse_x — Ludic + + + + + + +
+
API Reference › Input › Input.mouse_x
+
+ method +

Input.mouse_x

+
+ Input.mouse_x() -> int +

The mouse's x position this frame — fed by the window when windowed, or by Input.set_mouse otherwise.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(10, 20, 0, 0)
+    Input.poll()
+    print(Input.mouse_x())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-mouse_y.html b/input-mouse_y.html new file mode 100644 index 00000000..3ae683b2 --- /dev/null +++ b/input-mouse_y.html @@ -0,0 +1,34 @@ + + + + + +Input.mouse_y — Ludic + + + + + + +
+
API Reference › Input › Input.mouse_y
+
+ method +

Input.mouse_y

+
+ Input.mouse_y() -> int +

The mouse's y position this frame — fed by the window when windowed, or by Input.set_mouse otherwise.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(10, 20, 0, 0)
+    Input.poll()
+    print(Input.mouse_y())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-move_i.html b/input-move_i.html new file mode 100644 index 00000000..f02b8757 --- /dev/null +++ b/input-move_i.html @@ -0,0 +1,30 @@ + + + + + +Input.move_i — Ludic + + + + + + +
+
API Reference › Input › Input.move_i
+
+ method +

Input.move_i

+
+ Input.move_i() -> IVec2 +

WASD or the arrow keys, and the left stick of pad 0 (past a 0.35 deadzone) when one is connected, as an IVec2 of -1, 0 or 1 per axis — what a top-down mover feeds TopDown.move.

+ +

Example

# doc-check: skip — illustrative
+let move = Input.move_i()
+TopDown.move(player, move.x, move.y)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-pad_axis.html b/input-pad_axis.html new file mode 100644 index 00000000..b31930c5 --- /dev/null +++ b/input-pad_axis.html @@ -0,0 +1,33 @@ + + + + + +Input.pad_axis — Ludic + + + + + + +
+
API Reference › Input › Input.pad_axis
+
+ method +

Input.pad_axis

+
+ Input.pad_axis(pad, axis) -> fixed +

Returns a gamepad analog axis as a fixed in -1.0..+1.0 — axis 0 left-stick x, 1 left-stick y, 2 right-stick x, 3 right-stick y. Apply your own deadzone near zero.

+ +

Example

program Demo {
+  entry {
+    Input.set_pad(0, true, 0, 1.0, 0.0, 0.0, 0.0)
+    print(Math.round(Input.pad_axis(0, 0)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-pad_button.html b/input-pad_button.html new file mode 100644 index 00000000..fd391a79 --- /dev/null +++ b/input-pad_button.html @@ -0,0 +1,33 @@ + + + + + +Input.pad_button — Ludic + + + + + + +
+
API Reference › Input › Input.pad_button
+
+ method +

Input.pad_button

+
+ Input.pad_button(pad, button) -> bool +

Returns whether button on gamepad pad is held — a bit in the pad's button mask, in the SDL standard order (A/B/X/Y, bumpers, start, …).

+ +

Example

program Demo {
+  entry {
+    Input.set_pad(0, true, 1, 0.0, 0.0, 0.0, 0.0)
+    if Input.pad_button(0, 0) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-pad_connected.html b/input-pad_connected.html new file mode 100644 index 00000000..8cf59d85 --- /dev/null +++ b/input-pad_connected.html @@ -0,0 +1,33 @@ + + + + + +Input.pad_connected — Ludic + + + + + + +
+
API Reference › Input › Input.pad_connected
+
+ method +

Input.pad_connected

+
+ Input.pad_connected(pad) -> bool +

Returns whether gamepad pad (0..3) is connected. Fed by Input.set_pad (and, where a platform gamepad binding is present, by the device); lets a game show a "controller connected" prompt.

+ +

Example

program Demo {
+  entry {
+    Input.set_pad(0, true, 0, 0.0, 0.0, 0.0, 0.0)
+    if Input.pad_connected(0) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-poll.html b/input-poll.html new file mode 100644 index 00000000..0144f387 --- /dev/null +++ b/input-poll.html @@ -0,0 +1,34 @@ + + + + + +Input.poll — Ludic + + + + + + +
+
API Reference › Input › Input.poll
+
+ method +

Input.poll

+
+ Input.poll() -> int +

Advances the input by one frame and returns the frame's key. Call it once at the top of a frame; the action reads (Input.down, Input.pressed) then report on the key it captured. It is the single place input crosses into the frame, which is what makes recording and replay possible: in record mode it saves the live key, and in replay mode it takes the next key from the tape instead of the device — so "read input" and "read a recorded snapshot" are the same call.

+ +

Example

program Poll {
+  entry {
+    Input.bind("up", 'w')
+    Input.poll()
+    if Input.down("up") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-press.html b/input-press.html new file mode 100644 index 00000000..6b9302f8 --- /dev/null +++ b/input-press.html @@ -0,0 +1,34 @@ + + + + + +Input.press — Ludic + + + + + + +
+
API Reference › Input › Input.press
+
+ method +

Input.press

+
+ Input.press(key) +

Injects a held key into the input state — the same idea as Godot's action_press. It persists until Input.release, and combines with the platform's real keys, so an AI, a replay, a tutorial, or the network can drive input exactly as a player would. Deterministic on every target.

+ +

Example

program Demo {
+  entry {
+    Input.press('w')
+    Input.poll()
+    if Input.key_down('w') { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-pressed.html b/input-pressed.html new file mode 100644 index 00000000..b59edae3 --- /dev/null +++ b/input-pressed.html @@ -0,0 +1,34 @@ + + + + + +Input.pressed — Ludic + + + + + + +
+
API Reference › Input › Input.pressed
+
+ method +

Input.pressed

+
+ Input.pressed(action: str) -> bool +

Returns whether a named action went down this frame — held on the frame last polled, but not on the one before — the edge you want for "press to jump / confirm / fire", where Input.down would retrigger every frame the key is held. Depends on the two most recent Input.poll calls, so poll once per frame.

+ +

Example

program Edge {
+  entry {
+    Input.bind("jump", ' ')
+    Input.poll()
+    if Input.pressed("jump") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-rebind.html b/input-rebind.html new file mode 100644 index 00000000..b9d54de6 --- /dev/null +++ b/input-rebind.html @@ -0,0 +1,35 @@ + + + + + +Input.rebind — Ludic + + + + + + +
+
API Reference › Input › Input.rebind
+
+ method +

Input.rebind

+
+ Input.rebind(action: str, from: int, to: int) -> void +

Replaces the key from with to on a named action, at runtime — the primitive a "press a key to rebind" options screen is built on. A no-op if the action does not exist or is not bound to from. Because gameplay reads actions, not keys, a rebind takes effect immediately with no change to the game logic.

+ +

Example

program Rebinding {
+  entry {
+    Input.bind("jump", ' ')
+    Input.rebind("jump", ' ', 'x')
+    Input.poll()
+    if Input.down("jump") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-record.html b/input-record.html new file mode 100644 index 00000000..9f43ab86 --- /dev/null +++ b/input-record.html @@ -0,0 +1,35 @@ + + + + + +Input.record — Ludic + + + + + + +
+
API Reference › Input › Input.record
+
+ method +

Input.record

+
+ Input.record() -> void +

Puts input into record mode and resets the tape: from now on each Input.poll reads the live key and appends it to a recording. Because the simulation is deterministic in its input stream, that tape is all you need to reproduce a run — play it back with Input.replay for a free exact replay, the seed of lockstep netcode. Recording captures input only; it does not change what the game sees this frame.

+ +

Example

program Recording {
+  entry {
+    Input.bind("up", 'w')
+    Input.record()
+    Input.poll()
+    if Input.down("up") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-release.html b/input-release.html new file mode 100644 index 00000000..07575711 --- /dev/null +++ b/input-release.html @@ -0,0 +1,36 @@ + + + + + +Input.release — Ludic + + + + + + +
+
API Reference › Input › Input.release
+
+ method +

Input.release

+
+ Input.release(key) +

Releases a key previously held with Input.press. After the next Input.poll the key reads as up (and fires Input.key_released that frame).

+ +

Example

program Demo {
+  entry {
+    Input.press('w')
+    Input.poll()
+    Input.release('w')
+    Input.poll()
+    print(0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-replay.html b/input-replay.html new file mode 100644 index 00000000..a89307f1 --- /dev/null +++ b/input-replay.html @@ -0,0 +1,37 @@ + + + + + +Input.replay — Ludic + + + + + + +
+
API Reference › Input › Input.replay
+
+ method +

Input.replay

+
+ Input.replay() -> void +

Rewinds the recording made by Input.record and switches to replay mode: each subsequent Input.poll returns the next key from the tape instead of the live device, so the recorded session runs again exactly — deterministic replays, demo playback, and the basis of rollback netcode. Past the end of the tape, poll reports no input.

+ +

Example

program Replaying {
+  entry {
+    Input.bind("up", 'w')
+    Input.record()
+    Input.poll()
+    Input.replay()
+    Input.poll()
+    if Input.down("up") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-set_mouse.html b/input-set_mouse.html new file mode 100644 index 00000000..caf66705 --- /dev/null +++ b/input-set_mouse.html @@ -0,0 +1,34 @@ + + + + + +Input.set_mouse — Ludic + + + + + + +
+
API Reference › Input › Input.set_mouse
+
+ method +

Input.set_mouse

+
+ Input.set_mouse(x, y, buttons, wheel) +

Injects the whole mouse state: position x/y, a buttons bitmask (bit 0 left, 1 right, 2 middle), and this frame's wheel delta. The mouse counterpart to Input.press — for headless runs, AI, replays, or network-fed cursors.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(64, 32, 1, 0)
+    Input.poll()
+    print(Input.mouse_x())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-set_pad.html b/input-set_pad.html new file mode 100644 index 00000000..98e0c8d6 --- /dev/null +++ b/input-set_pad.html @@ -0,0 +1,33 @@ + + + + + +Input.set_pad — Ludic + + + + + + +
+
API Reference › Input › Input.set_pad
+
+ method +

Input.set_pad

+
+ Input.set_pad(pad, connected, buttons, lx, ly, rx, ry) +

Injects gamepad pad's whole state: connected, a buttons bitmask, and the four stick axes (lx, ly, rx, ry) as fixed in -1.0..+1.0. A platform gamepad binding feeds the same state; until then this drives it from replays, AI, or the network.

+ +

Example

program Demo {
+  entry {
+    Input.set_pad(0, true, 5, 1.0, 0.0, 0.0, 0.0)
+    if Input.pad_button(0, 2) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-set_touch.html b/input-set_touch.html new file mode 100644 index 00000000..e26d507a --- /dev/null +++ b/input-set_touch.html @@ -0,0 +1,33 @@ + + + + + +Input.set_touch — Ludic + + + + + + +
+
API Reference › Input › Input.set_touch
+
+ method +

Input.set_touch

+
+ Input.set_touch(index, x, y, active) +

Injects touch point index (0..7): active with a position, or inactive. A platform touch binding feeds the same points; until then this drives multi-touch from replays, AI, or the network.

+ +

Example

program Demo {
+  entry {
+    Input.set_touch(0, 64, 64, true)
+    print(Input.touch_count())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-strength.html b/input-strength.html new file mode 100644 index 00000000..e272a244 --- /dev/null +++ b/input-strength.html @@ -0,0 +1,35 @@ + + + + + +Input.strength — Ludic + + + + + + +
+
API Reference › Input › Input.strength
+
+ method +

Input.strength

+
+ Input.strength(action) -> fixed +

Returns the 0.0..1.0 strength of a named action — 1.0 when any bound key is held, 0 otherwise for keys (digital), and the analog magnitude when a gamepad feeds the action. Lets analog and digital sources share one read.

+ +

Example

program Demo {
+  entry {
+    Input.bind("gas", 'w')
+    Input.press('w')
+    Input.poll()
+    print(Math.round(Input.strength("gas")))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-touch_count.html b/input-touch_count.html new file mode 100644 index 00000000..9a1bcd83 --- /dev/null +++ b/input-touch_count.html @@ -0,0 +1,34 @@ + + + + + +Input.touch_count — Ludic + + + + + + +
+
API Reference › Input › Input.touch_count
+
+ method +

Input.touch_count

+
+ Input.touch_count() -> int +

Returns how many touch points are currently active (0..8). Iterate them with Input.touch_x / Input.touch_y.

+ +

Example

program Demo {
+  entry {
+    Input.set_touch(0, 10, 20, true)
+    Input.set_touch(1, 30, 40, true)
+    print(Input.touch_count())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-touch_x.html b/input-touch_x.html new file mode 100644 index 00000000..2599eeea --- /dev/null +++ b/input-touch_x.html @@ -0,0 +1,33 @@ + + + + + +Input.touch_x — Ludic + + + + + + +
+
API Reference › Input › Input.touch_x
+
+ method +

Input.touch_x

+
+ Input.touch_x(index) -> int +

The x position of touch point index (0..7). Pair with Input.touch_y and Input.touch_count for multi-touch.

+ +

Example

program Demo {
+  entry {
+    Input.set_touch(0, 100, 200, true)
+    print(Input.touch_x(0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-touch_y.html b/input-touch_y.html new file mode 100644 index 00000000..005538d8 --- /dev/null +++ b/input-touch_y.html @@ -0,0 +1,33 @@ + + + + + +Input.touch_y — Ludic + + + + + + +
+
API Reference › Input › Input.touch_y
+
+ method +

Input.touch_y

+
+ Input.touch_y(index) -> int +

The y position of touch point index (0..7) — the vertical companion to Input.touch_x.

+ +

Example

program Demo {
+  entry {
+    Input.set_touch(0, 100, 200, true)
+    print(Input.touch_y(0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-vector.html b/input-vector.html new file mode 100644 index 00000000..36ad006f --- /dev/null +++ b/input-vector.html @@ -0,0 +1,36 @@ + + + + + +Input.vector — Ludic + + + + + + +
+
API Reference › Input › Input.vector
+
+ method +

Input.vector

+
+ Input.vector(left, right, up, down) -> Vector +

Returns a 2D movement Vector from four direction keys, normalized so a diagonal is not faster than a straight move (each diagonal component is 0.7071). The one call for eight-way keyboard movement.

+ +

Example

program Demo {
+  entry {
+    Input.press('d')
+    Input.press('w')
+    Input.poll()
+    let v = Input.vector('a', 'd', 'w', 's')
+    print(Math.round(Vector.x(v) * 100))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/input-wheel.html b/input-wheel.html new file mode 100644 index 00000000..60ea6847 --- /dev/null +++ b/input-wheel.html @@ -0,0 +1,34 @@ + + + + + +Input.wheel — Ludic + + + + + + +
+
API Reference › Input › Input.wheel
+
+ method +

Input.wheel

+
+ Input.wheel() -> int +

The scroll-wheel delta accumulated this frame — positive up, negative down, 0 when the wheel did not move. A per-frame delta, reset each Input.poll.

+ +

Example

program Demo {
+  entry {
+    Input.set_mouse(0, 0, 0, 3)
+    Input.poll()
+    print(Input.wheel())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 00000000..a098427c --- /dev/null +++ b/install.sh @@ -0,0 +1,393 @@ +#!/bin/sh +# install.sh — install the Ludic toolchain. +# +# curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh +# +# That URL is the documentation site, which publishes this file alongside the +# pages it is quoted on (see cmd_docs_gen). Everything host-shaped here is a +# variable with an environment override, so moving the project — a new domain, +# a new forge, a new name for the language — is an edit to the block below and +# to the prose that quotes it (CONTRIBUTING.md has the checklist). +# +# It puts a complete toolchain — the compiler, the `ludic` CLI, the formatter, +# the language server, the engine runtime and the bundled ludic.* packages — +# under ~/.ludic, and puts ~/.ludic/bin on your PATH. Nothing else on the system +# is touched, and uninstalling is `rm -rf ~/.ludic`. +# +# Where a prebuilt toolchain exists for the platform it is downloaded and +# checksum-verified. Where it does not, the source is fetched and bootstrapped +# from the compiler's own checked-in LLVM IR seed, which needs only clang and +# takes a few seconds — the same path a contributor uses. +# +# Options (also usable through `ludic upgrade`): +# --version X.Y.Z install that release instead of the latest +# --dir PATH install root (default: $HOME/.ludic) +# --no-modify-path do not touch any shell profile +# --from-source skip the prebuilt artifacts and bootstrap from source +# --help +# +# POSIX sh on purpose: this is the first thing a new user runs, and it has to +# work on a stock macOS /bin/sh and on a minimal Linux image alike. + +set -eu + +REPO_API="${LUDIC_FORGEJO_API:-https://git.workshopsoft.io/api/v1/repos/workshopsoft/ludic}" +REPO_URL="${LUDIC_REPO_URL:-https://git.workshopsoft.io/workshopsoft/ludic}" +INSTALL_URL="${LUDIC_INSTALL_URL:-https://workshopsoft.pages.workshopsoft.io/ludic/install.sh}" +INSTALL_DIR="${LUDIC_INSTALL_DIR:-${HOME}/.ludic}" +VERSION="" +MODIFY_PATH=1 +FROM_SOURCE=0 + +# ---- output ----------------------------------------------------------------- + +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + B=$(printf '\033[1m'); DIM=$(printf '\033[2m'); R=$(printf '\033[0m') + GREEN=$(printf '\033[32m'); RED=$(printf '\033[31m') +else + B=''; DIM=''; R=''; GREEN=''; RED='' +fi + +say() { printf '%s\n' "$*"; } +step() { printf '%s==>%s %s\n' "$B" "$R" "$*"; } +warn() { printf '%swarning:%s %s\n' "$RED" "$R" "$*" >&2; } +die() { printf '%serror:%s %s\n' "$RED" "$R" "$*" >&2; exit 1; } +have() { command -v "$1" >/dev/null 2>&1; } + +usage() { + cat </dev/null | first_tag) + [ -n "$_v" ] || _v=$(curl -fsSL "${REPO_API}/releases?limit=1" 2>/dev/null | first_tag) + printf '%s\n' "$_v" +} + +first_tag() { + sed -n 's/.*"tag_name" *: *"v\{0,1\}\([^"]*\)".*/\1/p' | head -1 +} + +sha256_of() { + if have sha256sum; then sha256sum "$1" | cut -d' ' -f1 + elif have shasum; then shasum -a 256 "$1" | cut -d' ' -f1 + else return 1 + fi +} + +# download the toolchain tarball for this platform into $TMP, verify it against +# the published checksum, and echo its path. Silent failure (returns 1) means +# "no prebuilt for this platform" — the caller falls back to source. +download_prebuilt() { + _name="ludic-${VERSION}-${OS}-${ARCH}.tar.gz" + _url="${REPO_URL}/releases/download/v${VERSION}/${_name}" + _out="${TMP}/${_name}" + fetch "$_url" "$_out" 2>/dev/null || return 1 + if fetch "${_url}.sha256" "${_out}.sha256" 2>/dev/null; then + _want=$(cut -d' ' -f1 < "${_out}.sha256") + _got=$(sha256_of "$_out" || true) + if [ -n "$_got" ] && [ "$_want" != "$_got" ]; then + die "checksum mismatch for ${_name} (expected ${_want}, got ${_got})" + fi + [ -n "$_got" ] || warn "no sha256 tool found; the download was not verified" + else + warn "no published checksum for ${_name}; the download was not verified" + fi + printf '%s\n' "$_out" +} + +# ---- installing ------------------------------------------------------------- + +# Replace the install root with the staged tree. The swap is done at the end so +# an interrupted download never leaves a half-installed toolchain behind, and a +# working install is only removed once the new one is ready to take its place. +# Is this directory a usable install root? A toolchain is not just binaries: a +# game links against the engine runtime and resolves the bundled packages from +# here, so an artifact missing them installs something that cannot build +# anything. Older releases predate this layout, which is exactly the case that +# must degrade to a source build rather than to a broken ~/.ludic. +valid_root() { + [ -x "${1}/bin/ludic" ] && [ -x "${1}/bin/ludicc" ] \ + && [ -d "${1}/runtime/native" ] && [ -f "${1}/VERSION" ] +} + +install_staged() { + _staged="$1" + valid_root "$_staged" || die "the staged toolchain is incomplete" + mkdir -p "$(dirname "$INSTALL_DIR")" + if [ -d "$INSTALL_DIR" ]; then + rm -rf "${INSTALL_DIR}.old" + mv "$INSTALL_DIR" "${INSTALL_DIR}.old" + # The install root is not only the artifact: `ludic add` caches packages in + # /store, which is the user's data and takes a network round trip to + # rebuild. Carry it across rather than replacing the directory out from under + # it. (Anything else here is part of the toolchain and should be replaced.) + if [ -d "${INSTALL_DIR}.old/store" ]; then + mv "${INSTALL_DIR}.old/store" "${_staged}/store" || warn "could not carry the package store across" + fi + fi + mv "$_staged" "$INSTALL_DIR" + rm -rf "${INSTALL_DIR}.old" +} + +# Build the toolchain from the source of a tag: clang assembles the compiler's +# own IR seed, that compiler builds the CLI, and the CLI builds the rest. This +# is the only step Ludic cannot do for itself, and it is also the whole story of +# how the language bootstraps. +install_from_source() { + have git || die "installing from source needs git" + have clang || have cc || die "installing from source needs clang. ${CC_HINT}" + step "building from source (no prebuilt toolchain for ${OS}-${ARCH})" + _src="${TMP}/src" + git clone --quiet --depth 1 --branch "v${VERSION}" "${REPO_URL}.git" "$_src" 2>/dev/null \ + || git clone --quiet --depth 1 "${REPO_URL}.git" "$_src" \ + || die "could not clone ${REPO_URL}" + # Which entry point builds the toolchain depends on the age of the tag: the + # contributor tool was split out of the CLI after 0.5.2, so an older release + # still bootstraps through main.ludic. A release older than either predates the + # CLI entirely, and saying so beats failing deep inside clang. + if [ -f "${_src}/tools/ludic-cli/dev.ludic" ]; then + _boot='bin/ludicc tools/ludic-cli/dev.ludic -o bin/ludic-dev && bin/ludic-dev build' + elif [ -f "${_src}/tools/ludic-cli/main.ludic" ]; then + _boot='bin/ludicc tools/ludic-cli/main.ludic -o bin/ludic && bin/ludic dev build' + else + die "release v${VERSION} predates the ludic CLI; install a newer one with --version X.Y.Z" + fi + CC=clang; have clang || CC=cc + ( cd "$_src" \ + && mkdir -p bin \ + && "$CC" selfhost/ludicc.seed.ll -o bin/ludicc 2>/dev/null \ + && eval "$_boot" >/dev/null ) || die "the source build failed" + + _stage="${TMP}/stage" + mkdir -p "${_stage}/bin" + cp "${_src}"/bin/ludic "${_src}"/bin/ludicc "${_src}"/bin/ludic-fmt "${_src}"/bin/ludic-lsp "${_stage}/bin/" + cp -R "${_src}/runtime" "${_stage}/runtime" + cp -R "${_src}/packages" "${_stage}/packages" + cp "${_src}/VERSION" "${_src}/LICENSE" "${_src}/README.md" "${_stage}/" + install_staged "$_stage" +} + +# ---- PATH ------------------------------------------------------------------- +# +# "Installed but `ludic` is not a command" is the most common way an installer +# fails, so this is deliberate about it. +# +# The PATH edit itself lives in one file, /env, and each shell profile +# gets a single line that sources it. That keeps the change easy to find, easy to +# undo (delete the line, delete ~/.ludic), and means a later install does not +# accumulate a second export. +# +# Which profiles: enough to cover every shell the user actually opens, not just +# the one $SHELL happens to name. +# +# ~/.profile sh, dash, and bash login shells that have no .bash_profile. +# Created when missing — it is the portable catch-all. +# ~/.bash_profile bash login shells (what macOS Terminal starts). Only touched +# ~/.bashrc when they already exist: creating .bash_profile would stop +# bash from reading ~/.profile and silently break other tools. +# ~/.zshenv zsh reads this for *every* invocation, interactive or not, +# and never reads ~/.profile. Created when missing. +# fish config only when fish is installed. + +# write /env and /env.fish — the one place the PATH is set +write_env_files() { + cat > "${INSTALL_DIR}/env" < "${INSTALL_DIR}/env.fish" </dev/null; then + ALREADY="${ALREADY} ${_file}" + return 0 + fi + mkdir -p "$(dirname "$_file")" 2>/dev/null || true + printf '\n# ludic\n%s\n' "$_line" >> "$_file" 2>/dev/null || { + warn "could not write ${_file}" + return 0 + } + TOUCHED="${TOUCHED} ${_file}" +} + +add_to_path() { + [ "$MODIFY_PATH" -eq 1 ] || return 0 + write_env_files + _posix=". \"${INSTALL_DIR}/env\"" + + add_line "${HOME}/.profile" "$_posix" + add_line "${ZDOTDIR:-$HOME}/.zshenv" "$_posix" + # bash reads ~/.bashrc for a non-login interactive shell — what most Linux + # terminal emulators start — and it is safe to create, since its presence + # changes nothing else. ~/.bash_profile is only appended to when it already + # exists: creating that one would stop bash reading ~/.profile. + [ -f "${HOME}/.bash_profile" ] && add_line "${HOME}/.bash_profile" "$_posix" + if [ -f "${HOME}/.bashrc" ] || have bash; then + add_line "${HOME}/.bashrc" "$_posix" + fi + if have fish || [ -f "${HOME}/.config/fish/config.fish" ]; then + add_line "${HOME}/.config/fish/config.fish" "source \"${INSTALL_DIR}/env.fish\"" + fi + return 0 +} + +# ---- go --------------------------------------------------------------------- + +TMP=$(mktemp -d "${TMPDIR:-/tmp}/ludic-install.XXXXXX") +trap 'rm -rf "$TMP"' EXIT INT TERM + +if [ -z "$VERSION" ]; then + step "looking up the latest release" + VERSION=$(latest_version || true) + [ -n "$VERSION" ] || die "could not determine the latest version (set one with --version X.Y.Z)" +fi +say " ludic ${B}${VERSION}${R} ${DIM}${OS}-${ARCH} -> ${INSTALL_DIR}${R}" + +if [ "$FROM_SOURCE" -eq 1 ]; then + install_from_source +else + step "downloading the toolchain" + if TARBALL=$(download_prebuilt); then + STAGE="${TMP}/stage" + mkdir -p "$STAGE" + tar -xzf "$TARBALL" -C "$STAGE" + # A current artifact unpacks to one top-level directory that is the install + # root; an older one unpacks its contents directly. Try both, and if neither + # is a usable root the release predates this layout — build from source + # instead of installing something that cannot compile a game. + ROOTDIR="" + if valid_root "$STAGE"; then + ROOTDIR="$STAGE" + else + for _d in "$STAGE"/*; do + if [ -d "$_d" ] && valid_root "$_d"; then ROOTDIR="$_d"; break; fi + done + fi + if [ -n "$ROOTDIR" ]; then + install_staged "$ROOTDIR" + else + warn "the ${VERSION} artifact for ${OS}-${ARCH} is not a complete toolchain; building from source" + install_from_source + fi + else + install_from_source + fi +fi + +TOUCHED='' +ALREADY='' +add_to_path + +# ---- report ----------------------------------------------------------------- + +INSTALLED=$("${INSTALL_DIR}/bin/ludic" version 2>/dev/null || echo "ludic ${VERSION}") +say "" +say "${GREEN}installed${R} ${INSTALLED} -> ${INSTALL_DIR}" + +if [ -n "$CC_HINT" ]; then + say "" + warn "no clang found. Ludic emits LLVM IR and needs clang to assemble and link it." + say " ${CC_HINT}" +fi +if [ "$OS" != darwin ]; then + say "" + say " ${DIM}note: windowing is macOS-only today. On ${OS}, programs build and run" + say " headless (ludic build --headless); a window will not open yet.${R}" +fi + +say "" +if [ "$MODIFY_PATH" -eq 1 ]; then + [ -n "${TOUCHED# }" ] && say " ${DIM}added ${INSTALL_DIR}/bin to your PATH in:${R}${TOUCHED# }" + [ -n "${ALREADY# }" ] && say " ${DIM}already on your PATH in:${R}${ALREADY# }" +fi +case ":${PATH}:" in + *":${INSTALL_DIR}/bin:"*) ;; + *) + say " ${DIM}this shell was started before that, so for right now:${R}" + say " export PATH=\"${INSTALL_DIR}/bin:\$PATH\"" + say " ${DIM}(new terminals pick it up on their own)${R}" + ;; +esac +say "" +say "next:" +say " ${B}ludic new mygame${R} create a project" +say " ${B}cd mygame && ludic run${R} build it and play" +say " ${B}ludic help${R} everything else" diff --git a/ivec2-add.html b/ivec2-add.html new file mode 100644 index 00000000..0bb497b1 --- /dev/null +++ b/ivec2-add.html @@ -0,0 +1,32 @@ + + + + + +IVec2.add — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.add
+
+ method +

IVec2.add

+
+ IVec2.add(a, b) -> IVec2 +

Adds two integer vectors component-wise. Adding a direction step to a position is how a token moves one cell on a grid.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let next = IVec2.add(pos, IVec2.make(1, 0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-along.html b/ivec2-along.html new file mode 100644 index 00000000..3098cd0a --- /dev/null +++ b/ivec2-along.html @@ -0,0 +1,29 @@ + + + + + +IVec2.along — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.along
+
+ method +

IVec2.along

+
+ IVec2.along(origin, degrees, distance) -> IVec2 +

The point distance pixels from origin along degrees — the tip of an aim line, the centre of a melee arc.

+ +

Example

# doc-check: skip — illustrative
+let tip = IVec2.along(hero, aim, 18)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-distance2.html b/ivec2-distance2.html new file mode 100644 index 00000000..9939dc1e --- /dev/null +++ b/ivec2-distance2.html @@ -0,0 +1,29 @@ + + + + + +IVec2.distance2 — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.distance2
+
+ method +

IVec2.distance2

+
+ IVec2.distance2(a, b) -> int +

dx*dx + dy*dy — compare it against a squared radius to avoid a square root.

+ +

Example

# doc-check: skip — illustrative
+if IVec2.distance2(hero, foe) < 24 * 24 { … }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-dot.html b/ivec2-dot.html new file mode 100644 index 00000000..d362c88d --- /dev/null +++ b/ivec2-dot.html @@ -0,0 +1,32 @@ + + + + + +IVec2.dot — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.dot
+
+ method +

IVec2.dot

+
+ IVec2.dot(a, b) -> int +

Returns the integer dot product ax*bx + ay*by. Its sign tells you whether two directions point roughly the same way.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let facing = IVec2.dot(heading, toTarget)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-equal.html b/ivec2-equal.html new file mode 100644 index 00000000..6b19d8d9 --- /dev/null +++ b/ivec2-equal.html @@ -0,0 +1,32 @@ + + + + + +IVec2.equal — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.equal
+
+ method +

IVec2.equal

+
+ IVec2.equal(a, b) -> bool +

Compares two integer vectors for exact equality — true only when both the x and y components match.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if IVec2.equal(pos, goal) { win() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-heading.html b/ivec2-heading.html new file mode 100644 index 00000000..16a907ac --- /dev/null +++ b/ivec2-heading.html @@ -0,0 +1,29 @@ + + + + + +IVec2.heading — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.heading
+
+ method +

IVec2.heading

+
+ IVec2.heading(from, to) -> int +

The angle from from to to in whole degrees: 0 is +x, 90 is +y (screen y grows downward). Pair it with Angle.diff_degrees for an arc test.

+ +

Example

# doc-check: skip — illustrative
+let to_foe = IVec2.heading(hero, foe)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-make.html b/ivec2-make.html new file mode 100644 index 00000000..85d4f0cc --- /dev/null +++ b/ivec2-make.html @@ -0,0 +1,32 @@ + + + + + +IVec2.make — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.make
+
+ method +

IVec2.make

+
+ IVec2.make(x, y) -> IVec2 +

Builds an IVec2 from its integer x and y components. This is the usual way to name a tile or grid cell; read the parts back with IVec2.x and IVec2.y.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let cell = IVec2.make(4, 7)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-manhattan.html b/ivec2-manhattan.html new file mode 100644 index 00000000..aeb7de2b --- /dev/null +++ b/ivec2-manhattan.html @@ -0,0 +1,32 @@ + + + + + +IVec2.manhattan — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.manhattan
+
+ method +

IVec2.manhattan

+
+ IVec2.manhattan(a, b) -> int +

Returns the Manhattan (taxicab) distance |dx| + |dy| between two cells — the number of orthogonal steps between them, the natural distance metric on a 4-connected grid.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let steps = IVec2.manhattan(pos, goal)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-scale.html b/ivec2-scale.html new file mode 100644 index 00000000..7ad7fad7 --- /dev/null +++ b/ivec2-scale.html @@ -0,0 +1,32 @@ + + + + + +IVec2.scale — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.scale
+
+ method +

IVec2.scale

+
+ IVec2.scale(v, s) -> IVec2 +

Multiplies both components by an integer scalar — useful to convert a cell coordinate into a pixel offset by the tile size.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let px = IVec2.scale(cell, 16)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-step.html b/ivec2-step.html new file mode 100644 index 00000000..f1ef05b2 --- /dev/null +++ b/ivec2-step.html @@ -0,0 +1,29 @@ + + + + + +IVec2.step — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.step
+
+ method +

IVec2.step

+
+ IVec2.step(degrees) -> IVec2 +

The heading snapped to the eight compass steps: each component is -1, 0 or 1. A roll or knockback direction from an aim angle.

+ +

Example

# doc-check: skip — illustrative
+let direction = IVec2.step(TopDown.of(player).aim_angle)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-sub.html b/ivec2-sub.html new file mode 100644 index 00000000..8a2f7d12 --- /dev/null +++ b/ivec2-sub.html @@ -0,0 +1,32 @@ + + + + + +IVec2.sub — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.sub
+
+ method +

IVec2.sub

+
+ IVec2.sub(a, b) -> IVec2 +

Subtracts b from a component-wise, giving the integer offset from one cell to another.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let delta = IVec2.sub(target, pos)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-to_vector.html b/ivec2-to_vector.html new file mode 100644 index 00000000..67ec676d --- /dev/null +++ b/ivec2-to_vector.html @@ -0,0 +1,32 @@ + + + + + +IVec2.to_vector — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.to_vector
+
+ method +

IVec2.to_vector

+
+ IVec2.to_vector(v) -> Vector +

Widens an integer vector into a fixed-point Vector, so a grid coordinate can flow into the sub-pixel Vector.* math (interpolation, rotation, length).

+ +

Example

program Demo {
+  handler Step phase Update {
+    let world = IVec2.to_vector(cell)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-within.html b/ivec2-within.html new file mode 100644 index 00000000..5fe96d87 --- /dev/null +++ b/ivec2-within.html @@ -0,0 +1,29 @@ + + + + + +IVec2.within — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.within
+
+ method +

IVec2.within

+
+ IVec2.within(a, b, radius) -> bool +

True when the distance between a and b is at most radius (compared squared, no square root).

+ +

Example

# doc-check: skip — illustrative
+if IVec2.within(hero, heart, 14) { pick_up() }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-x.html b/ivec2-x.html new file mode 100644 index 00000000..58927a77 --- /dev/null +++ b/ivec2-x.html @@ -0,0 +1,32 @@ + + + + + +IVec2.x — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.x
+
+ method +

IVec2.x

+
+ IVec2.x(v) -> int +

Reads the x (column) component of an IVec2 as a plain int.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let col = IVec2.x(IVec2.make(4, 7))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-y.html b/ivec2-y.html new file mode 100644 index 00000000..b47fc36c --- /dev/null +++ b/ivec2-y.html @@ -0,0 +1,32 @@ + + + + + +IVec2.y — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.y
+
+ method +

IVec2.y

+
+ IVec2.y(v) -> int +

Reads the y (row) component of an IVec2 as a plain int.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let row = IVec2.y(IVec2.make(4, 7))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ivec2-zero.html b/ivec2-zero.html new file mode 100644 index 00000000..0ba15394 --- /dev/null +++ b/ivec2-zero.html @@ -0,0 +1,32 @@ + + + + + +IVec2.zero — Ludic + + + + + + +
+
API Reference › IVec2 › IVec2.zero
+
+ method +

IVec2.zero

+
+ IVec2.zero() -> IVec2 +

Returns the origin (0, 0) — a handy neutral value to start an accumulation or mark an unset cell.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let origin = IVec2.zero()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-cancel.html b/job-cancel.html new file mode 100644 index 00000000..5650c19c --- /dev/null +++ b/job-cancel.html @@ -0,0 +1,29 @@ + + + + + +Job.cancel — Ludic + + + + + + +
+
API Reference › Job › Job.cancel
+
+ method +

Job.cancel

+
+ Job.cancel(handle) -> void +

Cancel a job before it finishes.

+ +

Example

let job = Job.defer()
+Job.cancel(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-cancelled.html b/job-cancelled.html new file mode 100644 index 00000000..645c8621 --- /dev/null +++ b/job-cancelled.html @@ -0,0 +1,30 @@ + + + + + +Job.cancelled — Ludic + + + + + + +
+
API Reference › Job › Job.cancelled
+
+ method +

Job.cancelled

+
+ Job.cancelled(handle) -> bool +

Was the job cancelled?

+ +

Example

let job = Job.defer()
+Job.cancel(job)
+let stopped = Job.cancelled(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-defer.html b/job-defer.html new file mode 100644 index 00000000..ca22ce97 --- /dev/null +++ b/job-defer.html @@ -0,0 +1,29 @@ + + + + + +Job.defer — Ludic + + + + + + +
+
API Reference › Job › Job.defer
+
+ method +

Job.defer

+
+ Job.defer() -> Job +

A future you resolve yourself later.

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 42)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-done.html b/job-done.html new file mode 100644 index 00000000..8ac0ca8d --- /dev/null +++ b/job-done.html @@ -0,0 +1,30 @@ + + + + + +Job.done — Ludic + + + + + + +
+
API Reference › Job › Job.done
+
+ method +

Job.done

+
+ Job.done(handle) -> bool +

Has the job resolved (any outcome)?

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 1)
+let finished = Job.done(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-error.html b/job-error.html new file mode 100644 index 00000000..67b5e6aa --- /dev/null +++ b/job-error.html @@ -0,0 +1,30 @@ + + + + + +Job.error — Ludic + + + + + + +
+
API Reference › Job › Job.error
+
+ method +

Job.error

+
+ Job.error(handle) -> int +

The error code of a failed job.

+ +

Example

let job = Job.defer()
+Job.fail(job, 500)
+let code = Job.error(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-fail.html b/job-fail.html new file mode 100644 index 00000000..409df288 --- /dev/null +++ b/job-fail.html @@ -0,0 +1,29 @@ + + + + + +Job.fail — Ludic + + + + + + +
+
API Reference › Job › Job.fail
+
+ method +

Job.fail

+
+ Job.fail(handle, error) -> void +

Resolve a pending job as failed.

+ +

Example

let job = Job.defer()
+Job.fail(job, 404)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-failed.html b/job-failed.html new file mode 100644 index 00000000..3b629545 --- /dev/null +++ b/job-failed.html @@ -0,0 +1,30 @@ + + + + + +Job.failed — Ludic + + + + + + +
+
API Reference › Job › Job.failed
+
+ method +

Job.failed

+
+ Job.failed(handle) -> bool +

Did the job fail?

+ +

Example

let job = Job.defer()
+Job.fail(job, 9)
+let bad = Job.failed(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-free.html b/job-free.html new file mode 100644 index 00000000..fa295d7e --- /dev/null +++ b/job-free.html @@ -0,0 +1,30 @@ + + + + + +Job.free — Ludic + + + + + + +
+
API Reference › Job › Job.free
+
+ method +

Job.free

+
+ Job.free(handle) -> void +

Release a job slot back to the pool.

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 1)
+Job.free(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-fulfill.html b/job-fulfill.html new file mode 100644 index 00000000..812dffcf --- /dev/null +++ b/job-fulfill.html @@ -0,0 +1,29 @@ + + + + + +Job.fulfill — Ludic + + + + + + +
+
API Reference › Job › Job.fulfill
+
+ method +

Job.fulfill

+
+ Job.fulfill(handle, value) -> void +

Resolve a pending job with a value.

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 7)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-is_worker.html b/job-is_worker.html new file mode 100644 index 00000000..4cac2882 --- /dev/null +++ b/job-is_worker.html @@ -0,0 +1,29 @@ + + + + + +Job.is_worker — Ludic + + + + + + +
+
API Reference › Job › Job.is_worker
+
+ method +

Job.is_worker

+
+ Job.is_worker() -> bool +

True when the code is running on a Job.parallel_for pool thread, false on the main thread (and on +the calling thread while it takes its own share of a batch).

+ +

Example

if not Job.is_worker() { print("main thread") }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-ok.html b/job-ok.html new file mode 100644 index 00000000..36098eac --- /dev/null +++ b/job-ok.html @@ -0,0 +1,30 @@ + + + + + +Job.ok — Ludic + + + + + + +
+
API Reference › Job › Job.ok
+
+ method +

Job.ok

+
+ Job.ok(handle) -> bool +

Did the job succeed?

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 1)
+let good = Job.ok(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-parallel_for.html b/job-parallel_for.html new file mode 100644 index 00000000..2cc561de --- /dev/null +++ b/job-parallel_for.html @@ -0,0 +1,42 @@ + + + + + +Job.parallel_for — Ludic + + + + + + +
+
API Reference › Job › Job.parallel_for
+
+ method +

Job.parallel_for

+
+ Job.parallel_for(count, work, ctx) -> void +

Run work(i, ctx) for every i in [0, count) on real OS threads, and return when every call has +finished. The indices are split into chunks shared by a worker pool (one thread per core but one, +started on first use) and the calling thread, so each index runs exactly once, in no particular order.

work is a top-level function taking (i: int, ctx), named with fn; ctx is whatever it computes +on (words, bytes or a pointer). A worker computes on what it was handed and writes only its own +index's results: it must not spawn, despawn, push onto a list another thread can see, or use +Http or Audio. spawn and despawn on a worker stop the program with a located message. Shared +counters go through Sync.add, shared totals behind a Sync.mutex.

+ +

Example

program Squares {
+  function square(i: int, out: words) -> void { out[i] = i * i }
+
+  entry {
+    let sq = words(20000)
+    Job.parallel_for(20000, fn square, sq)
+    print(sq[141])
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-pending.html b/job-pending.html new file mode 100644 index 00000000..390e6bde --- /dev/null +++ b/job-pending.html @@ -0,0 +1,28 @@ + + + + + +Job.pending — Ludic + + + + + + +
+
API Reference › Job › Job.pending
+
+ method +

Job.pending

+
+ Job.pending() -> int +

How many jobs are still unresolved.

+ +

Example

let left = Job.pending()   # a ready-made loading-screen counter
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-pump.html b/job-pump.html new file mode 100644 index 00000000..c14f666a --- /dev/null +++ b/job-pump.html @@ -0,0 +1,29 @@ + + + + + +Job.pump — Ludic + + + + + + +
+
API Reference › Job › Job.pump
+
+ method +

Job.pump

+
+ Job.pump(budget) -> int +

Advance background jobs; collect results.

+ +

Example

let job = Job.run(1, 1000)
+Job.pump(64)   # spend up to 64 steps this frame (0 = finish all)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-result.html b/job-result.html new file mode 100644 index 00000000..a3201a21 --- /dev/null +++ b/job-result.html @@ -0,0 +1,30 @@ + + + + + +Job.result — Ludic + + + + + + +
+
API Reference › Job › Job.result
+
+ method +

Job.result

+
+ Job.result(handle) -> int +

The success value of a done job.

+ +

Example

let job = Job.defer()
+Job.fulfill(job, 42)
+let value = Job.result(job)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/job-run.html b/job-run.html new file mode 100644 index 00000000..eb94f8bf --- /dev/null +++ b/job-run.html @@ -0,0 +1,29 @@ + + + + + +Job.run — Ludic + + + + + + +
+
API Reference › Job › Job.run
+
+ method +

Job.run

+
+ Job.run(kind, arg) -> Job +

Start a background compute job.

+ +

Example

let job = Job.run(1, 100)   # 1 = sum 1..arg, 2 = fib, 3 = count primes
+Job.pump(0)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/json-encode.html b/json-encode.html new file mode 100644 index 00000000..572628bb --- /dev/null +++ b/json-encode.html @@ -0,0 +1,28 @@ + + + + + +Json.encode — Ludic + + + + + + +
+
API Reference › Json › Json.encode
+
+ method +

Json.encode

+
+ Json.encode(value) -> string +

Walks a value tree and returns compact JSON text. int nodes become integers, fixed nodes an exact decimal, bool true/false, str a quoted (escaped) string, and lists/objects the usual […]/{…} — object members keep insertion order, so the output is stable and diffable. The inverse is Json.parse; together with Reflect.serialize this is a one-call save.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/json-parse.html b/json-parse.html new file mode 100644 index 00000000..33a00fb5 --- /dev/null +++ b/json-parse.html @@ -0,0 +1,28 @@ + + + + + +Json.parse — Ludic + + + + + + +
+
API Reference › Json › Json.parse
+
+ method +

Json.parse

+
+ Json.parse(text: string) -> value +

Parses JSON text into a value tree — the inverse of Json.encode. Numbers with a decimal point become fixed nodes (recovered bit-for-bit from a value written by Json.encode), whole numbers int nodes. It is a lean, best-effort reader for trusted saves, not a validating parser; feed a parsed object to Reflect.apply to load it into an entity.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-attach.html b/kw-attach.html new file mode 100644 index 00000000..07b8e6e0 --- /dev/null +++ b/kw-attach.html @@ -0,0 +1,44 @@ + + + + + +attach — Ludic + + + + + + +
+ +
+ keyword +

attach

+
+ attach Prop on entity { overrides } +

attach adds a property to a live model instance that did not have it, seeding the fields from their defaults plus any overrides in the { … } block, and fires the property's @OnAttach(Prop) hook. It is a structural change — it changes what the instance has — as opposed to enable, which merely resumes a property the instance already carries. Attaching a property the instance already has is a no-op, so the hook only runs on a real transition. Use it to grant something new at runtime: give a player a shield pickup, tag an enemy as enraged, or add a component only some instances need.

+ +

Example

program Pickups {
+  property Position { column: int = 0, row: int = 0 }
+  property Shield   { amount: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 6, row: 6 } }
+  }
+
+  handler GrabShield phase Update {
+    if Input.key() == ' ' {
+      for (position) in query [Position, {Player}] {
+        attach Shield on self() { amount: 3 }
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-become.html b/kw-become.html new file mode 100644 index 00000000..3aa5d522 --- /dev/null +++ b/kw-become.html @@ -0,0 +1,46 @@ + + + + + +become — Ludic + + + + + + +
+ +
+ keyword +

become

+
+ become Name +

A become statement performs a transition. Inside a machine, become Name moves to another state of that machine by storing the target state's value back into the machine's store, so the next dispatch runs the new state. Used from a scene's layer handler, become Scene instead switches the active scene: the current scene's on exit runs, the active-scene register is set, and the target scene's on enter runs — two direct calls and a store, with no dispatch table. become names its target and knows from context which kind of transition it is, so you rarely think about the machinery. Transitioning is cheap and takes effect immediately for machine states.

+ +

Example

program EncounterFlow {
+  enum Stage { Explore, Battle, Victory }
+  var stage: int = Stage.Explore
+  var enemies_left: int = 2
+
+  handler RunStage phase Update {
+    machine stage {
+      state Explore {
+        if Input.key() == ' ' { become Battle }
+      }
+      state Battle {
+        if enemies_left <= 0 { become Victory }
+      }
+      state Victory {
+        Screen.status("you win")
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-break.html b/kw-break.html new file mode 100644 index 00000000..f8c6ea9d --- /dev/null +++ b/kw-break.html @@ -0,0 +1,39 @@ + + + + + +break — Ludic + + + + + + +
+ +
+ keyword +

break

+
+ break +

break exits the innermost while or for loop at once, skipping the rest of the body and any remaining iterations, and continues with the code after the loop. Use it to stop early the moment you have what you need — the first match in a scan, a collision that ends the sweep, or a guard inside while true that decides when to leave. It affects only the loop that contains it; an outer loop keeps running.

+ +

Example

program FindFirst {
+  var found: int = 0 - 1
+
+  handler Scan phase Update {
+    for i in 0 .. 10 {
+      if i * i > 20 {
+        found = i
+        break        # stop at the first i whose square exceeds 20
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-cancel.html b/kw-cancel.html new file mode 100644 index 00000000..be2e9d39 --- /dev/null +++ b/kw-cancel.html @@ -0,0 +1,50 @@ + + + + + +cancel — Ludic + + + + + + +
+
API Reference › Events › cancel
+
+ keyword +

cancel

+
+ cancel +

cancel is used inside an @On listener to veto the cancellable event currently being emitted. Calling it sets the payload's cancelled flag, and once the emit finishes running its listeners that flag comes back to the caller — emit E(…) used as an expression yields 1 when any listener cancelled and 0 otherwise. Guard it behind whatever condition should block the action; listeners that do not cancel simply observe. cancel is only meaningful in a listener for an event declared cancellable.

+ +

Example

program CancelExample {
+  event cancellable BeforeOpenDoor { key_count: int = 0 }
+
+  var keys: int = 0
+  var doors_opened: int = 0
+
+  @On(BeforeOpenDoor) handler RequireKey { if key_count <= 0 { cancel } }
+
+  handler TryOpen phase Update {
+    if Input.key() == 'o' {
+      if emit BeforeOpenDoor(key_count: keys) == 0 {
+        doors_opened = doors_opened + 1
+        keys = keys - 1
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 4, y: 4, value: doors_opened, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-cancellable.html b/kw-cancellable.html new file mode 100644 index 00000000..c66229ce --- /dev/null +++ b/kw-cancellable.html @@ -0,0 +1,46 @@ + + + + + +cancellable — Ludic + + + + + + +
+
API Reference › Events › cancellable
+
+ keyword +

cancellable

+
+ event cancellable Name { … } +

cancellable marks an event as a decision, not just a notification. A plain event tells listeners something happened; a cancellable event is fired before an action so a listener can veto it by calling cancel. The caller reads the verdict back by using emit as an expression: it yields 0 when no listener vetoed and 1 when one did, so the action is applied only on 0. This is the Bukkit / DOM preventDefault shape — observation becomes control — and a foreign mod vetoes the same way by setting the payload's trailing cancelled flag over the ABI.

+ +

Example

program CancellableExample {
+  event cancellable BeforeHurt { amount: int = 0 }
+
+  var current_health: int = 100
+
+  @On(BeforeHurt) handler AbsorbSmallHits { if amount <= 5 { cancel } }
+
+  handler TakeDamage phase Update {
+    if Input.key() == 'h' {
+      if emit BeforeHurt(amount: 3) == 0 { current_health = current_health - 3 }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 4, y: 4, value: current_health, color: Color.Crimson, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-const.html b/kw-const.html new file mode 100644 index 00000000..76cc8a3f --- /dev/null +++ b/kw-const.html @@ -0,0 +1,47 @@ + + + + + +const — Ludic + + + + + + +
+ +
+ keyword +

const

+
+ const NAME: T = value +

A const binds a name to a value known at compile time. Unlike var, it has no storage and cannot be reassigned — the compiler folds it directly into wherever it is used, so it costs nothing at runtime. Reach for const for the fixed dimensions and magic numbers of your game — grid sizes, tile pixels, tuning values — so the code reads in names instead of literals. Give constants descriptive, uppercase names, and use them everywhere the value appears so a single edit changes the whole game.

+ +

Example

program Board {
+  const GRID_WIDTH: int = 20
+  const GRID_HEIGHT: int = 15
+  const TILE_SIZE: int = 16
+
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: GRID_WIDTH / 2, row: GRID_HEIGHT / 2 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position) in query [Position, {Player}] {
+      Screen.fill_rectangle(x: position.column * TILE_SIZE, y: position.row * TILE_SIZE, width: TILE_SIZE, height: TILE_SIZE, color: Color.Gold)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-continue.html b/kw-continue.html new file mode 100644 index 00000000..f9181202 --- /dev/null +++ b/kw-continue.html @@ -0,0 +1,37 @@ + + + + + +continue — Ludic + + + + + + +
+
API Reference › Control flow › continue
+
+ keyword +

continue

+
+ continue +

continue abandons the rest of the current loop pass and jumps straight to the next one — the next condition check in a while, or the next index in a for. Use it to skip elements that do not apply without nesting the rest of the body inside an if: filter out empty slots, ignore inactive entities, or step over even numbers. Like break, it acts on the innermost loop only.

+ +

Example

program SumOdds {
+  var total: int = 0
+
+  handler Add phase Update {
+    for i in 0 .. 10 {
+      if i % 2 == 0 { continue }   # skip even numbers
+      total = total + i
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-despawn.html b/kw-despawn.html new file mode 100644 index 00000000..400e970b --- /dev/null +++ b/kw-despawn.html @@ -0,0 +1,42 @@ + + + + + +despawn — Ludic + + + + + + +
+ +
+ keyword +

despawn

+
+ despawn entity +

A despawn removes a model instance from the world, freeing its slot and firing any @OnDespawn lifecycle hooks for its kind. The most common form is despawn self() inside a query loop, which removes the instance currently being visited. Because queries iterate lazily by ascending id rather than from a frozen snapshot, despawning the current instance — or one already visited — is safe and will not disturb the walk. Despawning is how the moving parts of a game leave: an enemy that reached zero health, a projectile that flew off screen, or every instance of a kind during a reset.

+ +

Example

program Cleanup {
+  property Position { column: int = 0, row: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+  model Enemy { Position, Health }
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 3, row: 3 }; Health { current: 0 } }
+  }
+
+  handler RemoveDefeated phase LateUpdate {
+    for (health) in query [Health, {Enemy}] {
+      if health.current <= 0 { despawn self() }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-detach.html b/kw-detach.html new file mode 100644 index 00000000..8bbf583f --- /dev/null +++ b/kw-detach.html @@ -0,0 +1,42 @@ + + + + + +detach — Ludic + + + + + + +
+ +
+ keyword +

detach

+
+ detach Prop on entity +

detach removes a property from a live model instance, firing the property's @OnDetach(Prop) hook before the has-flag clears — so the hook can still read the outgoing field values as it tears down. It is the structural opposite of attach: where disable pauses a property but keeps its data for a later enable, detach genuinely removes the property, and a later attach re-seeds fresh fields from defaults. Detaching a property the instance does not have is a no-op. Reach for it when something is gone for good — a shield consumed, a status effect that has run its course, an ability removed.

+ +

Example

program ShieldBreak {
+  property Position { column: int = 0, row: int = 0 }
+  property Shield   { amount: int = 0 }
+  model Player { Position, Shield }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 6, row: 6 }; Shield { amount: 1 } }
+  }
+
+  handler ConsumeShield phase Update {
+    for (shield) in query [Shield, {Player}] {
+      if shield.amount <= 0 { detach Shield on self() }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-disable.html b/kw-disable.html new file mode 100644 index 00000000..8195c9ea --- /dev/null +++ b/kw-disable.html @@ -0,0 +1,42 @@ + + + + + +disable — Ludic + + + + + + +
+ +
+ keyword +

disable

+
+ disable Prop on entity +

disable switches something off while keeping it intact, so a later enable brings it back untouched — a reversible pause rather than a destruction. disable Prop on instance clears that instance's has-flag for the property, so queries stop matching it, but the field values stay in storage. It has three scopes: one property on one instance (disable Shield on self()), a whole model (disable Enemy drops all its instances out of every query), and a handler (disable AiThink stops it running each phase until re-enabled). Each toggle is a single flag flip, so nothing is copied or freed. Disabling a property fires any @OnDisable(Prop) hook. Contrast with detach, which structurally removes a property (a later attach re-seeds fresh fields).

+ +

Example

program Stealth {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Enemy { Position, Velocity }
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 4, row: 4 }; Velocity { delta_x: 1 } }
+  }
+
+  handler FreezeEnemies phase Update {
+    if Input.key() == 'p' {
+      for (velocity) in query [Velocity, {Enemy}] { disable Velocity on self() }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-emit.html b/kw-emit.html new file mode 100644 index 00000000..234a07ca --- /dev/null +++ b/kw-emit.html @@ -0,0 +1,46 @@ + + + + + +emit — Ludic + + + + + + +
+
API Reference › Events › emit
+
+ keyword +

emit

+
+ emit Name(field: value) +

emit Name(field: value, …) fires an event: it runs every @On(Name) listener in declaration order, passing the named payload fields (any you omit take the event's declared defaults). It desugars to a direct call into the event's generated function, so firing an event is as cheap as calling a function. For a cancellable event, emit can be used as an expression that yields the cancelled flag — 0 if no listener vetoed, 1 if one did — which is the "check the decision before acting" shape: apply the effect only when the emit returns 0.

+ +

Example

program EmitExample {
+  event cancellable BeforeSpend { amount: int = 0 }
+
+  var coins: int = 50
+
+  @On(BeforeSpend) handler RejectOverdraft { if amount > coins { cancel } }
+
+  handler TrySpend phase Update {
+    if Input.key() == 'b' {
+      if emit BeforeSpend(amount: 20) == 0 { coins = coins - 20 }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 4, y: 4, value: coins, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-enable.html b/kw-enable.html new file mode 100644 index 00000000..cdfe5951 --- /dev/null +++ b/kw-enable.html @@ -0,0 +1,43 @@ + + + + + +enable — Ludic + + + + + + +
+ +
+ keyword +

enable

+
+ enable Prop on entity +

enable reverses a disable: it turns something back on without recreating it. enable Prop on instance restores a property's has-flag so queries match the instance again, with the field values exactly as they were left — nothing was copied or freed, so re-enabling is cheap and lossless. There are three scopes, matching disable: a single property on one instance (enable Shield on self()), a whole model (enable Enemy, all its instances rejoin every query), and a handler (enable AiThink, it resumes running each phase). Re-enabling a property fires any @OnEnable(Prop) hook at the toggle point. Reach for enable/disable when you want to pause and resume, and for attach/detach when you want to add or structurally remove.

+ +

Example

program ShieldToggle {
+  property Position { column: int = 0, row: int = 0 }
+  property Shield   { amount: int = 0 }
+  model Player { Position, Shield }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 8, row: 8 }; Shield { amount: 3 } }
+    disable Shield on self()
+  }
+
+  handler RestoreShield phase Update {
+    if Input.key() == ' ' {
+      for (position) in query [Position, {Player}] { enable Shield on self() }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-enter.html b/kw-enter.html new file mode 100644 index 00000000..048f081b --- /dev/null +++ b/kw-enter.html @@ -0,0 +1,49 @@ + + + + + +enter — Ludic + + + + + + +
+ +
+ keyword +

enter

+
+ on enter { … } +

enter names the body of a scene's entry hook, written on enter { … }. It runs once each time the scene becomes active — at boot for the start scene (right after the Start phase), and on every become Name that switches into it. This is where a scene builds what it owns: spawn its models, reset its counters, set the tilemap, open a menu. It is a lifecycle hook, not a phase, so it may not contain phase handlers; the scene's ongoing behaviour goes in its layer handlers instead.

+ +

Example

program EnterHookExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  var score: int = 0
+
+  scene Overworld start {
+    on enter {
+      score = 0
+      spawn Player { Position { column: 5, row: 5 } }
+    }
+    layer World {
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        for (current_position) in query [Position, {Player}] {
+          Screen.fill_rectangle(x: current_position.column * 16, y: current_position.row * 16, width: 16, height: 16, color: Color.LimeGreen)
+        }
+        Screen.show()
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-entry.html b/kw-entry.html new file mode 100644 index 00000000..87d6939c --- /dev/null +++ b/kw-entry.html @@ -0,0 +1,33 @@ + + + + + +entry — Ludic + + + + + + +
+ +
+ keyword +

entry

+
+ entry { … } +

entry declares the program's entry point: a single block that runs once when the program starts, in order, and then the program exits. It is the counterpart to the ECS game loop — where a game is built from handlers driven by phases each frame, an entry program is a straight-line script, which is why the compiler and tools in this repo are written with it. Use it for command-line programs and one-shot tools; use program with handlers for interactive games.

+ +

Example

program Greet {
+  entry {
+    print("hello")
+    print(2 + 2)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-enum.html b/kw-enum.html new file mode 100644 index 00000000..66161fbc --- /dev/null +++ b/kw-enum.html @@ -0,0 +1,56 @@ + + + + + +enum — Ludic + + + + + + +
+ +
+ keyword +

enum

+
+ enum Name { A, B, C } +

An enum gives names to a set of related integer values so a magic-number space — a menu selection, a game mode, a machine state — reads as names instead of bare literals. Variants number themselves from 0 in declaration order, and you access one as Name.Variant, which is a compile-time int usable anywhere an int is: in match patterns, comparisons, and assignments. When every variant is a bare name, an enum value lives in an ordinary int or var and is saved with it, so a plain enum is best understood as a zero-cost naming layer over int.

## Tagged unions — variants with payloads

A variant may also carry a **payload**, written as a parenthesized list of types after its name. Doing so turns the whole enum into a *tagged union*: a value is now one of several shapes, each with its own data, and the type remembers which.

You **construct** a variant by name — Door(3), Portal(x, y), or bare Empty for a payload-less one — and store or pass it as a value of the enum's type (let t: Tile = Door(3)). Each value is a small boxed record: a tag naming the variant, plus its payload.

You take one apart with match, binding the payload names in each arm:

A tagged match must be **exhaustive**: it either handles every variant or ends with a _ catch-all. Leaving a variant out is a compile-time error (match on Tile is not exhaustive: variant Door is unhandled), so adding a new variant surfaces every site that must learn about it. Constructor and pattern arities are checked the same way. A plain (all-bare) enum keeps its compile-time-ordinal Name.Variant form and is unaffected.

+ +

Example

enum Tile { Empty, Wall, Door(int), Portal(int, int) }
function walk_cost(t: Tile) -> int {
+  match t {
+    Empty        => { return 1 }
+    Wall         => { return 0 }
+    Door(n)      => { return 10 + n }   # n is the door's int payload
+    Portal(x, y) => { return x + y }    # both fields bound in this arm
+  }
+}
program BattleMenu {
+  enum Action { Attack, Guard, Item, Flee }
+
+  var current_action: int = Action.Attack
+
+  handler ChooseAction phase Input {
+    let pressed = Input.key()
+    if pressed == 'a' { current_action = Action.Attack }
+    if pressed == 'g' { current_action = Action.Guard }
+    if pressed == 'f' { current_action = Action.Flee }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    match current_action {
+      Action.Attack => Screen.draw_text(x: 8, y: 8, text: "ATTACK", color: Color.Crimson, scale: 2)
+      Action.Guard  => Screen.draw_text(x: 8, y: 8, text: "GUARD", color: Color.White, scale: 2)
+      _             => Screen.draw_text(x: 8, y: 8, text: "FLEE", color: Color.Gold, scale: 2)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-event.html b/kw-event.html new file mode 100644 index 00000000..5542a52e --- /dev/null +++ b/kw-event.html @@ -0,0 +1,45 @@ + + + + + +event — Ludic + + + + + + +
+
API Reference › Events › event
+
+ keyword +

event

+
+ event Name { field: T = default } +

event declares a public event — a named signal carrying a flat payload of typed fields (each with a default; the payload may be empty). Where lifecycle hooks are the closed reactions the author compiles in, events are the open runtime surface a game exposes to mods: an in-language listener registers with @On(Name) and reads the payload fields by name, and emit Name(field: value, …) fires every listener in declaration order as a direct call. It all desugars to a generated function — there is no interpreter and no dispatch table — and a program that declares no event compiles byte-for-byte as before. Mark an event cancellable when a listener should be able to veto the action it announces.

+ +

Example

program EventExample {
+  event EnemyDefeated { points: int = 0 }
+
+  var score: int = 0
+
+  @On(EnemyDefeated) handler AddScore { score = score + points }
+  @On(EnemyDefeated) handler PlayChime { Screen.status("enemy down") }
+
+  handler ScoreOnKey phase Update {
+    if Input.key() == 'k' { emit EnemyDefeated(points: 10) }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 4, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-exit.html b/kw-exit.html new file mode 100644 index 00000000..cc774c41 --- /dev/null +++ b/kw-exit.html @@ -0,0 +1,56 @@ + + + + + +exit — Ludic + + + + + + +
+ +
+ keyword +

exit

+
+ on exit { … } +

exit names the body of a scene's exit hook, written on exit { … }. It runs once when the scene is left — on a become Other, the leaving scene's on exit runs first, then the active scene switches and the new scene's on enter runs. Use it to tear down what the scene created so it does not leak into the next state: despawn the scene's models, hide a menu, save progress. Like on enter, it is a lifecycle hook rather than a phase and holds plain statements, not phase handlers.

+ +

Example

program ExitHookExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  scene Battle start {
+    on enter { spawn Enemy { Position { column: 8, row: 4 } } }
+    on exit {
+      for (any_position) in query [Position, {Enemy}] { despawn self() }
+    }
+    layer Fight {
+      handler LeaveBattle phase Update {
+        if Input.key() == 'q' { become Overworld }
+      }
+      handler DrawBattle phase Render {
+        Screen.clear(Color.Crimson)
+        Screen.show()
+      }
+    }
+  }
+
+  scene Overworld {
+    layer World {
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.show()
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-extern.html b/kw-extern.html new file mode 100644 index 00000000..3f16d45a --- /dev/null +++ b/kw-extern.html @@ -0,0 +1,40 @@ + + + + + +extern — Ludic + + + + + + +
+ +
+ keyword +

extern

+
+ extern function name(a: T) -> R = "symbol" +

An extern function declares a function whose body lives outside Ludic and binds it to a native symbol resolved at link time. It is the seam through which Ludic reaches anything with a native interface — a system library, a math routine, or even another .ludic file compiled as a module. You write the Ludic signature you want to call and give the real symbol name after =; the linker connects them (pass -L/-l to ludicc to point at the library). Types must match the foreign ABI, so map each parameter and the return to the right Ludic type (int, fixed, pointer, …).

+

Parameters

aa typed argument passed straight through to the foreign symbol
+

Example

program Physics {
+  extern function c_hypot(a: fixed, b: fixed) -> fixed = "hypot_fx"
+
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Projectile { Velocity }
+
+  handler MeasureSpeed phase Update {
+    for (velocity) in query [Velocity, {Projectile}] {
+      let speed = c_hypot(a: fixed(velocity.delta_x), b: fixed(velocity.delta_y))
+      Screen.status(string(floor(speed)))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-fn.html b/kw-fn.html new file mode 100644 index 00000000..242470ef --- /dev/null +++ b/kw-fn.html @@ -0,0 +1,36 @@ + + + + + +fn — Ludic + + + + + + +
+ +
+ keyword +

fn

+
+ fn name +

fn name names a top-level function as a value, so it can be handed to something that calls it later - today, Job.parallel_for, which runs it on worker threads. It is a plain function reference: no closure and nothing captured, so everything the function needs is passed to it (for Job.parallel_for, through its ctx argument). A worker function takes (i: int, ctx) and returns void; the compiler rejects any other shape.

+ +

Example

program Squares {
+  function square(i: int, out: words) -> void { out[i] = i * i }
+
+  entry {
+    let sq = words(1000)
+    Job.parallel_for(1000, fn square, sq)
+    print(sq[12])
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-for.html b/kw-for.html new file mode 100644 index 00000000..74e45e75 --- /dev/null +++ b/kw-for.html @@ -0,0 +1,44 @@ + + + + + +for … in — Ludic + + + + + + +
+
API Reference › Control flow › for … in
+
+ keyword +

for … in

+
+ for name in a .. b { … } +

The numeric for … in loop walks the half-open range a .. b, running the body for each value from a up to but not including b. Each pass binds a fresh, immutable name — it is not a reusable variable and cannot be reassigned inside the body — which makes the loop easy to reason about. Both bounds are ordinary expressions, so ranges built from constants like 0 .. GRID_WIDTH read cleanly. The same for … in keyword also drives ECS query loops (for (…) in query […]); this page is the numeric range form. Use break and continue to exit early or skip to the next value.

+ +

Example

program Grid {
+  const GRID_WIDTH: int = 20
+  const GRID_HEIGHT: int = 15
+  const TILE_SIZE: int = 16
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for row in 0 .. GRID_HEIGHT {
+      for column in 0 .. GRID_WIDTH {
+        var tile_color = Color.MidnightBlue
+        if (column + row) % 2 == 0 { tile_color = Color.White }
+        Screen.fill_rectangle(x: column * TILE_SIZE, y: row * TILE_SIZE, width: TILE_SIZE, height: TILE_SIZE, color: tile_color)
+      }
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-function.html b/kw-function.html new file mode 100644 index 00000000..6e562a47 --- /dev/null +++ b/kw-function.html @@ -0,0 +1,47 @@ + + + + + +function — Ludic + + + + + + +
+ +
+ keyword +

function

+
+ function name(a: T, b: T) -> R { … } +

A function declares a function: a reusable block of logic with typed parameters and a return type, written -> R (use -> void for one that returns nothing). Call it positionally, heal(2, 10), or with named arguments, heal(amount: 2, maximum: 10), which reads more clearly at the call site and is the house style. Functions live at program scope alongside handlers and may be called from any handler; they can spawn, run queries, and read program-scope state. Use them to factor out logic shared by several handlers so each handler stays short.

+ +

Example

program Healer {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Player { Health }
+
+  function heal(amount: int, maximum: int) -> int {
+    let restored = amount * 2
+    if restored > maximum { return maximum }
+    return restored
+  }
+
+  handler Boot phase Start {
+    spawn Hero { Health { current: 10, maximum: 100 } }
+  }
+
+  handler Recover phase Update {
+    for (health) in query [Health, {Player}] {
+      health.current = heal(amount: health.current, maximum: health.maximum)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-handler.html b/kw-handler.html new file mode 100644 index 00000000..3b272ba7 --- /dev/null +++ b/kw-handler.html @@ -0,0 +1,50 @@ + + + + + +handler — Ludic + + + + + + +
+ +
+ keyword +

handler

+
+ handler Name phase P { … } +

A handler is a named block of behavior that the engine runs automatically during a given phase — the unit that turns your data into a game. A handler with no query runs once per phase tick; a handler with a @Queries annotation (or an inline for (…) in query […] loop) runs its body once per matching model instance, with each property bound by name and self() giving the current instance. Handlers are registered implicitly just by being declared, and you can pause one at runtime with disable Handler and bring it back with enable Handler. Keep behavior in handlers and keep data plain in properties — that separation is the whole point.

+ +

Example

program Runner {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Player { Position, Velocity }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 0, row: 6 }; Velocity { delta_x: 1 } }
+  }
+
+  handler AdvancePositions phase Update {
+    for (position, velocity) in query [Position, Velocity, {Player}] {
+      position.column = position.column + velocity.delta_x
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position) in query [Position, {Player}] {
+      Screen.fill_rectangle(x: position.column * 16, y: position.row * 16, width: 16, height: 16, color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-if.html b/kw-if.html new file mode 100644 index 00000000..59d21493 --- /dev/null +++ b/kw-if.html @@ -0,0 +1,51 @@ + + + + + +if / else — Ludic + + + + + + +
+
API Reference › Control flow › if / else
+
+ keyword +

if / else

+
+ if cond { … } else { … } +

An if runs its block when the condition is true; an optional else block runs when it is false, and the else may itself be another if to form a ladder. The condition is a plain boolean expression with no surrounding parentheses, and the braces are always required even for a single statement. Ludic's boolean operators are the words and, or and not (not &&/||/!), and note that bitwise operators bind tighter than comparison, so flags & MASK == 0 already means (flags & MASK) == 0. When the branches grow into a chain testing one value against several constants, reach for match instead.

+ +

Example

program Threshold {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Player { Health }
+
+  handler Boot phase Start {
+    spawn Hero { Health { current: 25 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (health) in query [Health, {Player}] {
+      if health.current <= 0 {
+        Screen.draw_text(x: 8, y: 8, text: "DEFEATED", color: Color.Crimson, scale: 2)
+      } else {
+        if health.current < 30 {
+          Screen.draw_text(x: 8, y: 8, text: "DANGER", color: Color.Gold, scale: 2)
+        } else {
+          Screen.draw_text(x: 8, y: 8, text: "OK", color: Color.LimeGreen, scale: 2)
+        }
+      }
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-import.html b/kw-import.html new file mode 100644 index 00000000..77473739 --- /dev/null +++ b/kw-import.html @@ -0,0 +1,37 @@ + + + + + +import — Ludic + + + + + + +
+ +
+ keyword +

import

+
+ import "file.ludic" +

An import pulls the declarations of another Ludic file into this program, letting you split a game across many files instead of one giant block. The imported file is a fragment — bare declarations with no program wrapper — and its contents are spliced in as if written here. Paths resolve relative to the importing file, imports may nest, and each resolved path is include-guarded, so importing the same file twice (even through different chains) pulls it in exactly once. Diagnostics still point at the real source file, so errors in an imported fragment report that file's name and line.

A path without .ludic names a directory through its barrel, index.ludic: import "camp" reads camp/index.ludic, which lists the directory's modules with imports relative to itself. import "dir/*.ludic" instead takes every file in the directory, in name order.

+ +

Example

# doc-check: skip — illustrative; the imported paths are example files, not
+# resolvable when this snippet is checked in isolation.
+program ChronoRift {
+  import "chronorift/world.ludic"     # properties and models
+  import "chronorift/combat.ludic"    # the battle handlers
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 4, row: 4 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-in.html b/kw-in.html new file mode 100644 index 00000000..fb147b5e --- /dev/null +++ b/kw-in.html @@ -0,0 +1,44 @@ + + + + + +in — Ludic + + + + + + +
+ +
+ keyword +

in

+
+ for x in range | query +

The in keyword is the bridge in a for loop between the loop's bindings and the source it walks. On the right of in you write either a numeric range, a .. b, binding one fresh index each pass, or an ECS query [...], binding one variable per non-tag property for each matching model instance. It always pairs with for and never stands alone. In the query form the parentheses group the bound properties — for (position, velocity) in query [Position, Velocity] — and the body then runs once per instance that carries them all.

+ +

Example

program Movement {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Enemy { Position, Velocity }
+
+  handler Boot phase Start {
+    for slot in 0 .. 3 {
+      spawn Grunt { Position { column: slot * 4, row: 2 }; Velocity { delta_x: 1 } }
+    }
+  }
+
+  handler AdvancePositions phase Update {
+    for (position, velocity) in query [Position, Velocity, {Enemy}] {
+      position.column = position.column + velocity.delta_x
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-layer.html b/kw-layer.html new file mode 100644 index 00000000..3ef81f21 --- /dev/null +++ b/kw-layer.html @@ -0,0 +1,54 @@ + + + + + +layer — Ludic + + + + + + +
+ +
+ keyword +

layer

+
+ layer Name { handlers } +

A layer groups handlers inside a scene, and declaration order is draw order: within a phase the active scene's layers run in the order they were written, so a Hud layer declared after a World layer paints its Render output on top. Layers are the natural unit for stacking a heads-up display over the play-field, or a menu over a paused world. A layer handler may use any phase except Start (boot-time setup belongs in the scene's on enter). Handlers that should run in every scene are declared outside any scene rather than in a layer.

+ +

Example

program LayerExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  var score: int = 0
+
+  scene Overworld start {
+    on enter { spawn Player { Position { column: 6, row: 6 } } }
+
+    layer World {
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        for (current_position) in query [Position, {Player}] {
+          Screen.fill_rectangle(x: current_position.column * 16, y: current_position.row * 16, width: 16, height: 16, color: Color.LimeGreen)
+        }
+      }
+    }
+
+    layer Hud {
+      handler DrawScore phase Render {
+        Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
+        Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
+        Screen.show()                     # the last layer to draw presents the frame
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-let.html b/kw-let.html new file mode 100644 index 00000000..1c6c1328 --- /dev/null +++ b/kw-let.html @@ -0,0 +1,43 @@ + + + + + +let — Ludic + + + + + + +
+ +
+ keyword +

let

+
+ let name = value +

A let introduces an immutable binding: once set, reassigning it (name = …) is a compile error. Reach for let by default and only switch to var when a value genuinely needs to change — it makes intent obvious and catches accidental writes. Immutability is of the binding, not the object it points at: a let that holds a property record or a slice still lets you mutate through it (node.current = 5), it just cannot be repointed at a different object. Bindings inside a body are locals scoped to their block.

+ +

Example

program Damage {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Enemy { Health }
+
+  handler Boot phase Start {
+    spawn Grunt { Health { current: 40 } }
+  }
+
+  handler ApplyHit phase Update {
+    let incoming_damage = 12
+    for (health) in query [Health, {Enemy}] {
+      let survivor = health.current - incoming_damage
+      health.current = survivor
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-machine.html b/kw-machine.html new file mode 100644 index 00000000..20a35fca --- /dev/null +++ b/kw-machine.html @@ -0,0 +1,51 @@ + + + + + +machine — Ludic + + + + + + +
+
API Reference › Control flow › machine
+
+ keyword +

machine

+
+ machine store { state Name { … } } +

A machine turns an integer store into an explicit state machine, replacing brittle if phase == N chains. It reads the store — a named program-scope var is the modern choice — and dispatches to the matching state block; inside a state, become Name transitions to another state of the same machine. States number themselves by declaration order (the first is 0, the next 1, and so on), so you never write magic constants, though state Name = expr is accepted when a state needs a specific value. Because become compiles to a single store back into the var, the whole machine lowers to plain branches with no dispatch table. Place a machine inside a handler so it runs each frame.

+ +

Example

program TurnOrder {
+  enum Phase { KnightMenu, KnightResolve, EnemyTurn }
+  var battle_phase: int = Phase.KnightMenu
+
+  handler RunTurn phase Update {
+    machine battle_phase {
+      state KnightMenu {
+        if Input.key() == ' ' { become KnightResolve }
+      }
+      state KnightResolve {
+        become EnemyTurn
+      }
+      state EnemyTurn {
+        become KnightMenu
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 8, y: 8, value: battle_phase, color: Color.Gold, scale: 2)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-match.html b/kw-match.html new file mode 100644 index 00000000..fc96990a --- /dev/null +++ b/kw-match.html @@ -0,0 +1,48 @@ + + + + + +match — Ludic + + + + + + +
+ +
+ keyword +

match

+
+ match value { 0, 1 => … _ => … } +

A match replaces an if/else ladder that tests one value against several constants. It evaluates the subject once, then takes the first arm whose pattern matches; an arm lists one or more literal patterns separated by commas and points at a body with =>, and a lone _ arm is the catch-all default. Patterns are compile-time constants — integers, char literals like 'w', or enum variants such as Action.Guard — which makes match ideal for dispatching on a key press, a tile code, or a mode. Each arm's body is a single statement or a { … } block; matching lowers to plain branches, so it is as cheap as the if chain it replaces.

When the subject is a **tagged-union enum** (an enum whose variants carry payloads), match also *destructures* it: an arm names a variant and binds its payload — Door(n) => …, Portal(x, y) => … — with n, x, y in scope for that arm's body. A tagged match must be **exhaustive**: it covers every variant or ends with a _, or the compiler rejects it, so a newly added variant flags every match that must handle it. See [enum](../structure/kw-enum) for the full picture.

+ +

Example

program Steering {
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Player { Velocity }
+
+  handler Boot phase Start {
+    spawn Hero { Velocity { delta_x: 0, delta_y: 0 } }
+  }
+
+  handler ReadKeys phase Input {
+    let pressed = Input.key()
+    for (velocity) in query [Velocity, {Player}] {
+      match pressed {
+        'w' => velocity.delta_y = -1
+        's' => velocity.delta_y = 1
+        'a', 'h' => velocity.delta_x = -1
+        'd', 'l' => velocity.delta_x = 1
+        _ => velocity.delta_x = 0
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-model.html b/kw-model.html new file mode 100644 index 00000000..88a94de0 --- /dev/null +++ b/kw-model.html @@ -0,0 +1,46 @@ + + + + + +model — Ludic + + + + + + +
+ +
+ keyword +

model

+
+ model Name { PropA, PropB, … } +

A model names a kind of thing in your game and the fixed set of properties every instance of it carries. Instead of attaching properties one by one, you spawn the model by name and every listed property comes with it, seeded from its defaults. A model's name doubles as a query tag: write {Player} inside a query [...] to match only instances of that model. The model itself has no fields of its own, so you never bind it to a variable — you bind its properties and filter by its tag.

+ +

Example

program Arena {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+
+  model Player { Position, Health }
+  model Enemy  { Position, Velocity, Health }
+
+  handler Spawn phase Start {
+    spawn Hero  { Position { column: 2, row: 8 } }
+    spawn Grunt { Position { column: 18, row: 8 }; Velocity { delta_x: -1 } }
+  }
+
+  handler AdvanceEnemies phase Update {
+    for (position, velocity) in query [Position, Velocity, {Enemy}] {
+      position.column = position.column + velocity.delta_x
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-namespace.html b/kw-namespace.html new file mode 100644 index 00000000..ea94846f --- /dev/null +++ b/kw-namespace.html @@ -0,0 +1,38 @@ + + + + + +namespace — Ludic + + + + + + +
+ +
+ keyword +

namespace

+
+ namespace Name { export function foo(…) … internal function bar(…) … } +

A namespace block declares a Name.* namespace once and controls its public surface declaratively, instead of annotating every function with @Namespace(Name) one at a time. Inside the block each function short(…) is emitted as namelower_short; an export function (the default) is callable from anywhere as Name.short(…), while an internal function is a private helper — emitted, and callable by its short name from other functions in the same block, but not part of the Name.* surface (calling Name.internalOne() is a compile error). It is the block sugar for the per-function @Namespace annotation, so a package's public API reads at a glance.

Sibling calls inside the block use the short name (base()), and the compiler rewrites them to the emitted name — so a namespace reads like a self-contained module.

+ +

Example

program Demo {
+  namespace Combat {
+    export function amount() -> int { return base() + 5 }
+    internal function base() -> int { return 10 }
+    export function double(n: int) -> int { return n + n }
+  }
+  entry {
+    print(Combat.amount())      # 15
+    print(Combat.double(21))    # 42
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-new.html b/kw-new.html new file mode 100644 index 00000000..1bf017d8 --- /dev/null +++ b/kw-new.html @@ -0,0 +1,36 @@ + + + + + +new — Ludic + + + + + + +
+ +
+ keyword +

new

+
+ new Record { … } new []T +

new constructs a heap value and returns a reference to it. new Record { field: value, … } allocates a property (or struct) record, filling any omitted fields with their declared defaults; new []T allocates an empty growable slice of element type T, ready for List.push. Because both are references, passing one around shares the same underlying object. Use new for standalone data your code holds directly — as opposed to spawn, which creates a full ECS entity the world tracks and queries.

+ +

Example

program Build {
+  property Point { x: int = 0, y: int = 0 }
+
+  entry {
+    let origin = new Point { x: 3 }   # y defaults to 0
+    let items = new []int             # empty, then grow it
+    List.push(items, origin.x)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-on.html b/kw-on.html new file mode 100644 index 00000000..1f86c507 --- /dev/null +++ b/kw-on.html @@ -0,0 +1,64 @@ + + + + + +on enter / on exit — Ludic + + + + + + +
+
API Reference › Scenes & layers › on enter / on exit
+
+ keyword +

on enter / on exit

+
+ on enter { … } on exit { … } +

on introduces a scene's lifecycle hooks: on enter { … } runs once when the scene becomes active, and on exit { … } runs once as it is left. They are hooks, not phases — the place for per-scene setup and teardown that has no equivalent in a mode register. The start scene's on enter fires at boot right after the Start phase; on a become Other the current scene's on exit runs, the active scene switches, then the new scene's on enter runs. Use on enter to spawn the models a scene needs and reset its counters, and on exit to tidy up before leaving.

+ +

Example

program SceneHookExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  var elapsed_frames: int = 0
+
+  scene Overworld start {
+    layer World {
+      handler EnterBattle phase Update {
+        if Input.key() == 'b' { become Battle }
+      }
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.show()
+      }
+    }
+  }
+
+  scene Battle {
+    on enter {
+      elapsed_frames = 0
+      spawn Enemy { Position { column: 8, row: 4 } }
+    }
+    on exit {
+      for (any_position) in query [Position, {Enemy}] { despawn self() }
+    }
+    layer Fight {
+      handler DrawBattle phase Render {
+        Screen.clear(Color.Crimson)
+        for (current_position) in query [Position, {Enemy}] {
+          Screen.fill_rectangle(x: current_position.column * 16, y: current_position.row * 16, width: 16, height: 16, color: Color.White)
+        }
+        Screen.show()
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-phase.html b/kw-phase.html new file mode 100644 index 00000000..b1ccf8df --- /dev/null +++ b/kw-phase.html @@ -0,0 +1,58 @@ + + + + + +phase — Ludic + + + + + + +
+ +
+ keyword +

phase

+
+ phase Start | Input | Update | FixedUpdate | Render +

Every handler declares a phase — the stage of the frame in which the engine calls it. Start runs once at boot, before the first frame, and is where you seed the world. Then every frame the engine runs, in order, Input (read the keyboard), FixedUpdate (the deterministic fixed-timestep step, for physics and anything that must be reproducible), Update (the ordinary per-frame logic), and Render (draw the frame, ending with Screen.show()). Handlers in the same phase run in declaration order, so ordering within a phase is under your control. Put drawing only in Render; put keyboard reads in Input.

+ +

Example

program PhaseTour {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Player { Position, Velocity }
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 5, row: 5 } }
+  }
+
+  handler ReadKeys phase Input {
+    let pressed = Input.key()
+    for (velocity) in query [Velocity, {Player}] {
+      if pressed == 'd' { velocity.delta_x = 1 }
+      if pressed == 'a' { velocity.delta_x = -1 }
+    }
+  }
+
+  handler AdvancePositions phase Update {
+    for (position, velocity) in query [Position, Velocity, {Player}] {
+      position.column = position.column + velocity.delta_x
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position) in query [Position, {Player}] {
+      Screen.fill_rectangle(x: position.column * 16, y: position.row * 16, width: 16, height: 16, color: Color.White)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-prefab.html b/kw-prefab.html new file mode 100644 index 00000000..21f7453f --- /dev/null +++ b/kw-prefab.html @@ -0,0 +1,33 @@ + + + + + +prefab — Ludic + + + + + + +
+ +
+ keyword +

prefab

+
+ prefab Name: Model { Comp { field: value }, … } +

A prefab names a model together with preset field values, so what every spawn of a kind shares is written once: prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } }. spawn Grunt { Position { x: 40 } } spawns the model with the prefab's presets and then the spawn's own fields on top. Prefabs chain — prefab Grunt: Foe where Foe is itself a prefab — so shared presets live in one place. Field values are ordinary expressions evaluated at each spawn (a preset may read a global such as a loaded sprite id), @OnSpawn(Model) runs as for any spawn of the model, spawn is also an expression yielding the new entity, and Prefab.spawn(name:) picks a prefab by name at runtime.

+ +

Example

# doc-check: skip — composite: prefabs plus their spawns
+prefab Foe:   Creature { Faction { id: 2 }, Body { policy: BodyPolicy.TopDown } }
+prefab Grunt: Foe { Stats { hp: 30, max_hp: 30 }, Weapon { def_id: WeaponId.Bite } }
+
+let grunt = spawn Grunt { Position { x: 40, y: 60 } }
+let other = Prefab.spawn(name: "Grunt")
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-program.html b/kw-program.html new file mode 100644 index 00000000..78b7e183 --- /dev/null +++ b/kw-program.html @@ -0,0 +1,46 @@ + + + + + +program — Ludic + + + + + + +
+ +
+ keyword +

program

+
+ program Name { … } +

A program is the outermost unit of Ludic source, and everything else — properties, models, handlers, functions, constants and your named state — lives inside its braces. Each program compiles to exactly one native game (or, with a module, one shared library), so a project has a single top-level program block. The name you give it is used for the built binary and for diagnostics, and by convention matches the file. When the engine boots, it runs your Start handlers once and then drives the per-frame phase loop until the game quits.

+ +

Example

program SpaceDrift {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  var score: int = 0
+
+  handler Boot phase Start {
+    spawn Hero { Position { column: 10, row: 8 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position) in query [Position, {Player}] {
+      Screen.fill_rectangle(x: position.column * 16, y: position.row * 16, width: 16, height: 16, color: Color.LimeGreen)
+    }
+    Screen.draw_number(x: 6, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-property.html b/kw-property.html new file mode 100644 index 00000000..e5417049 --- /dev/null +++ b/kw-property.html @@ -0,0 +1,45 @@ + + + + + +property — Ludic + + + + + + +
+ +
+ keyword +

property

+
+ property Name { field: T = default, … } +

A property declares a named record of typed fields, each with a default value. It is the one record keyword in Ludic, and how you use it decides how it is stored: list it in a model (or attach it with spawn) and it becomes a per-instance component held in the engine's storage and bound in queries; construct it with new and it becomes a plain heap record addressed by a pointer. Fields carry a type and a default, so a freshly spawned or new-ed property starts fully seeded. Give fields descriptive names — column/row, not x/y — because those names are what handlers read and write.

+ +

Example

program Descent {
+  property Position { column: int = 0, row: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+  model Player { Position, Health }
+
+  handler Spawn phase Start {
+    spawn Hero { Position { column: 4, row: 4 }; Health { current: 80 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position, health) in query [Position, Health, {Player}] {
+      let bar_width = health.current / 4
+      Screen.fill_rectangle(x: position.column, y: position.row, width: bar_width, height: 4, color: Color.Crimson)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-public.html b/kw-public.html new file mode 100644 index 00000000..e4281493 --- /dev/null +++ b/kw-public.html @@ -0,0 +1,35 @@ + + + + + +public — Ludic + + + + + + +
+ +
+ keyword +

public

+
+ scene Name start public { … } +

public marks a scene, layer, or event as part of the game's public surface, so its lifecycle is emitted as named events other systems and mods can react to with @On. A public scene fires scene_<Name>_enter and scene_<Name>_exit; a public layer fires layer_<Name>_show / _hide. It is the declaration-level counterpart to the @Public annotation that promotes a model or handler's lifecycle hooks. Keep it off by default and add it only where the modding surface genuinely needs the hook.

+ +

Example

program Flow {
+  @On(scene_Menu_enter) handler Greet { print(1) }
+
+  scene Menu start public {
+    on enter { print(10) }   # entering Menu prints 10, then fires scene_Menu_enter
+    on exit  { print(20) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-query.html b/kw-query.html new file mode 100644 index 00000000..08024ad4 --- /dev/null +++ b/kw-query.html @@ -0,0 +1,44 @@ + + + + + +query — Ludic + + + + + + +
+ +
+ keyword +

query

+
+ query [PropA, PropB, {Tag}] +

A query selects every live model instance that carries all the listed properties, and is the core way handlers reach data. Use it in a for (…) in query [...] loop, where you write one loop variable per bound property in declaration order; each pass binds those properties for one matching instance and self() gives that instance. A {Model} term in tag position filters by model kind without binding a variable, so query [Position, {Player}] walks only players. Add a where clause to filter on field values (where health.current > 0). Matching is lazy and re-checked per instance as the loop reaches each id, not snapshotted, so spawns and despawns during the loop follow well-defined rules.

+ +

Example

program Collisions {
+  property Position { column: int = 0, row: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+  model Enemy { Position, Health }
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 5, row: 5 }; Health { current: 10 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (position, health) in query [Position, Health, {Enemy}] where health.current > 0 {
+      Screen.fill_rectangle(x: position.column * 16, y: position.row * 16, width: 16, height: 16, color: Color.Crimson)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-return.html b/kw-return.html new file mode 100644 index 00000000..4cfdebd3 --- /dev/null +++ b/kw-return.html @@ -0,0 +1,47 @@ + + + + + +return — Ludic + + + + + + +
+ +
+ keyword +

return

+
+ return value +

A return statement ends the current function and hands its result back to the caller. Its value must match the function's declared return type; in a -> void function you write a bare return (or simply let the body end) to exit early. return is often paired with an early guard — check a condition and return straight away — which keeps the common path unindented. Inside a handler's query loop, prefer break or continue to control the loop; return leaves the whole function.

+ +

Example

program Clamp {
+  property Health { current: int = 100, maximum: int = 100 }
+  model Player { Health }
+
+  function clamp_current(value: int, maximum: int) -> int {
+    if value < 0 { return 0 }
+    if value > maximum { return maximum }
+    return value
+  }
+
+  handler Boot phase Start {
+    spawn Hero { Health { current: 250, maximum: 100 } }
+  }
+
+  handler Normalize phase Update {
+    for (health) in query [Health, {Player}] {
+      health.current = clamp_current(value: health.current, maximum: health.maximum)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-scene.html b/kw-scene.html new file mode 100644 index 00000000..605e0a74 --- /dev/null +++ b/kw-scene.html @@ -0,0 +1,56 @@ + + + + + +scene — Ludic + + + + + + +
+ +
+ keyword +

scene

+
+ scene Name [start] { on enter{} on exit{} layer … } +

A scene is one of the mutually-exclusive states a game moves between — a title screen, the overworld, a battle, a shop. Exactly one scene is active at a time, and only the active scene's layer handlers run each phase; handlers declared outside any scene are global and run every frame regardless. A scene may carry an on enter / on exit pair of lifecycle hooks and one or more layer blocks that group its handlers. Under the hood a scene lowers to an implicit state machine the compiler writes for you (scenes number themselves by declaration order), so become from one scene to another costs two direct calls and a register store — no dispatch table.

+ +

Example

program SceneExample {
+  var elapsed_frames: int = 0
+
+  scene Title start {
+    on enter { elapsed_frames = 0 }
+    layer Main {
+      handler WaitForStart phase Update {
+        elapsed_frames = elapsed_frames + 1
+        if Input.key() != 0 { become Overworld }
+      }
+      handler DrawTitle phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.draw_text(x: 60, y: 100, text: "PRESS ANY KEY", color: Color.White, scale: 1)
+        Screen.show()
+      }
+    }
+  }
+
+  scene Overworld {
+    on enter { elapsed_frames = 0 }
+    layer World {
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.fill_rectangle(x: 80, y: 80, width: 16, height: 16, color: Color.LimeGreen)
+        Screen.show()
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-self.html b/kw-self.html new file mode 100644 index 00000000..a4479756 --- /dev/null +++ b/kw-self.html @@ -0,0 +1,42 @@ + + + + + +self — Ludic + + + + + + +
+ +
+ keyword +

self

+
+ self() +

self() yields the model instance currently being visited by the innermost enclosing query loop, as an entity handle. It is how a handler names "the instance this pass is about" — most often to despawn self(), to attach/detach a property on self(), or to pass the current instance to a function. Because it depends on the active query iteration, self() is only meaningful inside a for (…) in query […] loop (or a @Queries handler, which desugars to one). Outside any query it has no instance to refer to.

+ +

Example

program Reaper {
+  property Position { column: int = 0, row: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+  model Enemy { Position, Health }
+
+  handler Boot phase Start {
+    spawn Grunt { Position { column: 1, row: 1 }; Health { current: 0 } }
+  }
+
+  handler RemoveDefeated phase LateUpdate {
+    for (health) in query [Health, {Enemy}] {
+      if health.current <= 0 { despawn self() }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-spawn.html b/kw-spawn.html new file mode 100644 index 00000000..28fd840c --- /dev/null +++ b/kw-spawn.html @@ -0,0 +1,38 @@ + + + + + +spawn — Ludic + + + + + + +
+ +
+ keyword +

spawn

+
+ spawn Label { Prop { field: v }; … } +

A spawn creates a new model instance and attaches the listed properties, each field starting from its default and then taking any overrides you give. The Label after spawn is just a readable name for the instance at that call site — it does not have to be a declared model — while the {Prop { … }; …} block is what actually determines which properties the instance carries. If you spawn a declared model, every property in that model comes along automatically. A newly spawned instance is visible to matching queries immediately, and one spawned mid-loop at a higher id is even visited in the same tick, so spawn into a later phase if you want to defer that.

+ +

Example

program Waves {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  property Health   { current: int = 100, maximum: int = 100 }
+  model Enemy { Position, Velocity, Health }
+
+  handler SpawnWave phase Start {
+    spawn Grunt   { Position { column: 18, row: 4 }; Velocity { delta_x: -1 }; Health { current: 20 } }
+    spawn Brute   { Position { column: 18, row: 9 }; Velocity { delta_x: -1 }; Health { current: 60 } }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-start.html b/kw-start.html new file mode 100644 index 00000000..365131e7 --- /dev/null +++ b/kw-start.html @@ -0,0 +1,52 @@ + + + + + +start — Ludic + + + + + + +
+ +
+ keyword +

start

+
+ scene Name start { … } +

start marks the scene the game begins in. Written right after the scene's name (scene Title start { … }), it tells the compiler which scene becomes active at boot: its on enter hook fires once, immediately after the Start phase, and its layers begin dispatching on the first frame. Mark exactly one scene start; if no scene is marked, the first one declared is used. Every other scene is reached later with become.

+ +

Example

program StartSceneExample {
+  scene MainMenu start {
+    on enter { Screen.status("main menu") }
+    layer Menu {
+      handler ChooseOption phase Update {
+        if Input.key() == 'p' { become Overworld }
+      }
+      handler DrawMenu phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.draw_text(x: 40, y: 90, text: "PRESS P TO PLAY", color: Color.White, scale: 1)
+        Screen.show()
+      }
+    }
+  }
+
+  scene Overworld {
+    layer World {
+      handler DrawWorld phase Render {
+        Screen.clear(Color.MidnightBlue)
+        Screen.fill_rectangle(x: 100, y: 100, width: 16, height: 16, color: Color.Gold)
+        Screen.show()
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-state.html b/kw-state.html new file mode 100644 index 00000000..2024df1b --- /dev/null +++ b/kw-state.html @@ -0,0 +1,47 @@ + + + + + +state — Ludic + + + + + + +
+ +
+ keyword +

state

+
+ state Name { … } +

A state declares one state of an enclosing machine: a named block whose body runs while the machine's store holds that state's value. States take their value from declaration order — the first state is 0, the next 1, and so on — so you refer to them by name and never track the numbers yourself (write state Name = expr only when a state must have a specific value). From inside a state, become OtherName transitions the machine by storing the target state's value back into the store. Keep each state focused on the logic for that mode and hand off with become when its condition to move on is met.

+ +

Example

program DoorControl {
+  enum DoorState { Closed, Opening, Open }
+  var door: int = DoorState.Closed
+  var elapsed_frames: int = 0
+
+  handler RunDoor phase Update {
+    machine door {
+      state Closed {
+        if Input.key() == ' ' { elapsed_frames = 0; become Opening }
+      }
+      state Opening {
+        elapsed_frames = elapsed_frames + 1
+        if elapsed_frames >= 30 { become Open }
+      }
+      state Open {
+        Screen.status("door open")
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-test.html b/kw-test.html new file mode 100644 index 00000000..984c11ce --- /dev/null +++ b/kw-test.html @@ -0,0 +1,48 @@ + + + + + +test — Ludic + + + + + + +
+
API Reference › Testing › test
+
+ keyword +

test

+
+ test "name" { … } +

A test "name" { … } block declares a named test. Every test in a file is discovered automatically and run by a generated entry point — there is no entry to write and nothing to register. The runner prints ok - name for a test whose assertions all held and FAIL - name for one that had a failure, then a == N passed, M failed == summary, and the program exits non-zero if any test failed. Writing a test should feel like writing a function: put a few assertions in a block and run it.

Assertions (each records a failure and prints file:line: … failed but keeps going, so one run reports every failure):

- expect(cond) — the boolean cond must be true. +- expect_eq(a, b) — a must equal b; on failure prints (got a, want b). +- expect_near(a, b, tol) — a must be within tol of b (absolute). Use it for fixed-point results and accumulated integer math, where an exact match is too brittle.

Run a spec file directly with the compiler-runner — ludic mymath_test.ludic compiles it to a native binary, runs it, and forwards the pass/fail exit code — so it drops straight into bin/ludic and CI.

Line coverage. Compile with the --coverage flag and the compiler instruments every statement with a per-source-line hit counter; at exit the counts are written to the file named by $LUDIC_COVERAGE (default ludic.cov) as a FILE <name> header followed by one <line> <hits> row per instrumented line. The instrumentation is flag-gated and additive, so an ordinary build — and the compiler's own self-compile — stays byte-identical. bin/ludic-dev test --coverage compiles the test specs this way, runs them, and aggregates the dumps into a per-file report that names the lines your tests never reached:

`` +== line coverage (bin/ludic-dev test --coverage) == + examples/library/coverage.ludic 16/17 lines 94% uncovered: 33 + examples/library/testing.ludic 17/17 lines 100% + ---- + total: 33/34 lines 97% +``

+ +

Example

program MathSpec {
+  function add(a: int, b: int) -> int { return a + b }
+
+  test "addition adds" {
+    expect_eq(add(2, 3), 5)
+    expect(add(1, 1) == 2)
+  }
+
+  test "fixed math is close enough" {
+    let half = fixed(1) / 2          # 0.5 in Q16.16
+    expect_near(half, 32768, 2)      # within 2 raw units of 0.5
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-try.html b/kw-try.html new file mode 100644 index 00000000..b346b707 --- /dev/null +++ b/kw-try.html @@ -0,0 +1,51 @@ + + + + + +try — Ludic + + + + + + +
+ +
+ keyword +

try

+
+ try EXPR else { … } +

A try EXPR else { … } expression recovers a fallible operation without exceptions or unwinding. EXPR evaluates to a result — a value carrying either a success payload (from ok) or a failure message (from err). When it is ok, the whole try expression is that payload and the else block is skipped. When it is err, the else block runs and its trailing expression supplies the fallback value; inside the block the failure message is bound to error, so you can log or branch on it. It is a plain branch on the result's tag — the happy path stays one line, there is no hidden control flow, and nothing unwinds the stack.

The payload is an i32-width scalar (int, fixed, bool, or entity) — the common case of "a number or nothing". For richer payloads and full pattern-matching over tagged unions, see the type-system work (#1).

+ +

Example

program SaveLoad {
+  # a fallible op returns a result: ok(payload) or err(message)
+  function load_score(slot: int) -> result {
+    if slot == 1 { return ok(4200) }
+    return err("no save in that slot")
+  }
+
+  test "ok yields the payload, err falls back" {
+    let a = try load_score(1) else { 0 }      # 4200
+    let b = try load_score(9) else { 0 }      # 0 (fallback)
+    expect_eq(a, 4200)
+    expect_eq(b, 0)
+  }
+
+  test "the fallback can read the failure via `error`" {
+    var logged = 0
+    let score = try load_score(3) else {
+      if error == "no save in that slot" { logged = 1 }
+      0                                        # trailing expression = the fallback
+    }
+    expect_eq(score, 0)
+    expect_eq(logged, 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-ui.html b/kw-ui.html new file mode 100644 index 00000000..fda423ac --- /dev/null +++ b/kw-ui.html @@ -0,0 +1,55 @@ + + + + + +ui — Ludic + + + + + + +
+ +
+ keyword +

ui

+
+ ui { panel { … } } +

A ui block declares a retained widget tree as data — panels, labels and buttons — and hands layout, drawing and keyboard focus to the engine, so you describe the interface once instead of repainting it every frame. Widget types are panel (a container with optional skin/background/border), col/row (pure stacks), label, button (focusable), image and spacer, and their props are evaluated at build time, so font: title_font reads a value the program set first. Each id: Name mints a UI_Name handle you drive from handlers: call Ui.build() then Ui.open(UI_MainMenu) at start, Ui.tick(Input.key()) each update to move focus and activate, and Ui.clicked(UI_Name) to react. Draw it during Render with Ui.render() between Screen.clear and Screen.show().

+ +

Example

program TitleScreen {
+  var title_font: int = 0
+
+  ui MainMenu {
+    panel id: Root w: 288 pad: 16 gap: 6 bg: 0x1a1a2c align: center {
+      label  text: "CHRONO RIFT" font: title_font size: 26 fg: 0xffe060 align: center
+      button id: NewGame text: "New Game" font: title_font size: 16 w: 236
+      button id: Quit    text: "Quit"     font: title_font size: 16 w: 236
+    }
+  }
+
+  handler Boot phase Start {
+    title_font = Font.load("/System/Library/Fonts/Supplemental/Arial.ttf")
+    Ui.build()
+    Ui.open(UI_MainMenu)
+  }
+
+  handler Navigate phase Update {
+    Ui.tick(Input.key())
+    if Ui.clicked(UI_Quit) { quit() }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Ui.render()
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-var.html b/kw-var.html new file mode 100644 index 00000000..52cb3a7f --- /dev/null +++ b/kw-var.html @@ -0,0 +1,43 @@ + + + + + +var — Ludic + + + + + + +
+ +
+ keyword +

var

+
+ var name: T = value +

A var declares a mutable binding: unlike let, you may reassign it later with =, +=, -=, and friends. Where it appears decides its lifetime — inside a handler or function body it is a local, and at program scope it is persistent, named game state that every handler shares. Program-scope vars are the modern replacement for numeric registers: instead of reg(0), you write var score: int = 0 and read and write score by name. Use var for anything that genuinely changes — accumulators, the current score, a mode flag, an elapsed-frame counter — and prefer let for values that never change.

+ +

Example

program Scorekeeper {
+  var score: int = 0
+  var elapsed_frames: int = 0
+
+  handler Tick phase Update {
+    elapsed_frames = elapsed_frames + 1
+    if elapsed_frames % 60 == 0 { score = score + 1 }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
+    Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-where.html b/kw-where.html new file mode 100644 index 00000000..2537f67f --- /dev/null +++ b/kw-where.html @@ -0,0 +1,37 @@ + + + + + +where — Ludic + + + + + + +
+ +
+ keyword +

where

+
+ for (Binds) in query [Comps] where cond { … } +

where attaches a boolean condition to a query loop, so the body runs only for entities whose components satisfy it — the rows that fail the test are skipped entirely. The condition is an ordinary expression over the bound components, such as where Battle.hp <= 0 and Battle.side == 1. It is the imperative twin of a component filter written inside a @Queries annotation, and keeps the guard next to the loop instead of repeating an if at the top of the body.

+ +

Example

program Cleanup {
+  property Health { hp: int = 0 }
+  model Foe { Health }
+
+  handler ReapDead phase LateUpdate {
+    for (Health) in query [Health, {Foe}] where Health.hp <= 0 {
+      # only dead foes reach here
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/kw-while.html b/kw-while.html new file mode 100644 index 00000000..ff28aa90 --- /dev/null +++ b/kw-while.html @@ -0,0 +1,45 @@ + + + + + +while — Ludic + + + + + + +
+ +
+ keyword +

while

+
+ while cond { … } +

A while loop re-evaluates its condition before every pass and runs the body as long as it stays true, so it is the tool when the number of iterations is not known up front. As with if, the condition needs no parentheses and the braces are required. break leaves the loop immediately and continue jumps to the next condition check. When you are simply counting over a fixed range, prefer the numeric for i in a .. b loop, which is clearer and binds a fresh index for you; reach for while when the step or the stopping test is irregular.

+ +

Example

program Countdown {
+  var fuse: int = 5
+  var elapsed_frames: int = 0
+
+  handler Tick phase Update {
+    elapsed_frames = elapsed_frames + 1
+    while fuse > 0 and elapsed_frames % 60 == 0 {
+      fuse = fuse - 1
+      elapsed_frames = elapsed_frames + 1
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 8, y: 8, value: fuse, color: Color.Crimson, scale: 3)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-ambient.html b/light-ambient.html new file mode 100644 index 00000000..2152a60f --- /dev/null +++ b/light-ambient.html @@ -0,0 +1,37 @@ + + + + + +Light.ambient — Ludic + + + + + + +
+
API Reference › Light › Light.ambient
+
+ method +

Light.ambient

+
+ Light.ambient(color) +

Multiplies every framebuffer pixel by color (a 0x00RRGGBB tint), channel by channel (channel * tint / 255). This is the global modulate that sets mood before any light adds brightness back: a dim blue-grey gives night, a warm brown gives a cave, and 0xFFFFFF is a no-op. Call it once, after drawing the scene and before the Light.point calls that light it back up.

+

Parameters

colorthe ambient tint, 0x00RRGGBB (darker/cooler = a darker scene)
+

Example

program Demo {
+  property Tag { v: int = 0 }
+  model Marker { Tag }
+  handler Render phase Update {
+    Screen.clear(Color.rgb(120, 120, 140))
+    Light.ambient(Color.rgb(48, 48, 72))   # dusk: dim and cool
+    Light.point(160, 120, 90, Color.Amber, 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-clear_gel.html b/light-clear_gel.html new file mode 100644 index 00000000..13e875bf --- /dev/null +++ b/light-clear_gel.html @@ -0,0 +1,40 @@ + + + + + +Light.clear_gel — Ludic + + + + + + +
+
API Reference › Light › Light.clear_gel
+
+ method +

Light.clear_gel

+
+ Light.clear_gel() +

Clears any Light.gel set earlier, so lights emitted after it are a flat single colour again (no centre-to-rim tint). Pair it with Light.gel to scope a colour cookie to just one light.

+ +

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.gel(Color.rgb(40, 60, 160))
+    Light.point(60, 60, 60, Color.rgb(255, 200, 120), 1.0)  # gelled
+    Light.clear_gel()
+    Light.point(150, 60, 60, Color.rgb(255, 240, 200), 1.0)  # flat again
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-clear_normals.html b/light-clear_normals.html new file mode 100644 index 00000000..57dfaa5d --- /dev/null +++ b/light-clear_normals.html @@ -0,0 +1,38 @@ + + + + + +Light.clear_normals — Ludic + + + + + + +
+
API Reference › Light › Light.clear_normals
+
+ method +

Light.clear_normals

+
+ Light.clear_normals() +

Clears the normal buffer set by Light.normal, so every pixel is flat again (lit by distance only). Call it once per frame before re-stamping the surfaces that should shade by facing this frame — the same clear-then-register rhythm as Light.clear_occluders. A no-op if no normal was ever stamped.

+ +

Example

program Demo {
+  property Wall { x: int = 0, y: int = 0 }
+  model Block { Wall }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.clear_normals()
+    Light.normal(60, 50, 16, 24, 0.7, 0.0)
+    Light.point(120, 62, 90, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-clear_occluders.html b/light-clear_occluders.html new file mode 100644 index 00000000..bf6a4d46 --- /dev/null +++ b/light-clear_occluders.html @@ -0,0 +1,38 @@ + + + + + +Light.clear_occluders — Ludic + + + + + + +
+
API Reference › Light › Light.clear_occluders
+
+ method +

Light.clear_occluders

+
+ Light.clear_occluders() +

Forgets every rectangle previously passed to Light.occlude. Occluders accumulate across calls and persist between frames, so a game that casts shadows calls this once at the top of its render phase and then re-registers the walls that should block light this frame — the same clear-then-rebuild rhythm as an immediate-mode draw list. With no occluders registered, Light.point lights its whole radius unobstructed.

+ +

Example

program Demo {
+  property Tag { v: int = 0 }
+  model Marker { Tag }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.clear_occluders()                       # start the frame with no shadows
+    Light.occlude(90, 60, 6, 40)
+    Light.point(40, 80, 100, Color.rgb(255, 255, 255), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-falloff.html b/light-falloff.html new file mode 100644 index 00000000..18a34a47 --- /dev/null +++ b/light-falloff.html @@ -0,0 +1,38 @@ + + + + + +Light.falloff — Ludic + + + + + + +
+
API Reference › Light › Light.falloff
+
+ method +

Light.falloff

+
+ Light.falloff(exponent) +

Sets the **falloff curve** applied to every light emitted after it. A light's brightness ramps from full at the centre to zero at its radius; the ramp is raised to exponent, so 1 is the original linear falloff, 2 is quadratic (a softer, more concentrated core), 3 cubic, and so on. Like the occluder store, the setting persists across frames until changed — a game sets it once, or per light, and it rides alongside the short Light.point / Light.spot signatures. Integer exponent, Q16.16 ramp — deterministic.

+

Parameters

exponentthe ramp power, an integer >= 1 (1 = linear, the default)
+

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.falloff(2)                                        # a tighter, quadratic glow
+    Light.point(80, 60, 70, Color.rgb(255, 210, 130), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-gel.html b/light-gel.html new file mode 100644 index 00000000..faf89f9a --- /dev/null +++ b/light-gel.html @@ -0,0 +1,39 @@ + + + + + +Light.gel — Ludic + + + + + + +
+
API Reference › Light › Light.gel
+
+ method +

Light.gel

+
+ Light.gel(color) +

Sets a **gel** (a colour cookie) for lights emitted after it: the light blends from its own color at the centre to this color at the rim, so a single light can carry a two-tone tint — a warm core cooling to a blue edge, a fire that reddens outward. Clear it with Light.clear_gel to return to a flat single-colour light. The gel rides on both Light.point and Light.spot, and persists until cleared. Per-channel Q16.16 interpolation — deterministic.

+

Parameters

colorthe rim colour, 0x00RRGGBB
+

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.gel(Color.rgb(40, 60, 160))                      # cool blue rim
+    Light.point(80, 60, 70, Color.rgb(255, 200, 120), 1.0)  # warm core
+    Light.clear_gel()
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-height.html b/light-height.html new file mode 100644 index 00000000..0988f88c --- /dev/null +++ b/light-height.html @@ -0,0 +1,39 @@ + + + + + +Light.height — Ludic + + + + + + +
+
API Reference › Light › Light.height
+
+ method +

Light.height

+
+ Light.height(height) +

Sets how far above the surface plane lights sit, in pixels — the z distance used when shading normal-mapped surfaces. A low height makes light rays graze the surface, so tilted faces contrast sharply; a high height lights everything more head-on. It only affects the N·L term, so a scene with no stamped normals is unchanged. The default (64) suits most scenes. Persists until changed.

+

Parameters

heightthe light's height above the plane in pixels (> 0)
+

Example

program Demo {
+  property Wall { x: int = 0, y: int = 0 }
+  model Block { Wall }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.clear_normals()
+    Light.height(24)                                       # low, grazing light
+    Light.normal(60, 50, 16, 24, 0.7, 0.0)
+    Light.point(120, 62, 90, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-normal.html b/light-normal.html new file mode 100644 index 00000000..4436789a --- /dev/null +++ b/light-normal.html @@ -0,0 +1,39 @@ + + + + + +Light.normal — Ludic + + + + + + +
+
API Reference › Light › Light.normal
+
+ method +

Light.normal

+
+ Light.normal(x, y, width, height, nx, ny) +

Stamps a **surface normal** over a screen rectangle into the light pass's normal buffer. Where a normal is stamped, a light shades the surface by the angle it faces — the Lambert term N·L — not by distance alone: a wall facing a torch is bright, one turned away is dim, giving flat sprites a sense of relief (tier 3). nx and ny are the normal's x/y as a fixed in [-1, 1] (a surface facing straight at the camera is 0, 0); the z component is derived. Unstamped pixels are unaffected, and the buffer is allocated only when you stamp — clear it each frame with Light.clear_normals. Deterministic (Q16.16 dot product).

+ +

Example

program Demo {
+  property Wall { x: int = 0, y: int = 0 }
+  model Block { Wall }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.clear_normals()
+    Light.normal(60, 50, 16, 24, 0.7, 0.0)                 # this face is tilted rightward
+    Light.point(120, 62, 90, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-occlude.html b/light-occlude.html new file mode 100644 index 00000000..8bc0d94e --- /dev/null +++ b/light-occlude.html @@ -0,0 +1,38 @@ + + + + + +Light.occlude — Ludic + + + + + + +
+
API Reference › Light › Light.occlude
+
+ method +

Light.occlude

+
+ Light.occlude(x, y, width, height) +

Registers an axis-aligned rectangle (screen space) that blocks light: for every Light.point that follows, any pixel whose ray from the light centre crosses this rectangle — or lies inside it — is left in shadow, carving a hard umbra behind the wall. Register the geometry that should cast shadows this frame, then draw the lights. Occluders persist until Light.clear_occluders, so call that once per frame first; up to 64 occluders are kept.

+ +

Example

program Demo {
+  property Wall { x: int = 0, y: int = 0 }
+  model Block { Wall }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.clear_occluders()
+    Light.occlude(120, 80, 8, 48)                 # a pillar
+    Light.point(60, 100, 120, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-point.html b/light-point.html new file mode 100644 index 00000000..db897292 --- /dev/null +++ b/light-point.html @@ -0,0 +1,37 @@ + + + + + +Light.point — Ludic + + + + + + +
+
API Reference › Light › Light.point
+
+ method +

Light.point

+
+ Light.point(x, y, radius, color, energy) +

Additively accumulates a radial point light centred at (x, y). Brightness is full at the centre and falls off linearly to zero at radius pixels, scaled by energy (a fixed multiplier — 1.0 is full, 2.0 over-drives toward white, 0.5 is dim), and clamped per channel at 255 so overlapping lights add without wrapping. Only the light's bounding box is touched, and any Light.occlude rectangle between the centre and a pixel puts that pixel in shadow. Colours are 0x00RRGGBB.

+

Parameters

radiusthe reach in pixels; brightness is zero at and beyond it
colorthe light colour, 0x00RRGGBB
energya fixed brightness multiplier (1.0 = full)
+

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(32, 32, 48))
+    Light.point(80, 60, 70, Color.rgb(255, 210, 130), 1.0)   # a warm torch
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-soft.html b/light-soft.html new file mode 100644 index 00000000..c5d6c5de --- /dev/null +++ b/light-soft.html @@ -0,0 +1,40 @@ + + + + + +Light.soft — Ludic + + + + + + +
+
API Reference › Light › Light.soft
+
+ method +

Light.soft

+
+ Light.soft(radius) +

Turns on **soft shadows** for lights emitted after it. A hard shadow tests a single ray from the light centre to each pixel, so an occluder edge cuts sharply. With radius > 0 the light is treated as a small disk of that radius: each pixel samples visibility from a cross of points across the disk and averages them, so the shadow edge fades through a **penumbra** instead of a hard line. Light.soft(0) restores hard, single-sample shadows. The setting persists until changed. Deterministic (fixed-point average of integer ray tests).

+

Parameters

radiusthe light's apparent size in pixels; larger = wider penumbra (0 = hard)
+

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.clear_occluders()
+    Light.occlude(96, 40, 6, 40)                           # a pillar casting a shadow
+    Light.soft(5)                                          # 5px penumbra
+    Light.point(60, 60, 90, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-spot.html b/light-spot.html new file mode 100644 index 00000000..d3b1e2c9 --- /dev/null +++ b/light-spot.html @@ -0,0 +1,37 @@ + + + + + +Light.spot — Ludic + + + + + + +
+
API Reference › Light › Light.spot
+
+ method +

Light.spot

+
+ Light.spot(x, y, radius, color, energy, direction, spread) +

Accumulates a **cone** (spot) light — like Light.point, but only the wedge aimed at direction degrees (measured from +x, counter-clockwise) within a half-angle of spread degrees receives light. Pixels outside the cone stay dark, and the last few degrees of the edge feather so the boundary is not a hard line. A spot shares every quality control with a radial light — Light.falloff, Light.soft, Light.gel and the normal buffer all apply. Fully deterministic (integer degrees + Q16.16).

+

Parameters

radiusthe reach in pixels; brightness is zero at and beyond it
colorthe light colour, 0x00RRGGBB
energya fixed brightness multiplier (1.0 = full)
directionthe aim, in whole degrees (0 = right, 90 = up, 180 = left)
spreadthe cone half-angle, in whole degrees (180 ≈ omnidirectional)
+

Example

program Demo {
+  property Torch { x: int = 0, y: int = 0 }
+  model Lamp { Torch }
+  handler Render phase Update {
+    Screen.clear(0)
+    Light.ambient(Color.rgb(24, 24, 32))
+    Light.spot(80, 60, 70, Color.rgb(255, 240, 200), 1.0, 0, 30)   # a flashlight pointing right
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/light-time_of_day.html b/light-time_of_day.html new file mode 100644 index 00000000..a1ecdfee --- /dev/null +++ b/light-time_of_day.html @@ -0,0 +1,37 @@ + + + + + +Light.time_of_day — Ludic + + + + + + +
+
API Reference › Light › Light.time_of_day
+
+ method +

Light.time_of_day

+
+ Light.time_of_day(t) +

Sets the scene's ambient tint from a single **time-of-day** phase t — a fixed in [0, 1] where 0 is midnight, 0.25 dawn, 0.5 noon and 0.75 dusk. It ramps a deep-blue night up to full daylight and back over the day, so a game animates one value and the whole world's mood follows, without wiring the ambient colour by hand. It drives the same multiplicative modulate as Light.ambient, so call it once before adding lights. Deterministic (Q16.16 triangle ramp).

+

Parameters

tthe time of day, a fixed in [0, 1] (0 = midnight, 0.5 = noon)
+

Example

program Demo {
+  property Clock { t: int = 0 }
+  model World { Clock }
+  handler Render phase Update {
+    Screen.clear(Color.rgb(255, 255, 255))
+    Light.time_of_day(0.5)                                 # high noon — full daylight
+    Light.point(80, 60, 60, Color.rgb(255, 240, 200), 1.0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-clear.html b/list-clear.html new file mode 100644 index 00000000..03254be7 --- /dev/null +++ b/list-clear.html @@ -0,0 +1,34 @@ + + + + + +List.clear — Ludic + + + + + + +
+
API Reference › List › List.clear
+
+ method +

List.clear

+
+ List.clear(s) -> void +

Empties s by setting its length back to zero. The backing buffer is kept, so refilling the slice afterward reuses the same memory without reallocating. Use it to reset a per-frame list — collisions, visible tiles, particles to draw — at the start of each update instead of building a new slice every frame.

+

Parameters

sthe slice to empty
+

Example

program FrameHits {
+  var hits: []int = new []int
+
+  handler BeginFrame phase Update {
+    List.clear(hits)   # start the frame with an empty list
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-contains.html b/list-contains.html new file mode 100644 index 00000000..259423c8 --- /dev/null +++ b/list-contains.html @@ -0,0 +1,37 @@ + + + + + +List.contains — Ludic + + + + + + +
+
API Reference › List › List.contains
+
+ method +

List.contains

+
+ List.contains(s, v) -> bool +

Scans s and returns true if any element equals v. Comparison is by value for scalar elements (ints, entities, booleans) and by identity for reference elements. Use it for membership tests — has this tile been visited, is this entity already in the target list, is the achievement unlocked. It is a linear scan, so for very large or hot-path sets a dedicated structure will be faster.

+

Parameters

sthe slice to search
vthe value to look for
+

Example

program Visited {
+  var seen: []int = new []int
+
+  handler Step phase Update {
+    let tile = 42
+    if not List.contains(seen, tile) {
+      List.push(seen, tile)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-first.html b/list-first.html new file mode 100644 index 00000000..70c6becd --- /dev/null +++ b/list-first.html @@ -0,0 +1,36 @@ + + + + + +List.first — Ludic + + + + + + +
+
API Reference › List › List.first
+
+ method +

List.first

+
+ List.first(s) -> T +

Returns element 0 of s — the same as s[0], named for readability. The slice must hold at least one element; calling it on an empty slice reads out of bounds, so guard with List.len(s) > 0 when that is possible. Use it to peek at the head of a queue or the front of an ordered list.

+

Parameters

sa non-empty slice
+

Example

program Queue {
+  var waiting: []int = new []int
+
+  handler Serve phase Update {
+    if List.len(waiting) > 0 {
+      let next = List.first(waiting)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-index_of.html b/list-index_of.html new file mode 100644 index 00000000..c8023f0e --- /dev/null +++ b/list-index_of.html @@ -0,0 +1,37 @@ + + + + + +List.index_of — Ludic + + + + + + +
+
API Reference › List › List.index_of
+
+ method +

List.index_of

+
+ List.index_of(s, v) -> int +

Scans s and returns the index of the first element equal to v, or -1 if none match. It uses the same value/identity comparison as List.contains, but tells you where the match is so you can act on it — swap it out, read a parallel slice at the same index, or guard with if idx >= 0. Only the first match is reported.

+

Parameters

sthe slice to search
vthe value to locate
+

Example

program FindSlot {
+  var ids: []int = new []int
+
+  handler Locate phase Update {
+    let slot = List.index_of(ids, 99)
+    if slot >= 0 {
+      List.swap(ids, slot, List.len(ids) - 1)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-insert.html b/list-insert.html new file mode 100644 index 00000000..f50cffd4 --- /dev/null +++ b/list-insert.html @@ -0,0 +1,32 @@ + + + + + +List.insert — Ludic + + + + + + +
+
API Reference › List › List.insert
+
+ method +

List.insert

+
+ List.insert(s, i, v) -> void +

Inserts v at index i, moving the elements at and after i one place toward the end and growing the backing buffer if needed. Use it to keep a list ordered as you build it, or to splice an item into the middle. Appending is cheaper with List.push.

+ +

Example

program Demo {
+  handler Step phase Update {
+    List.insert(queue, 0, next_id)   # push to the front
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-last.html b/list-last.html new file mode 100644 index 00000000..42557322 --- /dev/null +++ b/list-last.html @@ -0,0 +1,36 @@ + + + + + +List.last — Ludic + + + + + + +
+
API Reference › List › List.last
+
+ method +

List.last

+
+ List.last(s) -> T +

Returns the final element of s — the value at index List.len(s) - 1. Like List.first, it assumes the slice is non-empty, so guard with a length check when the slice might be empty. Use it to read the most recently pushed item without removing it; use List.pop when you want to take it off.

+

Parameters

sa non-empty slice
+

Example

program Trail {
+  var points: []int = new []int
+
+  handler Head phase Update {
+    if List.len(points) > 0 {
+      let newest = List.last(points)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-len.html b/list-len.html new file mode 100644 index 00000000..1c59c9e9 --- /dev/null +++ b/list-len.html @@ -0,0 +1,34 @@ + + + + + +List.len — Ludic + + + + + + +
+
API Reference › List › List.len
+
+ method +

List.len

+
+ List.len(s) -> int +

Returns how many elements s currently holds. It reads the slice's length, which changes as you List.push and List.pop, and is the same value as the bare len(s). Use it to bound a loop, test for emptiness, or find the last index with List.len(s) - 1.

+

Parameters

sthe slice to measure
+

Example

program CountEnemies {
+  var enemies: []int = new []int
+
+  handler Report phase Update {
+    let n = List.len(enemies)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-pop.html b/list-pop.html new file mode 100644 index 00000000..690e7abe --- /dev/null +++ b/list-pop.html @@ -0,0 +1,36 @@ + + + + + +List.pop — Ludic + + + + + + +
+
API Reference › List › List.pop
+
+ method +

List.pop

+
+ List.pop(s) -> T +

Removes the final element of s and returns it, shortening the slice by one. Together with List.push this makes a slice work as a stack: push to add, pop to take the most recent back off. The slice must be non-empty. Use it to unwind a history, process a work list, or undo the last action.

+

Parameters

sa non-empty slice
+

Example

program UndoStack {
+  var history: []int = new []int
+
+  handler Undo phase Update {
+    if List.len(history) > 0 {
+      let last_action = List.pop(history)
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-push.html b/list-push.html new file mode 100644 index 00000000..e4be12ec --- /dev/null +++ b/list-push.html @@ -0,0 +1,34 @@ + + + + + +List.push — Ludic + + + + + + +
+
API Reference › List › List.push
+
+ method +

List.push

+
+ List.push(s, v) -> void +

Adds v to the end of s, growing the backing buffer if needed, so the slice's length increases by one. It is the same operation as the bare push(s, v). Because a slice is shared, the new element is visible to every holder. Use it to collect spawned entities, build a list of hits, or fill an inventory.

+

Parameters

sthe slice to append to
vthe element to add (matching the slice's element type)
+

Example

program Collect {
+  var picked: []int = new []int
+
+  handler Grab phase Update {
+    List.push(picked, 7)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-remove.html b/list-remove.html new file mode 100644 index 00000000..989302ce --- /dev/null +++ b/list-remove.html @@ -0,0 +1,32 @@ + + + + + +List.remove — Ludic + + + + + + +
+
API Reference › List › List.remove
+
+ method +

List.remove

+
+ List.remove(s, v) -> void +

Scans for the first element equal to v and removes it (shifting the rest down); if no element matches, the slice is unchanged. Comparison is by value for scalars and by identity for reference elements, matching List.contains. Only the first match is removed.

+ +

Example

program Demo {
+  handler Step phase Update {
+    List.remove(active, dead_entity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-remove_at.html b/list-remove_at.html new file mode 100644 index 00000000..40fc6c9c --- /dev/null +++ b/list-remove_at.html @@ -0,0 +1,32 @@ + + + + + +List.remove_at — Ludic + + + + + + +
+
API Reference › List › List.remove_at
+
+ method +

List.remove_at

+
+ List.remove_at(s, i) -> void +

Removes the element at index i, moving everything after it one place toward the front and shortening the slice by one. Order is preserved. When order does not matter, swapping the item to the end and calling List.pop is cheaper.

+ +

Example

program Demo {
+  handler Step phase Update {
+    List.remove_at(effects, expired)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-reverse.html b/list-reverse.html new file mode 100644 index 00000000..453e80d4 --- /dev/null +++ b/list-reverse.html @@ -0,0 +1,35 @@ + + + + + +List.reverse — Ludic + + + + + + +
+
API Reference › List › List.reverse
+
+ method +

List.reverse

+
+ List.reverse(s) -> void +

Reverses s so its first element becomes last and vice versa, mutating the slice in place rather than returning a copy. Use it to flip an order you built back-to-front — a path traced from goal to start, an undo stack you want oldest-first, or a list you appended to and now want to read in the opposite direction.

+

Parameters

sthe slice to reverse
+

Example

program PathOrder {
+  var path: []int = new []int
+
+  handler Finalize phase Update {
+    # path was built from the goal back to the start
+    List.reverse(path)   # now start -> goal
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-sample.html b/list-sample.html new file mode 100644 index 00000000..414b34e7 --- /dev/null +++ b/list-sample.html @@ -0,0 +1,29 @@ + + + + + +List.sample — Ludic + + + + + + +
+
API Reference › List › List.sample
+
+ method +

List.sample

+
+ List.sample(pool, count) -> []int +

Returns count picks from an []int, drawn from the seeded RNG. Picks are distinct while the pool has enough elements; with fewer, the spare picks repeat a valid element. An empty pool yields zeros. A relic offer, a loot roll, a random enemy roster.

+ +

Example

# doc-check: skip — illustrative
+relic_offer = List.sample(Relics.unowned(), 3)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-sort.html b/list-sort.html new file mode 100644 index 00000000..68eb3964 --- /dev/null +++ b/list-sort.html @@ -0,0 +1,32 @@ + + + + + +List.sort — Ludic + + + + + + +
+
API Reference › List › List.sort
+
+ method +

List.sort

+
+ List.sort(s) -> void +

Sorts the elements of s in ascending order in place. It is intended for slices of scalar elements (ints, entities, fixed) where < is meaningful; reference elements are ordered by identity, which is rarely useful. Small lists — the nearly-sorted, per-frame kind games usually hold — take a simple insertion sort; larger lists fall to a stable O(n log n) merge sort. Both are stable, so the result is identical either way. To sort records by a field, or in descending order, or with a custom comparator, use List.sort_by, List.sort_desc_by, or List.sort_with.

+ +

Example

program Demo {
+  handler Step phase Update {
+    List.sort(scores)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-sort_by.html b/list-sort_by.html new file mode 100644 index 00000000..42ba33b3 --- /dev/null +++ b/list-sort_by.html @@ -0,0 +1,35 @@ + + + + + +List.sort_by — Ludic + + + + + + +
+
API Reference › List › List.sort_by
+
+ method +

List.sort_by

+
+ List.sort_by(s, key) -> void +

Sorts s in place, ascending, by the number each element maps to through key — a function taking one element and returning an int, fixed, or long. This is how you sort records and entities rather than bare scalars: draw order by y, an inventory by price, nearest targets by distance. The sort is stable (O(n log n) merge sort), so elements with equal keys keep the order they already had — the tie-break that keeps sprites from flickering when their y matches.

+ +

Example

# doc-check: skip — illustrative; `key` is any fn(element) -> number in scope
+function draw_key(e: Sprite) -> int { return e.y }
+
+program Demo {
+  handler Step phase Update {
+    List.sort_by(sprites, draw_key)   # back-to-front draw order
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-sort_desc_by.html b/list-sort_desc_by.html new file mode 100644 index 00000000..e70a5939 --- /dev/null +++ b/list-sort_desc_by.html @@ -0,0 +1,35 @@ + + + + + +List.sort_desc_by — Ludic + + + + + + +
+
API Reference › List › List.sort_desc_by
+
+ method +

List.sort_desc_by

+
+ List.sort_desc_by(s, key) -> void +

Like List.sort_by, but descending — highest key first. The everyday case is a leaderboard: sort players by score so the top scorer lands at index 0. key is a function taking one element and returning a number (int, fixed, or long). The sort is stable (O(n log n) merge sort): players tied on score keep their prior order, so a stable tie-break (say, who reached the score first) survives the sort.

+ +

Example

# doc-check: skip — illustrative; `key` is any fn(element) -> number in scope
+function score_of(p: Player) -> int { return p.score }
+
+program Demo {
+  handler Step phase Update {
+    List.sort_desc_by(players, score_of)   # players[0] is the leader
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-sort_with.html b/list-sort_with.html new file mode 100644 index 00000000..7be5a301 --- /dev/null +++ b/list-sort_with.html @@ -0,0 +1,35 @@ + + + + + +List.sort_with — Ludic + + + + + + +
+
API Reference › List › List.sort_with
+
+ method +

List.sort_with

+
+ List.sort_with(s, cmp) -> void +

Sorts s in place with a full comparator cmp(a, b) -> int: return a negative number when a should come before b, positive when after, and zero when they tie. Reach for this when the order is not a single key — a multi-field sort (by rarity, then by name), or a comparison that mixes fields. For the common "subtract two numbers" comparator, returning a.field - b.field gives ascending order. The sort is stable (O(n log n) merge sort), so ties (a zero result) keep the elements' prior order.

+ +

Example

# doc-check: skip — illustrative; `cmp` is any fn(a, b) -> int in scope
+function by_price(a: Item, b: Item) -> int { return a.price - b.price }
+
+program Demo {
+  handler Step phase Update {
+    List.sort_with(shop, by_price)   # cheapest first
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/list-swap.html b/list-swap.html new file mode 100644 index 00000000..8d2a30da --- /dev/null +++ b/list-swap.html @@ -0,0 +1,37 @@ + + + + + +List.swap — Ludic + + + + + + +
+
API Reference › List › List.swap
+
+ method +

List.swap

+
+ List.swap(s, i, j) -> void +

Exchanges the elements at indices i and j in s, in place. Both indices must be within the slice. Swapping is the building block of shuffles, sorts, and moving an item to the end before List.pop to remove it cheaply without shifting the rest.

+

Parameters

sthe slice to modify
ithe first index
jthe second index
+

Example

program RemoveFast {
+  var actors: []int = new []int
+
+  handler Kill phase Update {
+    let dead = 2
+    let last = List.len(actors) - 1
+    List.swap(actors, dead, last)   # move it to the end...
+    let removed = List.pop(actors)  # ...then drop it
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-debug.html b/log-debug.html new file mode 100644 index 00000000..9a19470a --- /dev/null +++ b/log-debug.html @@ -0,0 +1,33 @@ + + + + + +Log.debug — Ludic + + + + + + +
+
API Reference › Log › Log.debug
+
+ method +

Log.debug

+
+ Log.debug(msg, [key, value]...) -> void +

Logs msg at the **debug** level (1) — development-time detail that is useful while building a feature but noise in a shipped game. It prints on stderr as [DEBUG] msg with any structured key=value fields, when the Log.set_level threshold is 1 or lower. Reach for it to trace state transitions, loaded assets, or the values feeding a calculation you are not yet sure of.

+

Parameters

msgthe message string
+

Example

program Debug {
+  entry {
+    Log.set_level(1)
+    Log.debug("player state", "hp", 100, "phase", "combat")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-error.html b/log-error.html new file mode 100644 index 00000000..fd4b57b5 --- /dev/null +++ b/log-error.html @@ -0,0 +1,32 @@ + + + + + +Log.error — Ludic + + + + + + +
+
API Reference › Log › Log.error
+
+ method +

Log.error

+
+ Log.error(msg, [key, value]...) -> void +

Logs msg at the **error** level (4), the highest — a real failure the game could not handle cleanly: a save that would not write, a required asset that could not load. It prints on stderr as [ERROR] msg with any structured key=value fields, and shows at every threshold except one set above 4. Error is the level you almost always want visible, including in release builds.

+

Parameters

msgthe message string
+

Example

program Error {
+  entry {
+    Log.error("save failed", "path", "slot1.sav", "code", 5)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-info.html b/log-info.html new file mode 100644 index 00000000..92a215b9 --- /dev/null +++ b/log-info.html @@ -0,0 +1,32 @@ + + + + + +Log.info — Ludic + + + + + + +
+
API Reference › Log › Log.info
+
+ method +

Log.info

+
+ Log.info(msg, [key, value]...) -> void +

Logs msg at the **info** level (2) — the normal-operation milestones you want to see in a running game: a level loaded, a match started, a save written. It prints on stderr as [INFO] msg with any structured key=value fields, when the Log.set_level threshold is 2 or lower. Info is a sensible default level for a development build.

+

Parameters

msgthe message string
+

Example

program Info {
+  entry {
+    Log.info("level loaded", "name", "cavern", "entities", 42)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-level.html b/log-level.html new file mode 100644 index 00000000..fd534e08 --- /dev/null +++ b/log-level.html @@ -0,0 +1,34 @@ + + + + + +Log.level — Ludic + + + + + + +
+
API Reference › Log › Log.level
+
+ method +

Log.level

+
+ Log.level() -> int +

Returns the current logging threshold — the minimum level that Log.set_level last set (0 by default). Use it to branch on how verbose logging is: skip building an expensive debug string when it would be dropped anyway, or show an in-game indicator that verbose logging is on. The levels are trace 0 through error 4.

+ +

Example

program Level {
+  entry {
+    print(Log.level())        # 0
+    Log.set_level(2)
+    print(Log.level())        # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-set_level.html b/log-set_level.html new file mode 100644 index 00000000..063dcd3b --- /dev/null +++ b/log-set_level.html @@ -0,0 +1,34 @@ + + + + + +Log.set_level — Ludic + + + + + + +
+
API Reference › Log › Log.set_level
+
+ method +

Log.set_level

+
+ Log.set_level(n) -> void +

Sets the logging threshold: only messages whose level is n or higher are written; everything below is silently dropped. The levels are trace 0, debug 1, info 2, warn 3, error 4, so set_level(3) shows only warnings and errors, and the default of 0 shows everything. Set it once at startup — low in development, high (3, or 5 to silence all) for release — and every call site adjusts automatically. Read the current value back with Log.level.

+

Parameters

nthe minimum level to show (0–4; use 5 to silence every level)
+

Example

program Quiet {
+  entry {
+    Log.set_level(3)          # warnings and errors only
+    Log.info("startup")       # dropped
+    Log.warn("low memory")    # shown
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-trace.html b/log-trace.html new file mode 100644 index 00000000..1fee6fb0 --- /dev/null +++ b/log-trace.html @@ -0,0 +1,33 @@ + + + + + +Log.trace — Ludic + + + + + + +
+
API Reference › Log › Log.trace
+
+ method +

Log.trace

+
+ Log.trace(msg, [key, value]...) -> void +

Logs msg at the **trace** level (0), the most verbose — the fine-grained "I am here, this is the value" tracing you turn on only when hunting a specific problem. It appears on stderr as [TRACE] msg, followed by any structured key=value fields, but only when the threshold set by Log.set_level is 0. Because trace is usually filtered out, keep the calls wherever they help; a disabled level costs only a comparison.

+

Parameters

msgthe message string
+

Example

program Trace {
+  entry {
+    Log.set_level(0)
+    Log.trace("spawned entity", "id", 7, "at", "cave")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/log-warn.html b/log-warn.html new file mode 100644 index 00000000..c4efa057 --- /dev/null +++ b/log-warn.html @@ -0,0 +1,32 @@ + + + + + +Log.warn — Ludic + + + + + + +
+
API Reference › Log › Log.warn
+
+ method +

Log.warn

+
+ Log.warn(msg, [key, value]...) -> void +

Logs msg at the **warn** level (3) — something is wrong but the game recovered: a missing asset fell back to a placeholder, a value was clamped, a deprecated path was taken. It prints on stderr as [WARN] msg with any structured key=value fields, when the Log.set_level threshold is 3 or lower. Warnings are a good level to keep on in release builds so player bug reports capture them.

+

Parameters

msgthe message string
+

Example

program Warn {
+  entry {
+    Log.warn("missing texture, using placeholder", "path", "rock.png")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ludic-highlight.js b/ludic-highlight.js new file mode 100644 index 00000000..fc352336 --- /dev/null +++ b/ludic-highlight.js @@ -0,0 +1,214 @@ +/* ============================================================================ + * ludic-highlight.js — the Ludic syntax highlighter + docs interactions. + * + * GENERATED FILE — do not edit by hand. The symbol tables below (keywords, + * types, phases, namespace methods + their parameter labels, builtins, + * annotations, namespaces, one-line tips, per-item page targets and hover-card + * data) are produced by tools/docgen/gen.py from the per-symbol source files in + * docs/language/**. Add a symbol there and it is recognized, colored, tipped, + * linked and card-previewed here automatically. + * + * In any code sample: every keyword/type/builtin/annotation links to its own + * page; `Screen.fill_rectangle` links `Screen` → the namespace page and + * `fill_rectangle` → the method page, separately; a named argument like + * `width:` links to that parameter on the method's page; `Color.Charcoal` + * links `Color` and `Charcoal` separately. Hovering any of them shows a summary + * card pulled from the real API data. + * ========================================================================== */ +(function (global) { + "use strict"; + + const S = {"keywords": {"program": "kw-program", "property": "kw-property", "model": "kw-model", "handler": "kw-handler", "phase": "kw-phase", "const": "kw-const", "var": "kw-var", "let": "kw-let", "function": "kw-function", "fn": "kw-fn", "prefab": "kw-prefab", "return": "kw-return", "entry": "kw-entry", "import": "kw-import", "public": "kw-public", "extern": "kw-extern", "new": "kw-new", "namespace": "kw-namespace", "enum": "kw-enum", "ui": "kw-ui", "spawn": "kw-spawn", "despawn": "kw-despawn", "query": "kw-query", "self": "kw-self", "enable": "kw-enable", "disable": "kw-disable", "attach": "kw-attach", "detach": "kw-detach", "if": "kw-if", "else": "kw-if", "while": "kw-while", "for": "kw-for", "in": "kw-in", "match": "kw-match", "machine": "kw-machine", "state": "kw-state", "become": "kw-become", "break": "kw-break", "continue": "kw-continue", "where": "kw-where", "try": "kw-try", "scene": "kw-scene", "layer": "kw-layer", "on": "kw-on", "enter": "kw-enter", "exit": "kw-exit", "start": "kw-start", "event": "kw-event", "emit": "kw-emit", "cancel": "kw-cancel", "cancellable": "kw-cancellable", "test": "kw-test"}, "types": {"int": "type-int", "fixed": "type-fixed", "long": "type-long", "bool": "type-bool", "float": "type-float", "string": "type-string", "double": "type-double", "entity": "type-entity", "pointer": "type-pointer", "words": "type-words", "byte": "type-byte", "fixeds": "type-fixeds", "countdown": "type-countdown", "floats": "type-floats", "doubles": "type-floats", "pointers": "type-pointers", "Vector": "type-vector", "IVec2": "type-ivec2", "Rect": "type-rect", "void": "type-void"}, "phases": {"Start": "phase-start", "Input": "phase-input", "FixedUpdate": "phase-fixedupdate", "Update": "phase-update", "LateUpdate": "phase-lateupdate", "Render": "phase-render", "Overlay": "phase-overlay"}, "builtins": {"world_attach_dyn": {"id": "fn-world_attach_dyn", "params": ["target", "property"]}, "world_count": {"id": "fn-world_count", "params": []}, "world_detach_dyn": {"id": "fn-world_detach_dyn", "params": ["target", "property"]}, "world_field_id": {"id": "fn-world_field_id", "params": ["property", "name"]}, "world_get": {"id": "fn-world_get", "params": ["target", "property", "field"]}, "world_has": {"id": "fn-world_has", "params": ["target", "property"]}, "world_kind": {"id": "fn-world_kind", "params": ["target"]}, "world_load": {"id": "fn-world_load", "params": ["buf", "len"]}, "world_model_id": {"id": "fn-world_model_id", "params": ["name"]}, "world_prop_id": {"id": "fn-world_prop_id", "params": ["name"]}, "world_query_next": {"id": "fn-world_query_next", "params": ["property", "cursor"]}, "world_register_prop": {"id": "fn-world_register_prop", "params": ["name", "fields"]}, "world_save": {"id": "fn-world_save", "params": ["buf"]}, "world_set": {"id": "fn-world_set", "params": ["target", "property", "field", "value"]}, "world_size": {"id": "fn-world_size", "params": []}, "world_spawn": {"id": "fn-world_spawn", "params": ["model"]}, "print": {"id": "fn-print", "params": ["x"]}, "string": {"id": "fn-string", "params": ["x"]}, "quit": {"id": "fn-quit", "params": []}, "save": {"id": "fn-save", "params": []}, "load": {"id": "fn-load", "params": []}, "len": {"id": "fn-len", "params": ["x"]}, "push": {"id": "fn-push", "params": ["slice", "x"]}, "abs": {"id": "fn-abs", "params": ["a"]}, "clamp": {"id": "fn-clamp", "params": ["v", "lo", "hi"]}, "panic": {"id": "fn-panic", "params": ["msg"]}, "assert": {"id": "fn-assert", "params": ["cond", "msg"]}, "ok": {"id": "fn-ok", "params": ["v"]}, "err": {"id": "fn-err", "params": ["msg"]}, "is_ok": {"id": "fn-is_ok", "params": ["r"]}, "is_err": {"id": "fn-is_err", "params": ["r"]}, "some": {"id": "fn-some", "params": ["v"]}, "none": {"id": "fn-none", "params": []}, "is_some": {"id": "fn-is_some", "params": ["o"]}, "is_none": {"id": "fn-is_none", "params": ["o"]}, "unwrap_or": {"id": "fn-unwrap_or", "params": ["o", "fallback"]}, "exit": {"id": "fn-exit", "params": ["code"]}, "run": {"id": "fn-run", "params": ["cmd"]}, "getenv": {"id": "fn-getenv", "params": ["name"]}, "read_char": {"id": "fn-read_char", "params": []}, "arg": {"id": "fn-arg", "params": ["i"]}, "arg_count": {"id": "fn-arg_count", "params": []}, "bytes": {"id": "fn-bytes", "params": ["n"]}, "file_stderr": {"id": "fn-file_stderr", "params": []}, "file_stdout": {"id": "fn-file_stdout", "params": []}, "file_write": {"id": "fn-file_write", "params": ["handle", "buf", "len"]}, "fixed": {"id": "fn-fixed", "params": ["x"]}, "floor": {"id": "fn-floor", "params": ["x"]}, "max": {"id": "fn-max", "params": ["a", "b"]}, "min": {"id": "fn-min", "params": ["a", "b"]}, "ui_build": {"id": "fn-ui_build", "params": []}, "words": {"id": "fn-words", "params": ["n"]}, "float": {"id": "fn-float", "params": ["x"]}, "double": {"id": "fn-float", "params": ["x"]}, "int": {"id": "fn-int", "params": ["x"]}, "long": {"id": "fn-int", "params": ["x"]}, "float_bits": {"id": "fn-float_bits", "params": ["x"]}, "float_from_bits": {"id": "fn-float_bits", "params": ["x"]}, "double_bits": {"id": "fn-float_bits", "params": ["x"]}, "double_from_bits": {"id": "fn-float_bits", "params": ["x"]}}, "nsmethods": {"Screen.clear": {"id": "screen-clear", "params": ["color"]}, "Screen.fill_rectangle": {"id": "screen-fill_rectangle", "params": ["x", "y", "width", "height", "color"]}, "Screen.draw_rectangle": {"id": "screen-draw_rectangle", "params": ["x", "y", "width", "height", "color"]}, "Screen.put_pixel": {"id": "screen-put_pixel", "params": ["x", "y", "color"]}, "Screen.draw_text": {"id": "screen-draw_text", "params": ["x", "y", "text", "color", "scale"]}, "Screen.draw_number": {"id": "screen-draw_number", "params": ["x", "y", "value", "color", "scale"]}, "Screen.show": {"id": "screen-show", "params": []}, "Screen.width": {"id": "screen-width", "params": []}, "Screen.height": {"id": "screen-height", "params": []}, "Screen.status": {"id": "screen-status", "params": ["text"]}, "Screen.line": {"id": "screen-line", "params": []}, "Screen.circle": {"id": "screen-circle", "params": []}, "Screen.fill_circle": {"id": "screen-fill_circle", "params": []}, "Screen.triangle": {"id": "screen-triangle", "params": []}, "Screen.fill_triangle": {"id": "screen-fill_triangle", "params": []}, "Screen.sprite": {"id": "screen-sprite", "params": []}, "Screen.sprite_scaled": {"id": "screen-sprite_scaled", "params": []}, "Screen.oval": {"id": "screen-oval", "params": []}, "Screen.camera": {"id": "screen-camera", "params": []}, "Screen.clip": {"id": "screen-clip", "params": []}, "Screen.clip_reset": {"id": "screen-clip_reset", "params": []}, "Screen.blend_mode": {"id": "screen-blend_mode", "params": []}, "Screen.measure_text": {"id": "screen-measure_text", "params": []}, "Screen.pixel": {"id": "screen-pixel", "params": []}, "Screen.bar": {"id": "screen-bar", "params": []}, "Clock.now": {"id": "clock-now", "params": []}, "Clock.set": {"id": "clock-set", "params": []}, "Clock.advance": {"id": "clock-advance", "params": []}, "Clock.reset": {"id": "clock-reset", "params": []}, "Collision.rects": {"id": "collision-rects", "params": []}, "Collision.point_rect": {"id": "collision-point_rect", "params": []}, "Collision.circles": {"id": "collision-circles", "params": []}, "Collision.rect_circle": {"id": "collision-rect_circle", "params": []}, "Color.rgb": {"id": "color-rgb", "params": []}, "Color.rgba": {"id": "color-rgba", "params": []}, "Color.lerp": {"id": "color-lerp", "params": []}, "Color.darken": {"id": "color-darken", "params": []}, "Color.lighten": {"id": "color-lighten", "params": []}, "Color.with_alpha": {"id": "color-with_alpha", "params": []}, "Crypto.sha256": {"id": "crypto-sha256", "params": ["s"]}, "Crypto.hmac_sha256": {"id": "crypto-hmac_sha256", "params": ["key", "msg"]}, "Crypto.verify_hmac": {"id": "crypto-verify_hmac", "params": ["key", "msg", "mac"]}, "Crypto.hex": {"id": "crypto-hex", "params": ["s"]}, "Crypto.ct_equal": {"id": "crypto-ct_equal", "params": ["a", "b"]}, "Crypto.random_bytes": {"id": "crypto-random_bytes", "params": ["n"]}, "Crypto.random_hex": {"id": "crypto-random_hex", "params": ["n"]}, "Crypto.random_u32": {"id": "crypto-random_u32", "params": []}, "Crypto.base64": {"id": "crypto-base64", "params": ["s"]}, "Date.new": {"id": "date-new", "params": []}, "Date.year": {"id": "date-year", "params": []}, "Date.month": {"id": "date-month", "params": []}, "Date.day": {"id": "date-day", "params": []}, "Date.weekday": {"id": "date-weekday", "params": []}, "Date.is_leap": {"id": "date-is_leap", "params": []}, "Date.days_in_month": {"id": "date-days_in_month", "params": []}, "Date.to_epoch": {"id": "date-to_epoch", "params": []}, "Date.add_days": {"id": "date-add_days", "params": []}, "Date.diff_days": {"id": "date-diff_days", "params": []}, "DateTime.from": {"id": "datetime-from", "params": []}, "DateTime.date": {"id": "datetime-date", "params": []}, "DateTime.add": {"id": "datetime-add", "params": []}, "DateTime.year": {"id": "datetime-year", "params": []}, "DateTime.month": {"id": "datetime-month", "params": []}, "DateTime.day": {"id": "datetime-day", "params": []}, "DateTime.weekday": {"id": "datetime-weekday", "params": []}, "DateTime.hour": {"id": "datetime-hour", "params": []}, "DateTime.minute": {"id": "datetime-minute", "params": []}, "DateTime.second": {"id": "datetime-second", "params": []}, "DateTime.format": {"id": "datetime-format", "params": []}, "DateTime.parse": {"id": "datetime-parse", "params": []}, "Duration.seconds": {"id": "duration-seconds", "params": []}, "Duration.minutes": {"id": "duration-minutes", "params": []}, "Duration.hours": {"id": "duration-hours", "params": []}, "Duration.days": {"id": "duration-days", "params": []}, "Duration.as_seconds": {"id": "duration-as_seconds", "params": []}, "Duration.as_minutes": {"id": "duration-as_minutes", "params": []}, "Duration.as_hours": {"id": "duration-as_hours", "params": []}, "Duration.as_days": {"id": "duration-as_days", "params": []}, "Ease.in": {"id": "ease-in", "params": []}, "Ease.out": {"id": "ease-out", "params": []}, "Ease.in_out": {"id": "ease-in_out", "params": []}, "Ease.back": {"id": "ease-back", "params": []}, "Ease.bounce": {"id": "ease-bounce", "params": []}, "Ease.elastic": {"id": "ease-elastic", "params": []}, "Fs.exists": {"id": "fs-exists", "params": ["p"]}, "Fs.is_dir": {"id": "fs-is_dir", "params": ["p"]}, "Fs.read_text": {"id": "fs-read_text", "params": ["p"]}, "Fs.write_text": {"id": "fs-write_text", "params": ["p", "s"]}, "Fs.append_text": {"id": "fs-append_text", "params": ["p", "s"]}, "Fs.remove": {"id": "fs-remove", "params": ["p"]}, "Fs.size": {"id": "fs-size", "params": ["p"]}, "Fs.mkdir": {"id": "fs-mkdir", "params": ["p"]}, "Fs.copy": {"id": "fs-copy", "params": ["src", "dst"]}, "Fs.list": {"id": "fs-list", "params": ["dir"]}, "Hash.of": {"id": "hash-of", "params": ["s"]}, "Hash.fnv1a": {"id": "hash-fnv1a", "params": ["s"]}, "Hash.crc32": {"id": "hash-crc32", "params": ["s"]}, "Hash.mix": {"id": "hash-mix", "params": ["x"]}, "Hash.combine": {"id": "hash-combine", "params": []}, "Hash.of64": {"id": "hash-of64", "params": ["s"]}, "Hash.fnv1a_64": {"id": "hash-fnv1a_64", "params": ["s"]}, "Hash.mix64": {"id": "hash-mix64", "params": ["x"]}, "Input.key": {"id": "input-key", "params": []}, "Input.bind": {"id": "input-bind", "params": []}, "Input.rebind": {"id": "input-rebind", "params": []}, "Input.poll": {"id": "input-poll", "params": []}, "Input.down": {"id": "input-down", "params": []}, "Input.pressed": {"id": "input-pressed", "params": []}, "Input.record": {"id": "input-record", "params": []}, "Input.replay": {"id": "input-replay", "params": []}, "Input.key_down": {"id": "input-key_down", "params": []}, "Input.key_pressed": {"id": "input-key_pressed", "params": []}, "Input.key_released": {"id": "input-key_released", "params": []}, "Input.key_label": {"id": "input-key_label", "params": []}, "Input.press": {"id": "input-press", "params": []}, "Input.release": {"id": "input-release", "params": []}, "Input.axis": {"id": "input-axis", "params": []}, "Input.axis_i": {"id": "input-axis_i", "params": []}, "Input.vector": {"id": "input-vector", "params": []}, "Input.strength": {"id": "input-strength", "params": []}, "Input.mouse_x": {"id": "input-mouse_x", "params": []}, "Input.mouse_y": {"id": "input-mouse_y", "params": []}, "Input.mouse_dx": {"id": "input-mouse_dx", "params": []}, "Input.mouse_dy": {"id": "input-mouse_dy", "params": []}, "Input.mouse_down": {"id": "input-mouse_down", "params": []}, "Input.wheel": {"id": "input-wheel", "params": []}, "Input.set_mouse": {"id": "input-set_mouse", "params": []}, "Input.pad_connected": {"id": "input-pad_connected", "params": []}, "Input.pad_button": {"id": "input-pad_button", "params": []}, "Input.pad_axis": {"id": "input-pad_axis", "params": []}, "Input.set_pad": {"id": "input-set_pad", "params": []}, "Input.touch_count": {"id": "input-touch_count", "params": []}, "Input.touch_x": {"id": "input-touch_x", "params": []}, "Input.touch_y": {"id": "input-touch_y", "params": []}, "Input.set_touch": {"id": "input-set_touch", "params": []}, "Input.action": {"id": "input-action", "params": []}, "Input.bind_pad": {"id": "input-bind_pad", "params": []}, "Input.active": {"id": "input-active", "params": []}, "Input.just_pressed": {"id": "input-just_pressed", "params": []}, "Input.just_released": {"id": "input-just_released", "params": []}, "Input.cursor_mode": {"id": "input-cursor_mode", "params": []}, "Input.move_i": {"id": "input-move_i", "params": []}, "List.len": {"id": "list-len", "params": ["s"]}, "List.push": {"id": "list-push", "params": ["s", "v"]}, "List.clear": {"id": "list-clear", "params": ["s"]}, "List.first": {"id": "list-first", "params": ["s"]}, "List.last": {"id": "list-last", "params": ["s"]}, "List.pop": {"id": "list-pop", "params": ["s"]}, "List.swap": {"id": "list-swap", "params": ["s", "i", "j"]}, "List.contains": {"id": "list-contains", "params": ["s", "v"]}, "List.index_of": {"id": "list-index_of", "params": ["s", "v"]}, "List.reverse": {"id": "list-reverse", "params": ["s"]}, "List.insert": {"id": "list-insert", "params": []}, "List.remove_at": {"id": "list-remove_at", "params": []}, "List.remove": {"id": "list-remove", "params": []}, "List.sort": {"id": "list-sort", "params": []}, "List.sort_by": {"id": "list-sort_by", "params": []}, "List.sort_desc_by": {"id": "list-sort_desc_by", "params": []}, "List.sort_with": {"id": "list-sort_with", "params": []}, "List.sample": {"id": "list-sample", "params": []}, "Log.trace": {"id": "log-trace", "params": ["msg"]}, "Log.debug": {"id": "log-debug", "params": ["msg"]}, "Log.info": {"id": "log-info", "params": ["msg"]}, "Log.warn": {"id": "log-warn", "params": ["msg"]}, "Log.error": {"id": "log-error", "params": ["msg"]}, "Log.set_level": {"id": "log-set_level", "params": ["n"]}, "Log.level": {"id": "log-level", "params": []}, "Math.min": {"id": "math-min", "params": ["a", "b"]}, "Math.max": {"id": "math-max", "params": ["a", "b"]}, "Math.abs": {"id": "math-abs", "params": ["x"]}, "Math.clamp": {"id": "math-clamp", "params": ["v", "lo", "hi"]}, "Math.sign": {"id": "math-sign", "params": ["x"]}, "Math.floor": {"id": "math-floor", "params": ["x"]}, "Math.ceil": {"id": "math-ceil", "params": ["x"]}, "Math.round": {"id": "math-round", "params": ["x"]}, "Math.lerp": {"id": "math-lerp", "params": ["a", "b", "t"]}, "Math.inverse_lerp": {"id": "math-inverse_lerp", "params": ["a", "b", "v"]}, "Math.remap": {"id": "math-remap", "params": ["v", "in0", "in1", "out0", "out1"]}, "Math.sqrt": {"id": "math-sqrt", "params": []}, "Math.sin": {"id": "math-sin", "params": []}, "Math.cos": {"id": "math-cos", "params": []}, "Math.tan": {"id": "math-tan", "params": []}, "Math.hypot": {"id": "math-hypot", "params": []}, "Math.dist": {"id": "math-dist", "params": []}, "Math.dist2": {"id": "math-dist2", "params": []}, "Math.deg_to_rad": {"id": "math-deg_to_rad", "params": []}, "Math.rad_to_deg": {"id": "math-rad_to_deg", "params": []}, "Math.posmod": {"id": "math-posmod", "params": []}, "Math.wrap": {"id": "math-wrap", "params": []}, "Math.ping_pong": {"id": "math-ping_pong", "params": []}, "Math.snapped": {"id": "math-snapped", "params": []}, "Math.move_toward": {"id": "math-move_toward", "params": []}, "Math.smoothstep": {"id": "math-smoothstep", "params": []}, "Math.atan2": {"id": "math-atan2", "params": []}, "Math.asin": {"id": "math-asin", "params": []}, "Math.acos": {"id": "math-acos", "params": []}, "Math.exp": {"id": "math-exp", "params": []}, "Math.log": {"id": "math-log", "params": []}, "Math.pow": {"id": "math-pow", "params": []}, "Memory.bytes": {"id": "memory-bytes", "params": []}, "Memory.words": {"id": "memory-words", "params": []}, "Memory.copy": {"id": "memory-copy", "params": []}, "Memory.fill": {"id": "memory-fill", "params": []}, "Memory.peek": {"id": "memory-peek", "params": []}, "Memory.poke": {"id": "memory-poke", "params": []}, "Mime.of": {"id": "mime-of", "params": ["path"]}, "Mime.sniff": {"id": "mime-sniff", "params": ["path"]}, "Network.send": {"id": "network-send", "params": []}, "Network.poll": {"id": "network-poll", "params": []}, "Network.serialize": {"id": "network-serialize", "params": []}, "Network.apply": {"id": "network-apply", "params": []}, "Network.owner": {"id": "network-owner", "params": []}, "Network.set_owner": {"id": "network-set_owner", "params": []}, "Network.is_server": {"id": "network-is_server", "params": []}, "Network.is_owner": {"id": "network-is_owner", "params": []}, "Network.local_id": {"id": "network-local_id", "params": []}, "Noise.value2": {"id": "noise-value2", "params": ["seed"]}, "Noise.perlin2": {"id": "noise-perlin2", "params": ["seed"]}, "Noise.simplex2": {"id": "noise-simplex2", "params": ["seed"]}, "Noise.fbm2": {"id": "noise-fbm2", "params": ["seed", "octaves"]}, "Noise.cellular2": {"id": "noise-cellular2", "params": ["seed"]}, "Noise.cellular2_id": {"id": "noise-cellular2_id", "params": ["seed"]}, "Noise.unit": {"id": "noise-unit", "params": ["n"]}, "Os.args": {"id": "os-args", "params": []}, "Os.arg_count": {"id": "os-arg_count", "params": []}, "Os.arg": {"id": "os-arg", "params": ["i"]}, "Os.env": {"id": "os-env", "params": ["name"]}, "Os.env_or": {"id": "os-env_or", "params": ["name", "fallback"]}, "Os.has_env": {"id": "os-has_env", "params": ["name"]}, "Os.set_env": {"id": "os-set_env", "params": ["name", "value"]}, "Os.unset_env": {"id": "os-unset_env", "params": ["name"]}, "Os.exit": {"id": "os-exit", "params": ["code"]}, "Os.platform": {"id": "os-platform", "params": []}, "Os.arch": {"id": "os-arch", "params": []}, "Os.stdout_write": {"id": "os-stdout_write", "params": ["s"]}, "Os.stderr_write": {"id": "os-stderr_write", "params": ["s"]}, "Os.save_dir": {"id": "os-save_dir", "params": ["app"]}, "Os.config_dir": {"id": "os-config_dir", "params": ["app"]}, "Os.cache_dir": {"id": "os-cache_dir", "params": ["app"]}, "Os.temp_dir": {"id": "os-temp_dir", "params": []}, "Os.pid": {"id": "os-pid", "params": []}, "Path.join": {"id": "path-join", "params": ["a", "b"]}, "Path.dir": {"id": "path-dir", "params": ["p"]}, "Path.base": {"id": "path-base", "params": ["p"]}, "Path.ext": {"id": "path-ext", "params": ["p"]}, "Path.stem": {"id": "path-stem", "params": ["p"]}, "Path.normalize": {"id": "path-normalize", "params": ["p"]}, "Save.write": {"id": "save-write", "params": []}, "Save.read": {"id": "save-read", "params": []}, "System.run": {"id": "system-run", "params": []}, "System.read_char": {"id": "system-read_char", "params": []}, "System.file_open": {"id": "system-file_open", "params": []}, "System.file_read": {"id": "system-file_read", "params": []}, "System.file_write": {"id": "system-file_write", "params": []}, "System.file_seek": {"id": "system-file_seek", "params": []}, "System.file_tell": {"id": "system-file_tell", "params": []}, "System.file_close": {"id": "system-file_close", "params": []}, "Text.length": {"id": "text-length", "params": ["s"]}, "Text.char_at": {"id": "text-char_at", "params": ["s", "i"]}, "Text.slice": {"id": "text-slice", "params": ["s", "a", "b"]}, "Text.starts_with": {"id": "text-starts_with", "params": ["s", "prefix"]}, "Text.ends_with": {"id": "text-ends_with", "params": ["s", "suffix"]}, "Text.contains": {"id": "text-contains", "params": ["s", "sub"]}, "Text.index_of": {"id": "text-index_of", "params": ["s", "sub"]}, "Text.equals": {"id": "text-equals", "params": ["a", "b"]}, "Text.concat": {"id": "text-concat", "params": ["a", "b"]}, "Text.to_int": {"id": "text-to_int", "params": ["s"]}, "Text.from_int": {"id": "text-from_int", "params": ["n"]}, "Text.upper": {"id": "text-upper", "params": []}, "Text.lower": {"id": "text-lower", "params": []}, "Text.trim": {"id": "text-trim", "params": []}, "Text.repeat": {"id": "text-repeat", "params": []}, "Text.pad_left": {"id": "text-pad_left", "params": []}, "Text.pad_right": {"id": "text-pad_right", "params": []}, "Text.split": {"id": "text-split", "params": []}, "Text.join": {"id": "text-join", "params": []}, "Text.replace": {"id": "text-replace", "params": []}, "Time.frame": {"id": "time-frame", "params": []}, "Time.delta": {"id": "time-delta", "params": []}, "Time.elapsed": {"id": "time-elapsed", "params": []}, "Time.now": {"id": "time-now", "params": []}, "Time.since": {"id": "time-since", "params": []}, "Unicode.len": {"id": "unicode-len", "params": ["s"]}, "Unicode.byte_len": {"id": "unicode-byte_len", "params": ["s"]}, "Unicode.is_valid_utf8": {"id": "unicode-is_valid_utf8", "params": ["s"]}, "Unicode.char_at": {"id": "unicode-char_at", "params": ["s", "i"]}, "Unicode.chars": {"id": "unicode-chars", "params": ["s"]}, "Unicode.upper": {"id": "unicode-upper", "params": ["s"]}, "Unicode.lower": {"id": "unicode-lower", "params": ["s"]}, "Unicode.truncate": {"id": "unicode-truncate", "params": ["s", "n"]}, "Unicode.grapheme_len": {"id": "unicode-grapheme_len", "params": ["s"]}, "Uuid.new": {"id": "uuid-new", "params": []}, "Uuid.v4": {"id": "uuid-v4", "params": []}, "Uuid.new_v7": {"id": "uuid-new_v7", "params": []}, "Uuid.v7": {"id": "uuid-v7", "params": []}, "Uuid.parse": {"id": "uuid-parse", "params": ["s"]}, "Uuid.is_valid": {"id": "uuid-is_valid", "params": ["s"]}, "Uuid.to_text": {"id": "uuid-to_text", "params": ["id"]}, "Uuid.equals": {"id": "uuid-equals", "params": []}, "Uuid.nil": {"id": "uuid-nil", "params": []}, "World.get": {"id": "world-get", "params": []}, "World.set": {"id": "world-set", "params": []}, "World.has": {"id": "world-has", "params": []}, "World.count": {"id": "world-count", "params": []}, "World.size": {"id": "world-size", "params": []}, "World.spawn": {"id": "world-spawn", "params": []}, "World.save": {"id": "world-save", "params": []}, "World.load": {"id": "world-load", "params": []}, "World.prop_id": {"id": "world-prop_id", "params": []}, "World.field_id": {"id": "world-field_id", "params": []}, "World.model_id": {"id": "world-model_id", "params": []}, "World.kind": {"id": "world-kind", "params": []}, "World.register_prop": {"id": "world-register_prop", "params": []}, "World.attach": {"id": "world-attach", "params": []}, "World.detach": {"id": "world-detach", "params": []}, "World.query_next": {"id": "world-query_next", "params": []}, "World.despawn": {"id": "world-despawn", "params": []}, "Audio.load": {"id": "audio-load", "params": []}, "Audio.play": {"id": "audio-play", "params": []}, "Audio.play_at": {"id": "audio-play_at", "params": []}, "Audio.play_sound": {"id": "audio-play_sound", "params": []}, "Audio.play_music": {"id": "audio-play_music", "params": []}, "Audio.stop": {"id": "audio-stop", "params": []}, "Audio.stop_music": {"id": "audio-stop_music", "params": []}, "Audio.stop_all": {"id": "audio-stop_all", "params": []}, "Audio.volume": {"id": "audio-volume", "params": []}, "Audio.pitch": {"id": "audio-pitch", "params": []}, "Audio.is_playing": {"id": "audio-is_playing", "params": []}, "Audio.define": {"id": "audio-define", "params": []}, "Audio.named": {"id": "audio-named", "params": []}, "Random.range": {"id": "random-range", "params": ["low", "high"]}, "Random.chance": {"id": "random-chance", "params": ["percent"]}, "Random.seed": {"id": "random-seed", "params": ["value"]}, "Random.value": {"id": "random-value", "params": []}, "Random.int": {"id": "random-int", "params": []}, "Random.sign": {"id": "random-sign", "params": []}, "Random.weighted": {"id": "random-weighted", "params": []}, "Vector.make": {"id": "vector-make", "params": []}, "Vector.zero": {"id": "vector-zero", "params": []}, "Vector.x": {"id": "vector-x", "params": []}, "Vector.y": {"id": "vector-y", "params": []}, "Vector.add": {"id": "vector-add", "params": []}, "Vector.sub": {"id": "vector-sub", "params": []}, "Vector.scale": {"id": "vector-scale", "params": []}, "Vector.dot": {"id": "vector-dot", "params": []}, "Vector.length": {"id": "vector-length", "params": []}, "Vector.distance": {"id": "vector-distance", "params": []}, "Vector.normalize": {"id": "vector-normalize", "params": []}, "Vector.lerp": {"id": "vector-lerp", "params": []}, "Vector.rotate": {"id": "vector-rotate", "params": []}, "Vector.angle": {"id": "vector-angle", "params": []}, "Vector.from_angle": {"id": "vector-from_angle", "params": []}, "Http.get": {"id": "http-get", "params": []}, "Http.post": {"id": "http-post", "params": []}, "Http.request": {"id": "http-request", "params": []}, "Http.open": {"id": "http-open", "params": []}, "Http.set": {"id": "http-set", "params": []}, "Http.body": {"id": "http-body", "params": []}, "Http.body_bytes": {"id": "http-body_bytes", "params": []}, "Http.send": {"id": "http-send", "params": []}, "Http.poll": {"id": "http-poll", "params": []}, "Http.status": {"id": "http-status", "params": []}, "Http.ok": {"id": "http-ok", "params": []}, "Http.text": {"id": "http-text", "params": []}, "Http.body_len": {"id": "http-body_len", "params": []}, "Http.header": {"id": "http-header", "params": []}, "Http.free": {"id": "http-free", "params": []}, "Http.parse": {"id": "http-parse", "params": []}, "Http.save_to": {"id": "http-save_to", "params": []}, "Http.received": {"id": "http-received", "params": []}, "Http.expected": {"id": "http-expected", "params": []}, "IVec2.make": {"id": "ivec2-make", "params": []}, "IVec2.zero": {"id": "ivec2-zero", "params": []}, "IVec2.x": {"id": "ivec2-x", "params": []}, "IVec2.y": {"id": "ivec2-y", "params": []}, "IVec2.add": {"id": "ivec2-add", "params": []}, "IVec2.sub": {"id": "ivec2-sub", "params": []}, "IVec2.scale": {"id": "ivec2-scale", "params": []}, "IVec2.dot": {"id": "ivec2-dot", "params": []}, "IVec2.equal": {"id": "ivec2-equal", "params": []}, "IVec2.manhattan": {"id": "ivec2-manhattan", "params": []}, "IVec2.to_vector": {"id": "ivec2-to_vector", "params": []}, "IVec2.distance2": {"id": "ivec2-distance2", "params": []}, "IVec2.within": {"id": "ivec2-within", "params": []}, "IVec2.heading": {"id": "ivec2-heading", "params": []}, "IVec2.along": {"id": "ivec2-along", "params": []}, "IVec2.step": {"id": "ivec2-step", "params": []}, "Map.size": {"id": "map-size", "params": ["width", "height"]}, "Map.row": {"id": "map-row", "params": ["y", "cells"]}, "Map.tile": {"id": "map-tile", "params": ["x", "y"]}, "Map.get": {"id": "map-get", "params": []}, "Map.set": {"id": "map-set", "params": []}, "Map.fill": {"id": "map-fill", "params": []}, "Map.rect": {"id": "map-rect", "params": []}, "Map.border": {"id": "map-border", "params": []}, "Map.random_cell": {"id": "map-random_cell", "params": []}, "Map.is_solid": {"id": "map-is_solid", "params": []}, "Map.is_solid_at": {"id": "map-is_solid_at", "params": []}, "Map.width": {"id": "map-width", "params": []}, "Map.height": {"id": "map-height", "params": []}, "Map.random_cell_far": {"id": "map-random_cell_far", "params": []}, "Map.to_tile": {"id": "map-to_tile", "params": []}, "Rect.make": {"id": "rect-make", "params": []}, "Rect.x": {"id": "rect-x", "params": []}, "Rect.y": {"id": "rect-y", "params": []}, "Rect.w": {"id": "rect-w", "params": []}, "Rect.h": {"id": "rect-h", "params": []}, "Rect.right": {"id": "rect-right", "params": []}, "Rect.bottom": {"id": "rect-bottom", "params": []}, "Rect.center": {"id": "rect-center", "params": []}, "Rect.contains": {"id": "rect-contains", "params": []}, "Rect.intersects": {"id": "rect-intersects", "params": []}, "Udp.open": {"id": "udp-open", "params": []}, "Udp.port": {"id": "udp-port", "params": []}, "Udp.send": {"id": "udp-send", "params": []}, "Udp.recv": {"id": "udp-recv", "params": []}, "Udp.from_ip": {"id": "udp-from_ip", "params": []}, "Udp.from_port": {"id": "udp-from_port", "params": []}, "Udp.close": {"id": "udp-close", "params": []}, "Udp.resolve": {"id": "udp-resolve", "params": []}, "Udp.local_ip": {"id": "udp-local_ip", "params": []}, "Udp.ip": {"id": "udp-ip", "params": []}, "Udp.ip_text": {"id": "udp-ip_text", "params": []}, "BigInt.from": {"id": "bigint-from", "params": []}, "BigInt.parse": {"id": "bigint-parse", "params": []}, "BigInt.add": {"id": "bigint-add", "params": []}, "BigInt.sub": {"id": "bigint-sub", "params": []}, "BigInt.mul": {"id": "bigint-mul", "params": []}, "BigInt.neg": {"id": "bigint-neg", "params": []}, "BigInt.pow": {"id": "bigint-pow", "params": []}, "BigInt.div": {"id": "bigint-div", "params": []}, "BigInt.mod": {"id": "bigint-mod", "params": []}, "BigInt.cmp": {"id": "bigint-cmp", "params": []}, "BigInt.eq": {"id": "bigint-eq", "params": []}, "BigInt.is_zero": {"id": "bigint-is_zero", "params": []}, "BigInt.to_int": {"id": "bigint-to_int", "params": []}, "BigInt.str": {"id": "bigint-str", "params": []}, "Process.spawn": {"id": "process-spawn", "params": []}, "Process.poll": {"id": "process-poll", "params": []}, "Process.kill": {"id": "process-kill", "params": []}, "Process.free": {"id": "process-free", "params": []}, "Decimal.from": {"id": "decimal-from", "params": []}, "Decimal.parse": {"id": "decimal-parse", "params": []}, "Decimal.add": {"id": "decimal-add", "params": []}, "Decimal.sub": {"id": "decimal-sub", "params": []}, "Decimal.mul": {"id": "decimal-mul", "params": []}, "Decimal.neg": {"id": "decimal-neg", "params": []}, "Decimal.cmp": {"id": "decimal-cmp", "params": []}, "Decimal.eq": {"id": "decimal-eq", "params": []}, "Decimal.scale": {"id": "decimal-scale", "params": []}, "Decimal.rescale": {"id": "decimal-rescale", "params": []}, "Decimal.str": {"id": "decimal-str", "params": []}, "Dict.new": {"id": "dict-new", "params": []}, "Dict.set": {"id": "dict-set", "params": []}, "Dict.get": {"id": "dict-get", "params": []}, "Dict.get_or": {"id": "dict-get_or", "params": []}, "Dict.has": {"id": "dict-has", "params": []}, "Dict.remove": {"id": "dict-remove", "params": []}, "Dict.size": {"id": "dict-size", "params": []}, "Dict.clear": {"id": "dict-clear", "params": []}, "Dict.keys": {"id": "dict-keys", "params": []}, "Set.new": {"id": "set-new", "params": []}, "Set.add": {"id": "set-add", "params": []}, "Set.has": {"id": "set-has", "params": []}, "Set.remove": {"id": "set-remove", "params": []}, "Set.size": {"id": "set-size", "params": []}, "Set.clear": {"id": "set-clear", "params": []}, "Set.members": {"id": "set-members", "params": []}, "Huge.from": {"id": "huge-from", "params": []}, "Huge.add": {"id": "huge-add", "params": []}, "Huge.sub": {"id": "huge-sub", "params": []}, "Huge.mul": {"id": "huge-mul", "params": []}, "Huge.neg": {"id": "huge-neg", "params": []}, "Huge.cmp": {"id": "huge-cmp", "params": []}, "Huge.sign": {"id": "huge-sign", "params": []}, "Huge.mantissa": {"id": "huge-mantissa", "params": []}, "Huge.exp": {"id": "huge-exp", "params": []}, "Huge.str": {"id": "huge-str", "params": []}, "Angle.from_degrees": {"id": "angle-from_degrees", "params": []}, "Angle.to_degrees": {"id": "angle-to_degrees", "params": []}, "Angle.wrap": {"id": "angle-wrap", "params": []}, "Angle.sin": {"id": "angle-sin", "params": []}, "Angle.cos": {"id": "angle-cos", "params": []}, "Angle.add": {"id": "angle-add", "params": []}, "Angle.diff": {"id": "angle-diff", "params": []}, "Angle.lerp": {"id": "angle-lerp", "params": []}, "Angle.diff_degrees": {"id": "angle-diff_degrees", "params": []}, "Percent.clamp": {"id": "percent-clamp", "params": []}, "Percent.of": {"id": "percent-of", "params": []}, "Percent.lerp": {"id": "percent-lerp", "params": []}, "Percent.apply": {"id": "percent-apply", "params": []}, "Regex.compile": {"id": "regex-compile", "params": ["pattern"]}, "Regex.valid": {"id": "regex-valid", "params": ["pattern"]}, "Regex.matches": {"id": "regex-matches", "params": ["text", "pattern"]}, "Regex.test": {"id": "regex-test", "params": ["text", "re"]}, "Regex.find": {"id": "regex-find", "params": ["text", "pattern"]}, "Regex.exec": {"id": "regex-exec", "params": ["text", "re"]}, "Regex.next": {"id": "regex-next", "params": ["text", "re", "from"]}, "Regex.replace": {"id": "regex-replace", "params": ["text", "pattern", "replacement"]}, "Regex.group": {"id": "regex-group", "params": ["match", "n"]}, "Regex.group_count": {"id": "regex-group_count", "params": ["match"]}, "Regex.start": {"id": "regex-start", "params": ["match", "n"]}, "Regex.end": {"id": "regex-end", "params": ["match", "n"]}, "Regex.ok": {"id": "regex-ok", "params": ["match"]}, "Grid.line": {"id": "grid-line", "params": []}, "Grid.blocked": {"id": "grid-blocked", "params": ["wall"]}, "Grid.line_of_sight": {"id": "grid-line_of_sight", "params": ["wall"]}, "Grid.flood": {"id": "grid-flood", "params": ["wall"]}, "Grid.a_star": {"id": "grid-a_star", "params": ["wall"]}, "Anim.frame": {"id": "anim-frame", "params": ["timer", "fps", "count"]}, "Anim.once": {"id": "anim-once", "params": ["timer", "fps", "count"]}, "Anim.pingpong": {"id": "anim-pingpong", "params": ["timer", "fps", "count"]}, "Anim.finished": {"id": "anim-finished", "params": ["timer", "fps", "count"]}, "Anim.duration": {"id": "anim-duration", "params": ["fps", "count"]}, "Anim.cell_x": {"id": "anim-cell_x", "params": ["frame", "cols", "cell_w"]}, "Anim.cell_y": {"id": "anim-cell_y", "params": ["frame", "cols", "cell_h"]}, "Anim.play": {"id": "anim-play", "params": ["entity", "clip"]}, "Anim.clip": {"id": "anim-clip", "params": ["name", "frames", "fps", "mode"]}, "Anim.on_frame": {"id": "anim-on_frame", "params": ["entity", "frame"]}, "Anim.fired": {"id": "anim-fired", "params": ["entity"]}, "Motion.to": {"id": "motion-to", "params": ["entity", "dur", "ease"]}, "Tween.progress": {"id": "tween-progress", "params": ["timer", "duration"]}, "Tween.loop": {"id": "tween-loop", "params": ["timer", "duration"]}, "Tween.yoyo": {"id": "tween-yoyo", "params": ["timer", "duration"]}, "Tween.done": {"id": "tween-done", "params": ["timer", "duration"]}, "Tween.ease": {"id": "tween-ease", "params": ["t", "mode"]}, "Tween.number": {"id": "tween-number", "params": ["from", "to", "t"]}, "Tween.round": {"id": "tween-round", "params": ["from", "to", "t"]}, "Tween.point": {"id": "tween-point", "params": ["from", "to", "t"]}, "Tween.tint": {"id": "tween-tint", "params": ["from", "to", "t"]}, "Tween.to": {"id": "tween-to", "params": ["dur", "ease"]}, "Tween.chain": {"id": "tween-chain", "params": ["handle", "to", "dur", "ease"]}, "Tween.delay": {"id": "tween-delay", "params": ["handle", "ticks"]}, "Tween.value": {"id": "tween-value", "params": ["handle"]}, "Tween.stop": {"id": "tween-stop", "params": ["handle"]}, "Tween.parallel": {"id": "tween-parallel", "params": []}, "Query.count": {"id": "query-count", "params": ["prop"]}, "Query.first": {"id": "query-first", "params": ["prop"]}, "Query.nearest": {"id": "query-nearest", "params": ["prop", "pos"]}, "Query.within": {"id": "query-within", "params": ["prop", "pos", "radius"]}, "Reflect.prop": {"id": "reflect-prop", "params": ["name"]}, "Reflect.field": {"id": "reflect-field", "params": ["prop", "name"]}, "Reflect.prop_count": {"id": "reflect-prop_count", "params": []}, "Reflect.prop_name": {"id": "reflect-prop_name", "params": ["index"]}, "Reflect.field_count": {"id": "reflect-field_count", "params": ["prop"]}, "Reflect.field_name": {"id": "reflect-field_name", "params": ["prop", "index"]}, "Reflect.field_type": {"id": "reflect-field_type", "params": ["prop", "index"]}, "Reflect.get": {"id": "reflect-get", "params": ["entity", "prop", "field"]}, "Reflect.set": {"id": "reflect-set", "params": ["entity", "prop", "field", "value"]}, "Reflect.has": {"id": "reflect-has", "params": ["entity", "prop"]}, "Reflect.kind": {"id": "reflect-kind", "params": ["entity"]}, "Reflect.model": {"id": "reflect-model", "params": ["name"]}, "Reflect.serialize": {"id": "reflect-serialize", "params": []}, "Reflect.apply": {"id": "reflect-apply", "params": []}, "Camera.set": {"id": "camera-set", "params": []}, "Camera.follow": {"id": "camera-follow", "params": ["lerp"]}, "Camera.shake": {"id": "camera-shake", "params": ["amount"]}, "Camera.zoom": {"id": "camera-zoom", "params": ["scale"]}, "Camera.shake_for": {"id": "camera-shake_for", "params": []}, "Light.ambient": {"id": "light-ambient", "params": ["color"]}, "Light.point": {"id": "light-point", "params": ["radius", "color", "energy"]}, "Light.occlude": {"id": "light-occlude", "params": []}, "Light.clear_occluders": {"id": "light-clear_occluders", "params": []}, "Light.spot": {"id": "light-spot", "params": ["radius", "color", "energy", "direction", "spread"]}, "Light.falloff": {"id": "light-falloff", "params": ["exponent"]}, "Light.soft": {"id": "light-soft", "params": ["radius"]}, "Light.gel": {"id": "light-gel", "params": ["color"]}, "Light.clear_gel": {"id": "light-clear_gel", "params": []}, "Light.normal": {"id": "light-normal", "params": []}, "Light.clear_normals": {"id": "light-clear_normals", "params": []}, "Light.height": {"id": "light-height", "params": ["height"]}, "Light.time_of_day": {"id": "light-time_of_day", "params": ["t"]}, "Sprite.sheet": {"id": "sprite-sheet", "params": []}, "Sprite.cell": {"id": "sprite-cell", "params": []}, "Sprite.cell_span": {"id": "sprite-cell_span", "params": []}, "Sprite.define": {"id": "sprite-define", "params": []}, "Sprite.named": {"id": "sprite-named", "params": []}, "Sprite.draw": {"id": "sprite-draw", "params": []}, "Sprite.draw_scaled": {"id": "sprite-draw_scaled", "params": []}, "Sprite.width": {"id": "sprite-width", "params": []}, "Sprite.height": {"id": "sprite-height", "params": []}, "Sprite.strip": {"id": "sprite-strip", "params": []}, "Sprite.draw_meter": {"id": "sprite-draw_meter", "params": []}, "Assets.image": {"id": "assets-image", "params": []}, "Assets.load": {"id": "assets-load", "params": []}, "Assets.get": {"id": "assets-get", "params": []}, "Assets.enqueue": {"id": "assets-enqueue", "params": []}, "Assets.pump": {"id": "assets-pump", "params": []}, "Assets.total": {"id": "assets-total", "params": []}, "Assets.loaded": {"id": "assets-loaded", "params": []}, "Assets.ready": {"id": "assets-ready", "params": []}, "Assets.font": {"id": "assets-font", "params": []}, "Assets.progress": {"id": "assets-progress", "params": []}, "Assets.enqueue_dir": {"id": "assets-enqueue_dir", "params": []}, "Pool.live": {"id": "pool-live", "params": []}, "Pool.free": {"id": "pool-free", "params": []}, "Pool.reserved": {"id": "pool-reserved", "params": []}, "Pool.capacity": {"id": "pool-capacity", "params": []}, "Value.null": {"id": "value-null", "params": []}, "Value.int": {"id": "value-int", "params": []}, "Value.fixed": {"id": "value-fixed", "params": []}, "Value.bool": {"id": "value-bool", "params": []}, "Value.str": {"id": "value-str", "params": []}, "Value.list": {"id": "value-list", "params": []}, "Value.object": {"id": "value-object", "params": []}, "Value.add": {"id": "value-add", "params": []}, "Value.put": {"id": "value-put", "params": []}, "Value.get": {"id": "value-get", "params": []}, "Value.has": {"id": "value-has", "params": []}, "Value.at": {"id": "value-at", "params": []}, "Value.key_at": {"id": "value-key_at", "params": []}, "Value.count": {"id": "value-count", "params": []}, "Value.kind": {"id": "value-kind", "params": []}, "Value.as_int": {"id": "value-as_int", "params": []}, "Value.as_str": {"id": "value-as_str", "params": []}, "Json.encode": {"id": "json-encode", "params": []}, "Json.parse": {"id": "json-parse", "params": []}, "Job.defer": {"id": "job-defer", "params": []}, "Job.run": {"id": "job-run", "params": []}, "Job.fulfill": {"id": "job-fulfill", "params": []}, "Job.fail": {"id": "job-fail", "params": []}, "Job.cancel": {"id": "job-cancel", "params": []}, "Job.pump": {"id": "job-pump", "params": []}, "Job.done": {"id": "job-done", "params": []}, "Job.ok": {"id": "job-ok", "params": []}, "Job.failed": {"id": "job-failed", "params": []}, "Job.cancelled": {"id": "job-cancelled", "params": []}, "Job.result": {"id": "job-result", "params": []}, "Job.error": {"id": "job-error", "params": []}, "Job.pending": {"id": "job-pending", "params": []}, "Job.free": {"id": "job-free", "params": []}, "Job.parallel_for": {"id": "job-parallel_for", "params": []}, "Job.is_worker": {"id": "job-is_worker", "params": []}, "Promise.all": {"id": "promise-all", "params": []}, "Promise.race": {"id": "promise-race", "params": []}, "Promise.count_done": {"id": "promise-count_done", "params": []}, "Promise.all_done": {"id": "promise-all_done", "params": []}, "Sync.mutex": {"id": "sync-mutex", "params": []}, "Sync.lock": {"id": "sync-lock", "params": []}, "Sync.unlock": {"id": "sync-unlock", "params": []}, "Sync.try_lock": {"id": "sync-try_lock", "params": []}, "Sync.atomic": {"id": "sync-atomic", "params": []}, "Sync.get": {"id": "sync-get", "params": []}, "Sync.set": {"id": "sync-set", "params": []}, "Sync.add": {"id": "sync-add", "params": []}, "Sync.cas": {"id": "sync-cas", "params": []}, "Sync.channel": {"id": "sync-channel", "params": []}, "Sync.send": {"id": "sync-send", "params": []}, "Sync.recv": {"id": "sync-recv", "params": []}, "Sync.can_recv": {"id": "sync-can_recv", "params": []}, "Sync.len": {"id": "sync-len", "params": []}, "Sync.cpu_count": {"id": "sync-cpu_count", "params": []}, "Xml.parse": {"id": "xml-parse", "params": []}, "Xml.tag": {"id": "xml-tag", "params": []}, "Xml.text": {"id": "xml-text", "params": []}, "Xml.attr": {"id": "xml-attr", "params": []}, "Xml.attr_int": {"id": "xml-attr_int", "params": []}, "Xml.has": {"id": "xml-has", "params": []}, "Xml.attr_count": {"id": "xml-attr_count", "params": []}, "Xml.child_count": {"id": "xml-child_count", "params": []}, "Xml.child": {"id": "xml-child", "params": []}, "Xml.find": {"id": "xml-find", "params": []}, "Xml.count": {"id": "xml-count", "params": []}, "Base64.decode": {"id": "base64-decode", "params": []}, "Base64.encode": {"id": "base64-encode", "params": []}, "Tiled.read": {"id": "tiled-read", "params": []}, "Tiled.read_tsx": {"id": "tiled-read_tsx", "params": []}, "Tiled.load": {"id": "tiled-load", "params": []}, "Tiled.gid": {"id": "tiled-gid", "params": []}, "Tiled.resolve": {"id": "tiled-resolve", "params": []}, "Tiled.width": {"id": "tiled-width", "params": []}, "Tiled.height": {"id": "tiled-height", "params": []}, "Tiled.layer_count": {"id": "tiled-layer_count", "params": []}, "Tiled.layer_name": {"id": "tiled-layer_name", "params": []}, "Tiled.draw": {"id": "tiled-draw", "params": []}, "Tiled.project": {"id": "tiled-project", "params": []}, "Tiled.tree": {"id": "tiled-tree", "params": []}, "Tiled.tile_prop": {"id": "tiled-tile_prop", "params": []}, "Tiled.collide": {"id": "tiled-collide", "params": []}, "Tiled.collision_kind": {"id": "tiled-collision_kind", "params": []}, "Tiled.tile_shapes": {"id": "tiled-tile_shapes", "params": []}, "Tiled.draw_anim": {"id": "tiled-draw_anim", "params": []}, "Tiled.frame_gid": {"id": "tiled-frame_gid", "params": []}, "Tiled.animated": {"id": "tiled-animated", "params": []}, "Tiled.object_count": {"id": "tiled-object_count", "params": []}, "Tiled.object": {"id": "tiled-object", "params": []}, "Tiled.object_shape": {"id": "tiled-object_shape", "params": []}, "Tiled.prop": {"id": "tiled-prop", "params": []}, "Tiled.prop_int": {"id": "tiled-prop_int", "params": []}, "Tiled.prop_type": {"id": "tiled-prop_type", "params": []}, "Tiled.load_types": {"id": "tiled-load_types", "params": []}, "Tiled.template": {"id": "tiled-template", "params": []}, "Tiled.spawn": {"id": "tiled-spawn", "params": []}, "Tiled.spawn_layer": {"id": "tiled-spawn_layer", "params": []}, "Tiled.cell_x": {"id": "tiled-cell_x", "params": []}, "Tiled.cell_y": {"id": "tiled-cell_y", "params": []}, "Tiled.layer_kind": {"id": "tiled-layer_kind", "params": []}, "Tiled.layer_opacity": {"id": "tiled-layer_opacity", "params": []}, "Tiled.layer_tint": {"id": "tiled-layer_tint", "params": []}, "Tiled.layer_offsetx": {"id": "tiled-layer_offsetx", "params": []}, "Tiled.layer_offsety": {"id": "tiled-layer_offsety", "params": []}, "Tiled.world": {"id": "tiled-world", "params": []}, "Tiled.world_count": {"id": "tiled-world_count", "params": []}, "Tiled.world_map": {"id": "tiled-world_map", "params": []}, "Ui.build": {"id": "ui-build", "params": []}, "Ui.open": {"id": "ui-open", "params": []}, "Ui.close": {"id": "ui-close", "params": []}, "Ui.tick": {"id": "ui-tick", "params": []}, "Ui.clicked": {"id": "ui-clicked", "params": []}, "Ui.set_text": {"id": "ui-set_text", "params": []}, "Ui.render": {"id": "ui-render", "params": []}, "File.open": {"id": "file-open", "params": []}, "File.read": {"id": "file-read", "params": []}, "File.write": {"id": "file-write", "params": []}, "File.seek": {"id": "file-seek", "params": []}, "File.tell": {"id": "file-tell", "params": []}, "File.close": {"id": "file-close", "params": []}, "Font.load": {"id": "font-load", "params": []}, "Fx.sparks": {"id": "fx-sparks", "params": []}, "Fx.number": {"id": "fx-number", "params": []}, "Fx.clear": {"id": "fx-clear", "params": []}, "Prefab.spawn": {"id": "prefab-spawn", "params": []}, "Prefab.spawn_at": {"id": "prefab-spawn_at", "params": []}, "App.splash_hide": {"id": "app-splash_hide", "params": []}, "App.set_icon": {"id": "app-set_icon", "params": ["path"]}, "App.window_hide": {"id": "app-window_hide", "params": []}, "App.window_show": {"id": "app-window_show", "params": []}}, "annotations": {"@Queries": "annot-queries", "@Computed": "annot-computed", "@On": "annot-on", "@export": "annot-export", "@Sync": "annot-sync", "@Handles": "annot-handles", "@OnAttach": "annot-onattach", "@OnDespawn": "annot-ondespawn", "@OnDetach": "annot-ondetach", "@OnDisable": "annot-ondisable", "@OnEnable": "annot-onenable", "@OnQuit": "annot-onquit", "@OnSpawn": "annot-onspawn", "@OnStart": "annot-onstart", "@Owned": "annot-owned", "@Predicted": "annot-predicted", "@Public": "annot-public", "@Reads": "annot-reads", "@Server": "annot-server", "@ToClients": "annot-toclients", "@ToServer": "annot-toserver", "@Writes": "annot-writes", "@EngineSystem": "annot-enginesystem", "@Namespace": "annot-namespace", "\"@ClearColor\"": "annot-clearcolor", "@System": "annot-system"}, "namespaces": {"Angle": "ns-angle", "Anim": "ns-anim", "App": "ns-app", "Assets": "ns-assets", "Audio": "ns-audio", "Base64": "ns-base64", "BigInt": "ns-bigint", "Camera": "ns-camera", "Clock": "ns-clock", "Collision": "ns-collision", "Color": "ns-color", "Crypto": "ns-crypto", "Date": "ns-date", "DateTime": "ns-datetime", "Decimal": "ns-decimal", "Dict": "ns-dict", "Duration": "ns-duration", "Ease": "ns-ease", "File": "ns-file", "Font": "ns-font", "Fs": "ns-fs", "Fx": "ns-fx", "Grid": "ns-grid", "Hash": "ns-hash", "Http": "ns-http", "Huge": "ns-huge", "IVec2": "ns-ivec2", "Input": "ns-input", "Job": "ns-job", "Json": "ns-json", "Light": "ns-light", "List": "ns-list", "Log": "ns-log", "Map": "ns-map", "Math": "ns-math", "Memory": "ns-memory", "Mime": "ns-mime", "Motion": "ns-motion", "Network": "ns-network", "Noise": "ns-noise", "Os": "ns-os", "Path": "ns-path", "Percent": "ns-percent", "Pool": "ns-pool", "Prefab": "ns-prefab", "Process": "ns-process", "Promise": "ns-promise", "Query": "ns-query", "Random": "ns-random", "Rect": "ns-rect", "Reflect": "ns-reflect", "Regex": "ns-regex", "Save": "ns-save", "Screen": "ns-screen", "Set": "ns-set", "Sprite": "ns-sprite", "Sync": "ns-sync", "System": "ns-system", "Text": "ns-text", "Tiled": "ns-tiled", "Time": "ns-time", "Tween": "ns-tween", "Udp": "ns-udp", "Ui": "ns-ui", "Unicode": "ns-unicode", "Uuid": "ns-uuid", "Value": "ns-value", "Vector": "ns-vector", "World": "ns-world", "Xml": "ns-xml"}, "tips": {"program": "The top-level unit — one program compiles to one native game.", "property": "A named record of typed fields — a per-model component, or a plain heap record.", "model": "A named kind of thing — a fixed bundle of properties you spawn by one name.", "handler": "A named block the engine runs each frame during phase P.", "phase": "Names which stage of the frame a handler runs in.", "const": "A compile-time constant, folded into the code with no storage.", "var": "A mutable binding — at program scope, your game's persistent named state.", "let": "An immutable binding — the default choice for a value that never changes.", "function": "A function — reusable logic called positionally or with named arguments.", "fn": "A top-level function named as a value - the entry point handed to Job.parallel_for.", "prefab": "A model with preset component fields — spawn it, override what varies.", "return": "Hand a value back from a function and stop running it.", "entry": "The program's main block — runs once, top to bottom.", "import": "Splice another Ludic file's declarations into this program.", "public": "Expose a declaration's lifecycle on the public event bus.", "extern": "Bind a name to an external native symbol — the seam for platform and library calls.", "new": "Allocate a property record or an empty slice on the heap.", "namespace": "Group functions into a Name.* namespace and control its public surface.", "enum": "A named set of integer constants — names for a magic-number space.", "ui": "Declare a retained widget tree as data; the engine lays it out and draws it.", "spawn": "Create a model instance carrying the listed properties, seeding their fields.", "despawn": "Remove a model instance from the world, firing any @OnDespawn hooks.", "query": "Match every model instance carrying all the listed properties.", "self": "The model instance currently bound by the enclosing query loop.", "enable": "Re-activate a disabled property, model, or handler — its data is intact.", "disable": "Deactivate a property, model, or handler without destroying its data.", "attach": "Structurally add a property to a live model instance, seeding its fields.", "detach": "Structurally remove a property from a live model instance.", "world_attach_dyn": "Attach a property to an instance by numeric id at runtime (dynamic ECS).", "world_count": "The total number of live model instances in the world.", "world_detach_dyn": "Detach a property from an instance by numeric id at runtime (dynamic ECS).", "world_field_id": "Resolve a field name within a property to its numeric index.", "world_get": "Read one field of a model instance by numeric id (reflection ABI).", "world_has": "Test whether a model instance currently carries a property.", "world_kind": "The model id of an instance — which kind of thing it is.", "world_load": "Restore the whole world from a serialized byte buffer.", "world_model_id": "Resolve a model's name to its stable numeric id.", "world_prop_id": "Resolve a property's name to its stable numeric id.", "world_query_next": "Step to the next instance carrying a property, walking the world by id.", "world_register_prop": "Register a brand-new property at runtime and get its id (dynamic ECS).", "world_save": "Serialize the whole world into a buffer; returns the number of bytes written.", "world_set": "Write one field of a model instance by numeric id (reflection ABI).", "world_size": "The number of live model instances in the world.", "world_spawn": "Spawn an instance of a model chosen by numeric id at runtime.", "if": "A branch — run one block when a condition holds, another when it doesn't.", "else": "A branch — run one block when a condition holds, another when it doesn't.", "while": "Loop as long as a condition holds, re-checking it before each pass.", "for": "Range loop — iterate the half-open range from a up to but not including b.", "in": "The part of a for loop that names what to iterate — a range or a query.", "match": "Multi-way branch on one value, matching one or more literals per arm.", "machine": "An explicit state machine over an int var — dispatches on the store's value.", "state": "One state of a machine — its body runs while the machine sits in it.", "become": "Transition to another state of the enclosing machine, or to another scene.", "break": "Leave the enclosing loop immediately.", "continue": "Skip to the next iteration of the loop.", "where": "Filter a query loop to entities that satisfy a condition.", "try": "Recover a fallible result as a value, with a fallback — no exceptions, no unwinding.", "scene": "A mutually-exclusive game state — a title screen, the overworld, a battle.", "layer": "A named group of handlers inside a scene; layers render in declaration order.", "on": "Scene lifecycle hooks that fire as a scene becomes active or is left.", "enter": "The scene-entry hook — runs once as a scene becomes active.", "exit": "Terminate the process immediately with a status code.", "start": "Marks the one scene the game begins in.", "event": "Declare a public event carrying a flat payload that listeners react to.", "emit": "Fire an event, running every listener; as an expression it yields the veto flag.", "cancel": "Inside a listener, veto the cancellable event being emitted.", "cancellable": "Marks an event whose listeners may veto it with cancel.", "Screen.clear": "Fill the whole framebuffer with one color to start a fresh frame.", "Screen.fill_rectangle": "Draw a solid, filled rectangle at a pixel position.", "Screen.draw_rectangle": "Draw a one-pixel-thick rectangle outline (not filled).", "Screen.put_pixel": "Set a single pixel at a pixel coordinate to one color.", "Screen.draw_text": "Draw a string with the built-in font at an integer scale.", "Screen.draw_number": "Draw an integer directly, with no string allocation or conversion.", "Screen.show": "Present the finished frame — copy everything drawn this frame to the window.", "Screen.width": "The framebuffer width in pixels.", "Screen.height": "The framebuffer height in pixels.", "Screen.status": "Set the persistent one-line status/HUD string shown by the runtime.", "Screen.line": "Draw a straight line between two points.", "Screen.circle": "Draw a circle outline.", "Screen.fill_circle": "Draw a filled disc.", "Screen.triangle": "Draw a triangle outline.", "Screen.fill_triangle": "Draw a filled triangle.", "Screen.sprite": "Blit a sprite at its natural size.", "Screen.sprite_scaled": "Blit a sprite at an integer scale.", "Screen.oval": "Draw an axis-aligned ellipse outline.", "Screen.camera": "Set the world-space camera offset for the draw path.", "Screen.clip": "Restrict drawing to a screen-space rectangle.", "Screen.clip_reset": "Lift the clip rectangle (draw to the whole screen again).", "Screen.blend_mode": "Choose replace or additive pixel blending.", "Screen.measure_text": "The pixel advance width of text in the built-in font.", "Screen.pixel": "Read a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.", "Screen.bar": "A filled meter: value of max over a track.", "Clock.now": "The current simulated instant.", "Clock.set": "Set the clock to an instant.", "Clock.advance": "Move the clock forward by a Duration.", "Clock.reset": "Reset the clock to the epoch (0).", "Collision.rects": "Do two rectangles overlap?", "Collision.point_rect": "Is a point inside a rectangle?", "Collision.circles": "Do two circles overlap?", "Collision.rect_circle": "Does a rectangle overlap a circle?", "Color.rgb": "Build a color from red, green, blue.", "Color.rgba": "Build a color with an alpha byte.", "Color.lerp": "Blend between two colors.", "Color.darken": "Scale a color toward black.", "Color.lighten": "Scale a color toward white.", "Color.with_alpha": "Replace a color's alpha byte.", "Crypto.sha256": "SHA-256 of a string, as 64 hex characters.", "Crypto.hmac_sha256": "Sign a message with a shared secret key.", "Crypto.verify_hmac": "Constant-time check that a MAC matches.", "Crypto.hex": "Lowercase hex of a string's bytes.", "Crypto.ct_equal": "Constant-time string equality for secrets.", "Crypto.random_bytes": "n bytes from the OS CSPRNG, as a 2n-character hex string.", "Crypto.random_hex": "Alias for random_bytes — n secure bytes as a 2n-char hex string.", "Crypto.random_u32": "One CSPRNG-drawn 32-bit integer.", "Crypto.base64": "Standard base64 (RFC 4648) of a string's bytes.", "Date.new": "A calendar day as days-since-1970.", "Date.year": "The calendar year of a date.", "Date.month": "The month (1-12) of a date.", "Date.day": "The day of the month (1-31) of a date.", "Date.weekday": "Day of the week, 0=Sunday..6=Saturday.", "Date.is_leap": "True if the year is a leap year.", "Date.days_in_month": "Number of days in a given month.", "Date.to_epoch": "Midnight UTC of the day, as a DateTime.", "Date.add_days": "The date n days later (n may be negative).", "Date.diff_days": "Whole days from b to a (a - b).", "DateTime.from": "Build an instant from its calendar parts.", "DateTime.date": "The calendar day the instant falls on.", "DateTime.add": "The instant `span` seconds later.", "DateTime.year": "The calendar year of an instant.", "DateTime.month": "The month (1-12) of an instant.", "DateTime.day": "The day of the month (1-31) of an instant.", "DateTime.weekday": "Day of the week, 0=Sunday..6=Saturday.", "DateTime.hour": "The hour of day (0-23) of an instant.", "DateTime.minute": "The minute of the hour (0-59) of an instant.", "DateTime.second": "The second of the minute (0-59) of an instant.", "DateTime.format": "Render an instant as text using a token pattern.", "DateTime.parse": "Parse text into an instant; -1 on failure.", "Duration.seconds": "A span of n whole seconds.", "Duration.minutes": "A span of n minutes, in seconds.", "Duration.hours": "A span of n hours, in seconds.", "Duration.days": "A span of n days, in seconds.", "Duration.as_seconds": "The span in whole seconds.", "Duration.as_minutes": "The span in whole minutes.", "Duration.as_hours": "The span in whole hours.", "Duration.as_days": "The span in whole days.", "Ease.in": "Accelerate from rest (quadratic ease-in).", "Ease.out": "Decelerate to rest (quadratic ease-out).", "Ease.in_out": "Ease in and then out (smoothstep).", "Ease.back": "Ease in with a small backward anticipation.", "Ease.bounce": "Ease out with a settling bounce.", "Ease.elastic": "Ease out with a springy wobble.", "Fs.exists": "Does a path exist?", "Fs.is_dir": "Is a path a directory?", "Fs.read_text": "Read a whole file as a string.", "Fs.write_text": "Write a string to a file (atomically).", "Fs.append_text": "Append a string to a file.", "Fs.remove": "Delete a file.", "Fs.size": "File size in bytes.", "Fs.mkdir": "Create a directory and its parents.", "Fs.copy": "Copy a file byte-for-byte.", "Fs.list": "Directory entries, sorted.", "Hash.of": "The fast default string hash.", "Hash.fnv1a": "FNV-1a 32-bit, named explicitly.", "Hash.crc32": "CRC-32 checksum for corruption detection.", "Hash.mix": "Avalanche one integer into a well-scrambled value.", "Hash.combine": "Fold several ints into one deterministic seed.", "Hash.of64": "The fast default 64-bit string hash.", "Hash.fnv1a_64": "FNV-1a 64-bit, named explicitly.", "Hash.mix64": "Avalanche one 64-bit integer into a well-scrambled value.", "Input.key": "The key pressed this frame as a character code (0 when nothing is pressed).", "Input.bind": "Bind a physical key to a named action, so gameplay reads the action, not the key.", "Input.rebind": "Remap an action from one key to another at runtime (rebinding menus).", "Input.poll": "Advance one frame of input; the single per-frame read behind actions and replay.", "Input.down": "Is a named action held on the frame last polled?", "Input.pressed": "Did a named action go down this frame (a one-shot edge)?", "Input.record": "Start recording polled input each frame (for deterministic replay).", "Input.replay": "Replay recorded input; poll then reads the tape, not the device.", "Input.key_down": "Is a physical key held this frame?", "Input.key_pressed": "Did a key go down this frame (edge)?", "Input.key_released": "Did a key go up this frame (edge)?", "Input.key_label": "The name to show a player for a key - in their own keyboard layout.", "Input.press": "Inject a held key (AI, tutorial, testing, network).", "Input.release": "Release an injected key.", "Input.axis": "A -1..+1 axis from two keys.", "Input.axis_i": "A -1/0/1 directional intent as a plain int, no bool->int glue.", "Input.vector": "A normalized 2D vector from four keys.", "Input.strength": "0..1 strength of a named action.", "Input.mouse_x": "The mouse x position this frame.", "Input.mouse_y": "The mouse y position this frame.", "Input.mouse_dx": "Mouse x movement since the last poll.", "Input.mouse_dy": "Mouse y movement since the last poll.", "Input.mouse_down": "Is a mouse button held?", "Input.wheel": "Scroll-wheel delta this frame.", "Input.set_mouse": "Inject the mouse state (headless / AI / testing).", "Input.pad_connected": "Is a gamepad connected?", "Input.pad_button": "Is a gamepad button held?", "Input.pad_axis": "A gamepad analog axis, -1..+1.", "Input.set_pad": "Inject a gamepad's whole state.", "Input.touch_count": "How many touch points are active?", "Input.touch_x": "The x of a touch point.", "Input.touch_y": "The y of a touch point.", "Input.set_touch": "Inject a touch point.", "Input.action": "Register a default key binding for an action (kept if already bound).", "Input.bind_pad": "Also fire a named action from a gamepad button (device-agnostic).", "Input.active": "Is a named action active right now (any bound key held, or pad button down)?", "Input.just_pressed": "Did a named action go active this frame (the deterministic on-press)?", "Input.just_released": "Did a named action go inactive this frame (the on-release edge)?", "Input.cursor_mode": "Hide / lock / confine the OS mouse cursor to the window (windowed).", "Input.move_i": "The standard top-down movement intent, -1/0/1 per axis.", "List.len": "The number of elements in a slice.", "List.push": "Append an element to the end of a slice.", "List.clear": "Remove all elements, keeping capacity.", "List.first": "The first element of a slice.", "List.last": "The last element of a slice.", "List.pop": "Remove and return the last element.", "List.swap": "Exchange the elements at two indices.", "List.contains": "Whether a value appears in a slice.", "List.index_of": "The index of a value in a slice, or -1 if absent.", "List.reverse": "Reverse the order of a slice in place.", "List.insert": "Insert an element at an index, shifting the rest up.", "List.remove_at": "Remove the element at an index, shifting the rest down.", "List.remove": "Remove the first element equal to a value.", "List.sort": "Sort a slice in ascending order, in place.", "List.sort_by": "Sort a slice ascending by a key each element maps to.", "List.sort_desc_by": "Sort a slice descending by a key each element maps to.", "List.sort_with": "Sort a slice with a full two-argument comparator.", "List.sample": "Random picks from an int slice, distinct while it can.", "Log.trace": "Log at the trace level (0) — the most verbose.", "Log.debug": "Log at the debug level (1) — development detail.", "Log.info": "Log at the info level (2) — normal operation.", "Log.warn": "Log at the warn level (3) — something looks wrong.", "Log.error": "Log at the error level (4) — a failure.", "Log.set_level": "Show only messages at level n or above (0 = all).", "Log.level": "The current logging threshold.", "Math.min": "The smaller of two values.", "Math.max": "The larger of two values.", "Math.abs": "The magnitude of a value, dropping its sign.", "Math.clamp": "Constrain a value to the range [lo, hi].", "Math.sign": "The sign of a value as -1, 0, or 1.", "Math.floor": "Round a fixed value down to the nearest whole int.", "Math.ceil": "Round a fixed value up to the nearest whole int.", "Math.round": "Round a fixed value to the nearest whole int.", "Math.lerp": "Blend between two values by a 0..1 amount.", "Math.inverse_lerp": "Find where a value sits between two endpoints as a 0..1 fraction.", "Math.remap": "Rescale a value from one range into another.", "Math.sqrt": "The square root of a fixed value.", "Math.sin": "Sine of an angle in radians.", "Math.cos": "Cosine of an angle in radians.", "Math.tan": "Tangent of an angle in radians.", "Math.hypot": "Length of the vector (x, y).", "Math.dist": "Distance between two points.", "Math.dist2": "Squared distance between two points.", "Math.deg_to_rad": "Convert degrees to radians.", "Math.rad_to_deg": "Convert radians to degrees.", "Math.posmod": "Modulo that is always non-negative.", "Math.wrap": "Wrap a value into the range [lo, hi).", "Math.ping_pong": "Bounce a counter back and forth in [0, len].", "Math.snapped": "Round a value to the nearest multiple of step.", "Math.move_toward": "Step from one value toward another by at most delta.", "Math.smoothstep": "A smooth 0..1 ramp between two edges.", "Math.atan2": "The angle of the vector (x, y), in radians.", "Math.asin": "The arcsine of a value, in radians.", "Math.acos": "The arccosine of a value, in radians.", "Math.exp": "e raised to the power x.", "Math.log": "The natural logarithm of x.", "Math.pow": "base raised to a fixed exponent.", "Memory.bytes": "Allocate n bytes.", "Memory.words": "Allocate n 32-bit words.", "Memory.copy": "Copy n bytes between buffers.", "Memory.fill": "Set n bytes to a value.", "Memory.peek": "Read one byte.", "Memory.poke": "Write one byte.", "Mime.of": "MIME type from a file extension.", "Mime.sniff": "MIME type refined by magic bytes.", "Network.send": "Put a datagram on the wire.", "Network.poll": "Read an inbound datagram.", "Network.serialize": "Serialize an entity's synced state.", "Network.apply": "Apply serialized state to an entity.", "Network.owner": "The peer that owns an entity.", "Network.set_owner": "Assign ownership of an entity.", "Network.is_server": "Is this peer the server?", "Network.is_owner": "Does this peer own the entity?", "Network.local_id": "This peer's own id.", "Noise.value2": "2D value (lattice) noise, deterministic, in [-1, 1].", "Noise.perlin2": "2D Perlin gradient noise, deterministic, in [-1, 1].", "Noise.simplex2": "2D simplex noise, the organic default, in [-1, 1].", "Noise.fbm2": "Fractal Brownian motion — octaves of simplex, in [-1, 1].", "Noise.cellular2": "Worley (cellular) F1 distance to the nearest cell point.", "Noise.cellular2_id": "The id of the nearest Worley cell — stable per cell.", "Noise.unit": "Remap a [-1,1] noise sample to [0,1].", "Os.args": "Every command-line argument, as a list.", "Os.arg_count": "How many command-line arguments there are.", "Os.arg": "The i-th command-line argument.", "Os.env": "Read an environment variable (null if unset).", "Os.env_or": "Read an environment variable, or a fallback.", "Os.has_env": "Is an environment variable set?", "Os.set_env": "Set an environment variable.", "Os.unset_env": "Remove an environment variable.", "Os.exit": "Terminate the process with a status code.", "Os.platform": "The host platform id.", "Os.arch": "The host CPU architecture.", "Os.stdout_write": "Write a string to standard output.", "Os.stderr_write": "Write a string to standard error.", "Os.save_dir": "Per-user save directory for an app.", "Os.config_dir": "Per-user config directory for an app.", "Os.cache_dir": "Per-user cache directory for an app.", "Os.temp_dir": "The system temporary directory.", "Os.pid": "This process's id.", "Path.join": "Join two path segments with the separator.", "Path.dir": "The directory part of a path.", "Path.base": "The final component of a path.", "Path.ext": "The file extension, including the dot.", "Path.stem": "The final component without its extension.", "Path.normalize": "Collapse . / .. / duplicate separators.", "Save.write": "Save a snapshot of the world.", "Save.read": "Restore the saved snapshot.", "System.run": "Run a shell command.", "System.read_char": "Read one byte from standard input.", "System.file_open": "Open a file.", "System.file_read": "Read bytes from a file.", "System.file_write": "Write bytes to a file.", "System.file_seek": "Move a file's read/write position.", "System.file_tell": "The current file position.", "System.file_close": "Close a file.", "Text.length": "The number of bytes in a string.", "Text.char_at": "The byte value at an index, 0..255.", "Text.slice": "A substring covering bytes [a, b).", "Text.starts_with": "Whether a string begins with a given prefix.", "Text.ends_with": "Whether a string ends with a given suffix.", "Text.contains": "Whether a substring appears anywhere in a string.", "Text.index_of": "The byte index of a substring, or -1 if absent.", "Text.equals": "Whether two strings have identical bytes.", "Text.concat": "Join two strings into a new one.", "Text.to_int": "Parse a leading integer from a string.", "Text.from_int": "Render an integer as a string.", "Text.upper": "An uppercased copy of a string.", "Text.lower": "A lowercased copy of a string.", "Text.trim": "A copy with surrounding whitespace removed.", "Text.repeat": "A string repeated n times.", "Text.pad_left": "Pad with spaces on the left to a width.", "Text.pad_right": "Pad with spaces on the right to a width.", "Text.split": "Split a string on a separator into a list.", "Text.join": "Join a list of strings with a separator.", "Text.replace": "Replace every occurrence of a substring.", "Time.frame": "The number of frames elapsed since start.", "Time.delta": "Seconds per frame (a fixed 1/60).", "Time.elapsed": "Seconds since start (deterministic).", "Time.now": "Wall-clock seconds (non-deterministic).", "Time.since": "Seconds elapsed from a past instant to now (non-deterministic).", "Unicode.len": "Number of code points in a string (not bytes).", "Unicode.byte_len": "Number of bytes in a string.", "Unicode.is_valid_utf8": "Is a string well-formed UTF-8?", "Unicode.char_at": "The i-th code point of a string.", "Unicode.chars": "Every code point of a string, in order.", "Unicode.upper": "Uppercase a string.", "Unicode.lower": "Lowercase a string.", "Unicode.truncate": "First n code points of a string.", "Unicode.grapheme_len": "Number of user-perceived characters.", "Uuid.new": "A new random (v4) UUID as a canonical 36-char string.", "Uuid.v4": "Explicit alias for Uuid.new — a random (v4) UUID.", "Uuid.new_v7": "A new time-ordered (v7) UUID; sorts by creation time.", "Uuid.v7": "Explicit alias for Uuid.new_v7 — a time-ordered UUID.", "Uuid.parse": "Normalise an untrusted string to a lowercase UUID, or the nil UUID.", "Uuid.is_valid": "Is s a well-formed UUID string?", "Uuid.to_text": "The canonical text form of a UUID.", "Uuid.equals": "Case-insensitive UUID equality.", "Uuid.nil": "The all-zero UUID.", "World.get": "Read a component field by id.", "World.set": "Write a component field by id.", "World.has": "Does an entity have a component?", "World.count": "The number of live entities.", "World.size": "The serialized size of the world.", "World.spawn": "Spawn an entity of a model by id.", "World.save": "Serialize the world into a buffer.", "World.load": "Restore the world from a buffer.", "World.prop_id": "Look up a component's id by name.", "World.field_id": "Look up a field's id by name.", "World.model_id": "Look up a model's id by name.", "World.kind": "The model id of an entity.", "World.register_prop": "Register a new component at runtime.", "World.attach": "Attach a component to an entity at runtime.", "World.detach": "Detach a component from an entity at runtime.", "World.query_next": "Advance a reflective query.", "World.despawn": "Despawn an entity by id — runs its @OnDespawn hooks and frees the slot.", "Audio.load": "Load a sound file into a handle.", "Audio.play": "Fire a one-shot sound.", "Audio.play_at": "Fire a one-shot with its own gain, pitch and stereo position.", "Audio.play_sound": "Fire a one-shot sound (alias of play).", "Audio.play_music": "Loop a sound as background music.", "Audio.stop": "Stop one sound.", "Audio.stop_music": "Stop the music channel.", "Audio.stop_all": "Stop every sound.", "Audio.volume": "Set the master volume.", "Audio.pitch": "Set the playback rate / pitch.", "Audio.is_playing": "Is this sound playing?", "Audio.define": "Load a sound into the bank under a name.", "Audio.named": "The handle registered under a name, or 0.", "Random.range": "A random integer in the inclusive range [low, high].", "Random.chance": "Return true with the given percent probability.", "Random.seed": "Seed the random generator so runs are reproducible.", "Random.value": "A random fixed value in [0, 1).", "Random.int": "A random integer in [0, max).", "Random.sign": "A random +1 or -1.", "Random.weighted": "An index drawn in proportion to its weight.", "Vector.make": "Build a vector from x and y components.", "Vector.zero": "The zero vector, (0, 0).", "Vector.x": "The x component of a vector.", "Vector.y": "The y component of a vector.", "Vector.add": "Component-wise sum a + b.", "Vector.sub": "Component-wise difference a - b.", "Vector.scale": "Scale a vector by a scalar.", "Vector.dot": "The dot product of two vectors.", "Vector.length": "The length (magnitude) of a vector.", "Vector.distance": "The distance between two points.", "Vector.normalize": "A unit vector in the same direction.", "Vector.lerp": "Linear interpolation between two vectors.", "Vector.rotate": "Rotate a vector by an angle in radians.", "Vector.angle": "The angle of a vector in radians.", "Vector.from_angle": "A unit vector pointing at an angle.", "Http.get": "Start an async GET request.", "Http.post": "Start an async POST with a body.", "Http.request": "Start a request with any method.", "Http.open": "Open a request to configure before sending.", "Http.set": "Set a request header before sending.", "Http.body": "Set a string request body.", "Http.body_bytes": "Set a raw byte request body.", "Http.send": "Dispatch an opened request.", "Http.poll": "Poll a request; -1 pending, 0 error, else status.", "Http.status": "The response HTTP status code.", "Http.ok": "Was the response status 2xx?", "Http.text": "The response body as text.", "Http.body_len": "The response body length in bytes.", "Http.header": "A response header value (case-insensitive).", "Http.free": "Release a request's resources.", "Http.parse": "Parse a raw HTTP response into a handle.", "Http.save_to": "Stream the response body to a file.", "Http.received": "Body bytes received so far.", "Http.expected": "The body length the server announced, or -1.", "IVec2.make": "Build an integer vector from x and y components.", "IVec2.zero": "The origin cell, (0, 0).", "IVec2.x": "The x component of an integer vector.", "IVec2.y": "The y component of an integer vector.", "IVec2.add": "Component-wise sum of two integer vectors.", "IVec2.sub": "Component-wise difference of two integer vectors.", "IVec2.scale": "Multiply both components by an integer.", "IVec2.dot": "The dot product ax*bx + ay*by.", "IVec2.equal": "True when both components match.", "IVec2.manhattan": "Grid distance |dx| + |dy|.", "IVec2.to_vector": "Widen to a fixed-point Vector.", "IVec2.distance2": "The squared distance between two points.", "IVec2.within": "Are two points within a radius of each other?", "IVec2.heading": "The direction from one point to another, in degrees.", "IVec2.along": "The point a distance along a heading.", "IVec2.step": "A -1/0/1 unit step along a heading.", "Map.size": "Set the tilemap dimensions in cells before filling rows.", "Map.row": "Fill one row of the tilemap from a string of tile characters.", "Map.tile": "Read the tile character at a cell (out-of-bounds reads answer '#').", "Map.get": "The glyph at a cell ('#' outside the map).", "Map.set": "Write one cell in place.", "Map.fill": "Every cell becomes the glyph.", "Map.rect": "Fill a rectangle of cells.", "Map.border": "The outermost ring of cells.", "Map.random_cell": "A random cell holding the glyph (seeded RNG).", "Map.is_solid": "Is a cell solid, per the Solids config?", "Map.is_solid_at": "Is the cell under a pixel position solid?", "Map.width": "The map's width in cells.", "Map.height": "The map's height in cells.", "Map.random_cell_far": "A random cell with the glyph, at least a distance from a point.", "Map.to_tile": "The tile under a pixel position.", "Rect.make": "Build a rectangle from a corner and a size.", "Rect.x": "The left edge (x position).", "Rect.y": "The top edge (y position).", "Rect.w": "The width.", "Rect.h": "The height.", "Rect.right": "The right edge, x + w.", "Rect.bottom": "The bottom edge, y + h.", "Rect.center": "The center point as a Vector.", "Rect.contains": "True when the point is inside.", "Rect.intersects": "True when two rectangles overlap.", "Udp.open": "Open a socket on a port.", "Udp.port": "The port a socket is bound to.", "Udp.send": "Send one datagram.", "Udp.recv": "Read the next waiting datagram.", "Udp.from_ip": "The sender of the last datagram read.", "Udp.from_port": "The sender's port.", "Udp.close": "Close a socket.", "Udp.resolve": "Look a host name up.", "Udp.local_ip": "This machine's address.", "Udp.ip": "Parse a dotted address.", "Udp.ip_text": "Format an address.", "BigInt.from": "Turn a plain int into a BigInt.", "BigInt.parse": "Parse decimal text into a BigInt.", "BigInt.add": "Exact sum of two big integers.", "BigInt.sub": "Exact difference of two big integers.", "BigInt.mul": "Exact product of two big integers.", "BigInt.neg": "Negate a big integer.", "BigInt.pow": "Raise a big integer to an int power.", "BigInt.div": "Divide a big integer by an int (toward zero).", "BigInt.mod": "Remainder of a big integer divided by an int.", "BigInt.cmp": "Compare two big integers: -1, 0, or 1.", "BigInt.eq": "True when two big integers are equal.", "BigInt.is_zero": "True when a big integer is zero.", "BigInt.to_int": "Narrow a big integer to a plain int (clamped).", "BigInt.str": "Render a big integer as decimal text.", "Process.spawn": "Start a program; never blocks.", "Process.poll": "-1 while running, else the exit code.", "Process.kill": "End a running child.", "Process.free": "Let a process handle go.", "Decimal.from": "Turn a whole int into a Decimal.", "Decimal.parse": "Parse decimal text like \"19.99\".", "Decimal.add": "Exact sum of two decimals.", "Decimal.sub": "Exact difference of two decimals.", "Decimal.mul": "Exact product of two decimals.", "Decimal.neg": "Negate a decimal.", "Decimal.cmp": "Compare two decimals: -1, 0, or 1.", "Decimal.eq": "True when two decimals are equal in value.", "Decimal.scale": "The number of digits after the point.", "Decimal.rescale": "Change precision (truncates toward zero).", "Decimal.str": "Render a decimal as text.", "int": "Convert a number to a whole number, truncating toward zero.", "fixed": "Convert an int (or a float) into a Q16.16 fixed-point value.", "long": "Convert a number to a whole number, truncating toward zero.", "bool": "A truth value — the result of comparisons and of and / or / not.", "float": "Convert any number to a float (or double).", "string": "Convert an int, bool, or fixed value to text; a string passes through unchanged.", "double": "Convert any number to a float (or double).", "entity": "The id/handle of a spawned model instance, as returned by self().", "pointer": "A raw address into memory — a byte buffer from bytes(n), or an FFI handle.", "words": "Allocate a raw buffer of n 32-bit words, indexable with [i].", "byte": "A raw buffer indexed one byte at a time — each buffer[i] reads or writes a byte.", "fixeds": "A buffer of fixed-point values — f[i] reads and writes a fixed.", "countdown": "An int component field the engine steps toward 0 once per Update.", "floats": "A buffer of floats (or doubles) — v[i] reads and writes one.", "doubles": "A buffer of floats (or doubles) — v[i] reads and writes one.", "pointers": "A buffer of pointers — p[i] reads and writes a pointer.", "Vector": "A 2D vector — two fixed components (x, y), copied by value.", "IVec2": "An integer 2D vector — two int components (x, y), copied by value.", "Rect": "A rectangle — position (x, y) and size (w, h), copied by value.", "void": "The absence of a value — the return type of a function that returns nothing.", "Dict.new": "Create an empty string-keyed map.", "Dict.set": "Insert or update a key.", "Dict.get": "Look up a key (0 if absent).", "Dict.get_or": "Look up a key with an explicit default.", "Dict.has": "Is a key present?", "Dict.remove": "Delete a key.", "Dict.size": "How many keys are stored.", "Dict.clear": "Remove every key.", "Dict.keys": "Every key, as a slice of strings.", "and": "The boolean combinators, spelled as words — never && or || or a bare !.", "or": "The boolean combinators, spelled as words — never && or || or a bare !.", "not": "The boolean combinators, spelled as words — never && or || or a bare !.", "print": "Print an int or string followed by a newline — for headless tests and debugging.", "quit": "Stop the game loop cleanly after the current frame finishes.", "save": "Serialize the whole world — every model instance, property, and program var — in one call.", "load": "Restore the world from a snapshot previously written by save().", "len": "The number of elements in a slice, or the byte length of a string.", "push": "Append one element to the end of a growable slice.", "abs": "The absolute value of an integer (its magnitude, never negative).", "clamp": "Constrain a value to the inclusive range [lo, hi].", "panic": "Abort with a located error message instead of crashing.", "assert": "Abort with a located message when an invariant is false.", "ok": "Wrap a success payload in a result — the happy half of try/else.", "err": "Wrap a failure message in a result — the sad half, recovered by try/else.", "is_ok": "True when a result carries a success payload.", "is_err": "True when a result carries a failure.", "some": "Wrap a present value in an option.", "none": "The empty option — a missing value.", "is_some": "True when an option holds a value.", "is_none": "True when an option is empty.", "unwrap_or": "The option value, or a fallback if empty.", "run": "Run a string as a shell command.", "getenv": "Read an environment variable, returning empty when it is unset.", "read_char": "Read one byte from standard input, or -1 at end of input.", "arg": "The i-th command-line argument as a string.", "arg_count": "The number of command-line arguments, counting the program name.", "bytes": "Allocate a raw buffer of n bytes and return a pointer to it.", "file_stderr": "The standard-error stream handle for use with file_write.", "file_stdout": "The standard-output stream handle for use with file_write.", "file_write": "Write a run of raw bytes to a stream handle.", "floor": "Floor a fixed-point value down to the nearest integer.", "max": "The larger of two integers.", "min": "The smaller of two integers.", "ui_build": "Build the declared UI tree so it can be opened and rendered.", "float_bits": "A float's IEEE bit pattern as an int, and back.", "float_from_bits": "A float's IEEE bit pattern as an int, and back.", "double_bits": "A float's IEEE bit pattern as an int, and back.", "double_from_bits": "A float's IEEE bit pattern as an int, and back.", "Set.new": "Create an empty string set.", "Set.add": "Add a member (duplicates ignored).", "Set.has": "Is a member present?", "Set.remove": "Remove a member.", "Set.size": "How many members.", "Set.clear": "Remove every member.", "Set.members": "Every member, as a slice of strings.", "@Queries": "Declare the properties a handler operates on, binding their fields by name in the body.", "@Computed": "A derived field expanded inline wherever it is read, never stored.", "@On": "Register a compile-time listener that runs whenever an event is emitted.", "@export": "Expose a function as a native symbol so a host can call it.", "@Sync": "Mark fields as replicated and models as owned so the compiler generates networking.", "@Handles": "Declarative hint naming the event or subsystem a handler is responsible for.", "@OnAttach": "Run a handler when a property is structurally attached to a live instance.", "@OnDespawn": "Run a handler when a model instance is torn down, optionally knowing why.", "@OnDetach": "Run a handler when a property is structurally detached from a live instance.", "@OnDisable": "Run a handler when a property is paused (disabled) on an instance.", "@OnEnable": "Run a handler when a paused property is re-enabled on an instance.", "@OnQuit": "Run a handler once at shutdown, after the last frame.", "@OnSpawn": "Run a handler once each time an instance of a model is spawned.", "@OnStart": "Run a handler once at boot instead of assigning it a frame phase.", "@Owned": "Give a model a network owner slot so its instances can be assigned to a peer.", "@Predicted": "Run a control handler on the owning client speculatively and on the server authoritatively.", "@Public": "Promote a lifecycle hook to a public event other modules can listen for.", "@Reads": "Declare that a handler reads a property — an analysis and scheduling hint.", "@Server": "Run a handler only on the authority; clients receive the result via @Sync.", "@ToClients": "A remote event broadcast from the server to clients — a server→clients notification.", "@ToServer": "A remote event sent from a client to the server — a client→server request.", "@Writes": "Declare that a handler writes a property — an analysis and scheduling hint.", "@EngineSystem": "Register a package's engine-owned system so the frame loop runs it each phase.", "@Namespace": "Let a package provide a Name.method(…) namespace that dispatches to name_method.", "\"@ClearColor\"": "Declare a clear colour so the Render phase auto-clears + auto-presents for you.", "@System": "Register a prebuilt binary module's system with the host at load, per phase.", "Huge.from": "Turn an int into a Huge.", "Huge.add": "Sum of two big numbers.", "Huge.sub": "Difference of two big numbers.", "Huge.mul": "Product of two big numbers.", "Huge.neg": "Negate a big number.", "Huge.cmp": "Compare two big numbers: -1, 0, or 1.", "Huge.sign": "The sign: -1, 0, or 1.", "Huge.mantissa": "The mantissa in [1, 10).", "Huge.exp": "The base-10 exponent.", "Huge.str": "Render as scientific text like 1.23e45.", "Angle.from_degrees": "Degrees to a wrapped radian angle.", "Angle.to_degrees": "Radians back to degrees.", "Angle.wrap": "Normalize any radian value to [-pi, pi).", "Angle.sin": "Sine of an angle.", "Angle.cos": "Cosine of an angle.", "Angle.add": "Add two angles (wrapped).", "Angle.diff": "Shortest signed rotation from a to b.", "Angle.lerp": "Interpolate along the shortest arc.", "Angle.diff_degrees": "The signed difference between two headings in whole degrees.", "Percent.clamp": "Clamp any fixed into [0, 1].", "Percent.of": "Clamped ratio num / den.", "Percent.lerp": "Interpolate a..b by a clamped t.", "Percent.apply": "Scale a value by a clamped percent.", "Regex.compile": "Compile a pattern once for reuse; returns an error value (null) if it is invalid.", "Regex.valid": "True if the pattern is well-formed (compiles without error).", "Regex.matches": "True if the pattern matches anywhere in the text (a search).", "Regex.test": "Like matches, but reuses an already-compiled Regex.", "Regex.find": "The first match of the pattern in the text (a Match, or null if none).", "Regex.exec": "Like find, but with an already-compiled Regex.", "Regex.next": "The next match at or after byte offset from — the basis of a find-all loop.", "Regex.replace": "Replace every match; the replacement expands \\0..\\9 group references.", "Regex.group": "The text of capture group n (group 0 is the whole match); empty if unset.", "Regex.group_count": "How many capture groups the match's pattern has.", "Regex.start": "The start byte offset of group n, or -1 if it did not participate.", "Regex.end": "The end byte offset of group n, or -1 if it did not participate.", "Regex.ok": "True if the match succeeded (the Match is non-null).", "Grid.line": "Every cell a straight line from (x0,y0) to (x1,y1) crosses (Bresenham).", "Grid.blocked": "True if the cell is out of bounds or holds the wall tile.", "Grid.line_of_sight": "True if the straight line between two cells crosses no wall.", "Grid.flood": "Every passable cell reachable from (x,y), 4-connected, in BFS order.", "Grid.a_star": "The shortest 4-connected path between two cells (A*), or an empty list.", "Anim.frame": "The looping frame index for an elapsed timer at a given fps.", "Anim.once": "A non-looping frame index that clamps on the last frame.", "Anim.pingpong": "A frame index that bounces 0..count-1..0 and repeats.", "Anim.finished": "True once a one-shot clip has run past its last frame.", "Anim.duration": "Seconds for one full cycle of a clip: count / fps.", "Anim.cell_x": "The source x (pixels) of a frame on a grid spritesheet.", "Anim.cell_y": "The source y (pixels) of a frame on a grid spritesheet.", "Anim.play": "Start (or restart) a spritesheet clip on an entity in one call.", "Anim.clip": "Register a named spritesheet clip so Anim.play can play it by name.", "Anim.on_frame": "Arm a frame event — the engine flags the tick a clip lands on this frame.", "Anim.fired": "Did the entity's clip land on its armed frame event this tick?", "Motion.to": "Start a value tween on an entity's Motion component in one call.", "Tween.progress": "A one-shot 0..1 amount, clamped, for timer over duration.", "Tween.loop": "A repeating 0..1 sawtooth amount over duration.", "Tween.yoyo": "A repeating 0..1..0 triangle amount over duration.", "Tween.done": "True once a one-shot tween's timer reaches its duration.", "Tween.ease": "Shape a 0..1 amount through an easing curve chosen by a literal mode.", "Tween.number": "Linear blend of two fixeds by amount t.", "Tween.round": "Linear blend of two ints by amount t, rounded to the nearest int.", "Tween.point": "Component-wise blend of two Vectors by amount t.", "Tween.tint": "Per-channel blend of two colors by amount t.", "Tween.to": "Start a fluent, engine-advanced tween and return a handle.", "Tween.chain": "Append a tween segment that runs after the handle's current queue.", "Tween.delay": "Append a pause to a tween handle's sequence.", "Tween.value": "The current value of a fluent tween handle this frame.", "Tween.stop": "Stop and dispose a tween handle, freezing its value.", "Tween.parallel": "Are two tween handles both finished? — a parallel completion query.", "Query.count": "How many live entities carry a property.", "Query.first": "The lowest-id live entity carrying a property, or -1.", "Query.nearest": "The bearer of prop closest to a point, by squared distance.", "Query.within": "Every bearer of prop inside a radius of a point.", "Reflect.prop": "The property id for a property name, or -1 if unknown.", "Reflect.field": "The field id of a named field within a property, or -1.", "Reflect.prop_count": "How many properties the world schema defines.", "Reflect.prop_name": "The name of the property at an index (or \"\").", "Reflect.field_count": "How many fields a property has.", "Reflect.field_name": "The name of the field at an index within a property.", "Reflect.field_type": "The type name of the field at an index within a property.", "Reflect.get": "Read one field of an entity by (prop, field) id.", "Reflect.set": "Write one field of an entity by (prop, field) id.", "Reflect.has": "Does an entity carry a property?", "Reflect.kind": "The model id of an entity.", "Reflect.model": "The model id for a model name, or -1 if unknown.", "Reflect.serialize": "Walk an entity's components into a value tree.", "Reflect.apply": "Write a value tree's fields back into an entity.", "Camera.set": "Place the camera at a world-space offset.", "Camera.follow": "Ease the camera toward centring a target point.", "Camera.shake": "Jitter the camera by up to +/- amount pixels (seeded RNG).", "Camera.zoom": "Scale the whole view about the screen centre by a Q16.16 factor (1.0 = none).", "Camera.shake_for": "Shake for a number of frames, then stop, with no bookkeeping.", "Light.ambient": "Multiply the whole scene by a tint — the night/cave modulate.", "Light.point": "Add a radial glow that falls off with distance.", "Light.occlude": "Register a rectangle that blocks light — a hard shadow caster.", "Light.clear_occluders": "Forget every occluder — call once per frame before re-registering.", "Light.spot": "A cone / flashlight light aimed at a direction with a half-angle spread.", "Light.falloff": "Set the brightness-ramp exponent for later lights (1 linear, 2 quadratic…).", "Light.soft": "Soft shadows — an occluder edge fades through a penumbra of this radius.", "Light.gel": "A colour cookie — later lights gel from their centre colour to this rim colour.", "Light.clear_gel": "Clear the gel — later lights are a flat single colour again.", "Light.normal": "Stamp a surface normal over a rectangle so lights shade it by facing (N·L).", "Light.clear_normals": "Forget every stamped normal — call once per frame before re-stamping.", "Light.height": "Set the virtual height of lights above the surface, for normal-map shading.", "Light.time_of_day": "Set the ambient tint from a 0..1 time-of-day — a day/night cycle in one value.", "Sprite.sheet": "Load a spritesheet and remember its cell grid; returns a sheet handle.", "Sprite.cell": "One cell of a sheet, by grid coords; returns a sprite id.", "Sprite.cell_span": "A sprite spanning cols x rows cells (tall/wide art).", "Sprite.define": "Name a cell for later lookup; returns its sprite id.", "Sprite.named": "Look up a named sprite's id, or -1.", "Sprite.draw": "Blit an atlas sprite at (x, y) through the camera/zoom/clip.", "Sprite.draw_scaled": "Blit an atlas sprite scaled by an integer factor.", "Sprite.width": "The atlas sprite's width in pixels.", "Sprite.height": "The atlas sprite's height in pixels.", "Sprite.strip": "A run of animation frames as consecutive sprite ids.", "Sprite.draw_meter": "A value as a row of full / half / empty icons.", "Assets.image": "Load a whole image file as one sprite; returns its id.", "Assets.load": "Alias of Assets.image.", "Assets.get": "Look up a named sprite's id (alias of Sprite.named).", "Assets.enqueue": "Queue a named image file to preload later.", "Assets.pump": "Load up to max queued assets this frame; returns how many it loaded.", "Assets.total": "How many assets are enqueued.", "Assets.loaded": "How many enqueued assets have loaded so far.", "Assets.ready": "1 once every enqueued asset has loaded.", "Assets.font": "A font loaded through the preload queue, by name.", "Assets.progress": "Loading progress as a 0..100 percent.", "Assets.enqueue_dir": "Enqueue every file of a directory, named by its file name.", "test": "A named test block, run automatically with pass/fail reporting.", "Pool.live": "How many entities are currently alive.", "Pool.free": "How many freed slots are waiting to be reused.", "Pool.reserved": "High-water: how many slots have ever been allocated.", "Pool.capacity": "The maximum number of entities.", "Value.null": "An empty null node.", "Value.int": "Wrap an int in a value node.", "Value.fixed": "Wrap a fixed in a value node.", "Value.bool": "Wrap a bool in a value node.", "Value.str": "Wrap a string in a value node.", "Value.list": "An empty list node.", "Value.object": "An empty object node.", "Value.add": "Append an item to a list; returns the list.", "Value.put": "Set a key on an object; returns the object.", "Value.get": "Read a member of an object by key.", "Value.has": "Whether an object has a member.", "Value.at": "Read a list item by index.", "Value.key_at": "The key of an object member by position.", "Value.count": "Number of items/members in a list or object.", "Value.kind": "The node's kind tag.", "Value.as_int": "Read a scalar node as an int.", "Value.as_str": "Read a str node's text.", "Json.encode": "Serialize a value tree to compact JSON text.", "Json.parse": "Parse JSON text into a value tree.", "Job.defer": "A future you resolve yourself later.", "Job.run": "Start a background compute job.", "Job.fulfill": "Resolve a pending job with a value.", "Job.fail": "Resolve a pending job as failed.", "Job.cancel": "Cancel a job before it finishes.", "Job.pump": "Advance background jobs; collect results.", "Job.done": "Has the job resolved (any outcome)?", "Job.ok": "Did the job succeed?", "Job.failed": "Did the job fail?", "Job.cancelled": "Was the job cancelled?", "Job.result": "The success value of a done job.", "Job.error": "The error code of a failed job.", "Job.pending": "How many jobs are still unresolved.", "Job.free": "Release a job slot back to the pool.", "Job.parallel_for": "Run work(i, ctx) for every i in [0, count) across all cores; returns when every call is done.", "Job.is_worker": "True on a Job.parallel_for pool thread, false on the main thread.", "Promise.all": "Succeeds when every member succeeds.", "Promise.race": "Succeeds when the first member does.", "Promise.count_done": "How many members have resolved.", "Promise.all_done": "Have all members resolved?", "Sync.mutex": "Create a cooperative lock.", "Sync.lock": "Take the lock.", "Sync.unlock": "Release the lock.", "Sync.try_lock": "Take the lock only if it is free.", "Sync.atomic": "Create an atomic counter (starts at 0).", "Sync.get": "Read the counter.", "Sync.set": "Store a value in the counter.", "Sync.add": "Add to the counter; return the new value.", "Sync.cas": "Compare-and-set the counter.", "Sync.channel": "Create a bounded int FIFO channel.", "Sync.send": "Enqueue a value (false if full).", "Sync.recv": "Dequeue the oldest value.", "Sync.can_recv": "Is there a value waiting?", "Sync.len": "How many values are queued.", "Sync.cpu_count": "The machine's logical cores (1 to 64); Job.parallel_for uses one thread per core.", "Xml.parse": "Parse XML text into an element tree.", "Xml.tag": "The element's tag name.", "Xml.text": "The element's character data.", "Xml.attr": "An attribute's value by name.", "Xml.attr_int": "An attribute parsed as an integer.", "Xml.has": "Whether an attribute is present.", "Xml.attr_count": "How many attributes the element has.", "Xml.child_count": "How many child elements.", "Xml.child": "A child element by index.", "Xml.find": "The first child with a given tag.", "Xml.count": "How many children have a given tag.", "Base64.decode": "Decode standard base64 text to bytes.", "Base64.encode": "Encode bytes as standard base64 text.", "Tiled.read": "Read a map file into the intermediate value tree.", "Tiled.read_tsx": "Read a tileset file into a value object.", "Tiled.load": "Load a map file into the runtime map model.", "Tiled.gid": "The raw GID at a cell in a tile layer.", "Tiled.resolve": "Decode a GID into tileset, local id and flip flags.", "Tiled.width": "The map width in tiles.", "Tiled.height": "The map height in tiles.", "Tiled.layer_count": "How many layers the map has.", "Tiled.layer_name": "A layer's name by index.", "Tiled.draw": "Draw every visible tile layer.", "Tiled.project": "Project a collision layer to the byte tilemap.", "Tiled.tree": "The underlying intermediate value tree.", "Tiled.tile_prop": "Whether a GID's tile carries a bool property.", "Tiled.collide": "Drive collision from per-tile metadata.", "Tiled.collision_kind": "Classify a GID's collision from its tile metadata.", "Tiled.tile_shapes": "Whether a GID's tile has a collision shape.", "Tiled.draw_anim": "Draw with animated tiles advanced to a frame.", "Tiled.frame_gid": "The current GID of an animated tile at a frame.", "Tiled.animated": "Whether a GID's tile is animated.", "Tiled.object_count": "How many objects an object layer holds.", "Tiled.object": "An object on an object layer by index.", "Tiled.object_shape": "Classify an object's shape.", "Tiled.prop": "A custom property's value, with class-default fallback.", "Tiled.prop_int": "A custom property parsed as an integer.", "Tiled.prop_type": "A custom property's declared type.", "Tiled.load_types": "Load a project custom-type table.", "Tiled.template": "Read a template file into an object value.", "Tiled.spawn": "Spawn a Ludic entity from an object.", "Tiled.spawn_layer": "Spawn every object on a layer.", "Tiled.cell_x": "The screen x of a tile cell for the map orientation.", "Tiled.cell_y": "The screen y of a tile cell for the map orientation.", "Tiled.layer_kind": "A layer's kind.", "Tiled.layer_opacity": "A layer's opacity.", "Tiled.layer_tint": "A layer's tint colour.", "Tiled.layer_offsetx": "A layer's horizontal offset.", "Tiled.layer_offsety": "A layer's vertical offset.", "Tiled.world": "Read a .world file.", "Tiled.world_count": "How many maps a world stitches.", "Tiled.world_map": "A world member map by index.", "Ui.build": "Construct every declared ui block (loads skins, measures fonts).", "Ui.open": "Make one menu active and focus its first button.", "Ui.close": "Deactivate the menu: no menu is open.", "Ui.tick": "Advance navigation by one key (the frame loop does this for you).", "Ui.clicked": "Was this control activated this frame? (polled form of UiClicked)", "Ui.set_text": "Replace a label's or button's text.", "Ui.render": "Draw the active menu.", "File.open": "Open a file (fopen); null when it cannot be opened.", "File.read": "Read up to n bytes into a buffer; returns the bytes read.", "File.write": "Write n bytes from a buffer; returns the bytes written.", "File.seek": "Move the file cursor (0 start, 1 current, 2 end).", "File.tell": "The cursor position in bytes.", "File.close": "Close a handle from File.open.", "Font.load": "Load a TrueType font; returns a font handle.", "Fx.sparks": "A burst of sparks flying out of a point.", "Fx.number": "A number that floats up from a point and fades.", "Fx.clear": "Drop every spark and number at once.", "Prefab.spawn": "Spawn a prefab chosen by name at runtime; -1 if none has that name.", "Prefab.spawn_at": "Spawn a prefab by name and place it.", "App.splash_hide": "Dismiss the boot splash.", "App.set_icon": "Set the Dock icon from an image the game ships.", "App.window_hide": "Take the game's window off the screen.", "App.window_show": "Bring the game's window back.", "Start": "Runs once at boot, before the game loop — the place to set up initial state.", "Input": "The first per-frame phase — read the keyboard and record the player's intent.", "FixedUpdate": "The deterministic simulation step — physics and gameplay meant to be reproducible.", "Update": "The ordinary per-frame game-logic step, run after Input and FixedUpdate.", "LateUpdate": "Runs each frame after Update and before Render.", "Render": "The last per-frame phase — draw the world, then call Screen.show() once.", "Overlay": "The HUD pass — runs after Render and after every engine-owned drawing system."}, "cards": {"kw-program": {"name": "program", "sig": "program Name { … }", "tip": "The top-level unit — one program compiles to one native game.", "kind": "keyword", "page": "kw-program.html", "section": "Program structure"}, "kw-property": {"name": "property", "sig": "property Name { field: T = default, … }", "tip": "A named record of typed fields — a per-model component, or a plain heap record.", "kind": "keyword", "page": "kw-property.html", "section": "Program structure"}, "kw-model": {"name": "model", "sig": "model Name { PropA, PropB, … }", "tip": "A named kind of thing — a fixed bundle of properties you spawn by one name.", "kind": "keyword", "page": "kw-model.html", "section": "Program structure"}, "kw-handler": {"name": "handler", "sig": "handler Name phase P { … }", "tip": "A named block the engine runs each frame during phase P.", "kind": "keyword", "page": "kw-handler.html", "section": "Program structure"}, "kw-phase": {"name": "phase", "sig": "phase Start | Input | Update | FixedUpdate | Render", "tip": "Names which stage of the frame a handler runs in.", "kind": "keyword", "page": "kw-phase.html", "section": "Program structure"}, "kw-const": {"name": "const", "sig": "const NAME: T = value", "tip": "A compile-time constant, folded into the code with no storage.", "kind": "keyword", "page": "kw-const.html", "section": "Program structure"}, "kw-var": {"name": "var", "sig": "var name: T = value", "tip": "A mutable binding — at program scope, your game's persistent named state.", "kind": "keyword", "page": "kw-var.html", "section": "Program structure"}, "kw-let": {"name": "let", "sig": "let name = value", "tip": "An immutable binding — the default choice for a value that never changes.", "kind": "keyword", "page": "kw-let.html", "section": "Program structure"}, "kw-function": {"name": "function", "sig": "function name(a: T, b: T) -> R { … }", "tip": "A function — reusable logic called positionally or with named arguments.", "kind": "keyword", "page": "kw-function.html", "section": "Program structure"}, "kw-fn": {"name": "fn", "sig": "fn name", "tip": "A top-level function named as a value - the entry point handed to Job.parallel_for.", "kind": "keyword", "page": "kw-fn.html", "section": "Program structure"}, "kw-prefab": {"name": "prefab", "sig": "prefab Name: Model { Comp { field: value }, … }", "tip": "A model with preset component fields — spawn it, override what varies.", "kind": "keyword", "page": "kw-prefab.html", "section": "Program structure"}, "kw-return": {"name": "return", "sig": "return value", "tip": "Hand a value back from a function and stop running it.", "kind": "keyword", "page": "kw-return.html", "section": "Program structure"}, "kw-entry": {"name": "entry", "sig": "entry { … }", "tip": "The program's main block — runs once, top to bottom.", "kind": "keyword", "page": "kw-entry.html", "section": "Program structure"}, "kw-import": {"name": "import", "sig": "import \"file.ludic\"", "tip": "Splice another Ludic file's declarations into this program.", "kind": "keyword", "page": "kw-import.html", "section": "Program structure"}, "kw-public": {"name": "public", "sig": "scene Name start public { … }", "tip": "Expose a declaration's lifecycle on the public event bus.", "kind": "keyword", "page": "kw-public.html", "section": "Program structure"}, "kw-extern": {"name": "extern", "sig": "extern function name(a: T) -> R = \"symbol\"", "tip": "Bind a name to an external native symbol — the seam for platform and library calls.", "kind": "keyword", "page": "kw-extern.html", "section": "Program structure"}, "kw-new": {"name": "new", "sig": "new Record { … } new []T", "tip": "Allocate a property record or an empty slice on the heap.", "kind": "keyword", "page": "kw-new.html", "section": "Program structure"}, "kw-namespace": {"name": "namespace", "sig": "namespace Name { export function foo(…) … internal function bar(…) … }", "tip": "Group functions into a Name.* namespace and control its public surface.", "kind": "keyword", "page": "kw-namespace.html", "section": "Program structure"}, "kw-enum": {"name": "enum", "sig": "enum Name { A, B, C }", "tip": "A named set of integer constants — names for a magic-number space.", "kind": "keyword", "page": "kw-enum.html", "section": "Program structure"}, "kw-ui": {"name": "ui", "sig": "ui { panel { … } }", "tip": "Declare a retained widget tree as data; the engine lays it out and draws it.", "kind": "keyword", "page": "kw-ui.html", "section": "Program structure"}, "kw-spawn": {"name": "spawn", "sig": "spawn Label { Prop { field: v }; … }", "tip": "Create a model instance carrying the listed properties, seeding their fields.", "kind": "keyword", "page": "kw-spawn.html", "section": "Entities & the ECS"}, "kw-despawn": {"name": "despawn", "sig": "despawn entity", "tip": "Remove a model instance from the world, firing any @OnDespawn hooks.", "kind": "keyword", "page": "kw-despawn.html", "section": "Entities & the ECS"}, "kw-query": {"name": "query", "sig": "query [PropA, PropB, {Tag}]", "tip": "Match every model instance carrying all the listed properties.", "kind": "keyword", "page": "kw-query.html", "section": "Entities & the ECS"}, "kw-self": {"name": "self", "sig": "self()", "tip": "The model instance currently bound by the enclosing query loop.", "kind": "keyword", "page": "kw-self.html", "section": "Entities & the ECS"}, "kw-enable": {"name": "enable", "sig": "enable Prop on entity", "tip": "Re-activate a disabled property, model, or handler — its data is intact.", "kind": "keyword", "page": "kw-enable.html", "section": "Entities & the ECS"}, "kw-disable": {"name": "disable", "sig": "disable Prop on entity", "tip": "Deactivate a property, model, or handler without destroying its data.", "kind": "keyword", "page": "kw-disable.html", "section": "Entities & the ECS"}, "kw-attach": {"name": "attach", "sig": "attach Prop on entity { overrides }", "tip": "Structurally add a property to a live model instance, seeding its fields.", "kind": "keyword", "page": "kw-attach.html", "section": "Entities & the ECS"}, "kw-detach": {"name": "detach", "sig": "detach Prop on entity", "tip": "Structurally remove a property from a live model instance.", "kind": "keyword", "page": "kw-detach.html", "section": "Entities & the ECS"}, "fn-world_attach_dyn": {"name": "world_attach_dyn", "sig": "world_attach_dyn(target, property)", "tip": "Attach a property to an instance by numeric id at runtime (dynamic ECS).", "kind": "builtin", "page": "fn-world_attach_dyn.html", "section": "Entities & the ECS"}, "fn-world_count": {"name": "world_count", "sig": "world_count() -> int", "tip": "The total number of live model instances in the world.", "kind": "builtin", "page": "fn-world_count.html", "section": "Entities & the ECS"}, "fn-world_detach_dyn": {"name": "world_detach_dyn", "sig": "world_detach_dyn(target, property)", "tip": "Detach a property from an instance by numeric id at runtime (dynamic ECS).", "kind": "builtin", "page": "fn-world_detach_dyn.html", "section": "Entities & the ECS"}, "fn-world_field_id": {"name": "world_field_id", "sig": "world_field_id(property, name) -> int", "tip": "Resolve a field name within a property to its numeric index.", "kind": "builtin", "page": "fn-world_field_id.html", "section": "Entities & the ECS"}, "fn-world_get": {"name": "world_get", "sig": "world_get(target, property, field) -> int", "tip": "Read one field of a model instance by numeric id (reflection ABI).", "kind": "builtin", "page": "fn-world_get.html", "section": "Entities & the ECS"}, "fn-world_has": {"name": "world_has", "sig": "world_has(target, property) -> bool", "tip": "Test whether a model instance currently carries a property.", "kind": "builtin", "page": "fn-world_has.html", "section": "Entities & the ECS"}, "fn-world_kind": {"name": "world_kind", "sig": "world_kind(target) -> int", "tip": "The model id of an instance — which kind of thing it is.", "kind": "builtin", "page": "fn-world_kind.html", "section": "Entities & the ECS"}, "fn-world_load": {"name": "world_load", "sig": "world_load(buf, len)", "tip": "Restore the whole world from a serialized byte buffer.", "kind": "builtin", "page": "fn-world_load.html", "section": "Entities & the ECS"}, "fn-world_model_id": {"name": "world_model_id", "sig": "world_model_id(name) -> int", "tip": "Resolve a model's name to its stable numeric id.", "kind": "builtin", "page": "fn-world_model_id.html", "section": "Entities & the ECS"}, "fn-world_prop_id": {"name": "world_prop_id", "sig": "world_prop_id(name) -> int", "tip": "Resolve a property's name to its stable numeric id.", "kind": "builtin", "page": "fn-world_prop_id.html", "section": "Entities & the ECS"}, "fn-world_query_next": {"name": "world_query_next", "sig": "world_query_next(property, cursor) -> entity", "tip": "Step to the next instance carrying a property, walking the world by id.", "kind": "builtin", "page": "fn-world_query_next.html", "section": "Entities & the ECS"}, "fn-world_register_prop": {"name": "world_register_prop", "sig": "world_register_prop(name, fields) -> int", "tip": "Register a brand-new property at runtime and get its id (dynamic ECS).", "kind": "builtin", "page": "fn-world_register_prop.html", "section": "Entities & the ECS"}, "fn-world_save": {"name": "world_save", "sig": "world_save(buf) -> int", "tip": "Serialize the whole world into a buffer; returns the number of bytes written.", "kind": "builtin", "page": "fn-world_save.html", "section": "Entities & the ECS"}, "fn-world_set": {"name": "world_set", "sig": "world_set(target, property, field, value)", "tip": "Write one field of a model instance by numeric id (reflection ABI).", "kind": "builtin", "page": "fn-world_set.html", "section": "Entities & the ECS"}, "fn-world_size": {"name": "world_size", "sig": "world_size() -> int", "tip": "The number of live model instances in the world.", "kind": "builtin", "page": "fn-world_size.html", "section": "Entities & the ECS"}, "fn-world_spawn": {"name": "world_spawn", "sig": "world_spawn(model) -> entity", "tip": "Spawn an instance of a model chosen by numeric id at runtime.", "kind": "builtin", "page": "fn-world_spawn.html", "section": "Entities & the ECS"}, "kw-if": {"name": "if / else", "sig": "if cond { … } else { … }", "tip": "A branch — run one block when a condition holds, another when it doesn't.", "kind": "keyword", "page": "kw-if.html", "section": "Control flow"}, "kw-while": {"name": "while", "sig": "while cond { … }", "tip": "Loop as long as a condition holds, re-checking it before each pass.", "kind": "keyword", "page": "kw-while.html", "section": "Control flow"}, "kw-for": {"name": "for … in", "sig": "for name in a .. b { … }", "tip": "Range loop — iterate the half-open range from a up to but not including b.", "kind": "keyword", "page": "kw-for.html", "section": "Control flow"}, "kw-in": {"name": "in", "sig": "for x in range | query", "tip": "The part of a for loop that names what to iterate — a range or a query.", "kind": "keyword", "page": "kw-in.html", "section": "Control flow"}, "kw-match": {"name": "match", "sig": "match value { 0, 1 => … _ => … }", "tip": "Multi-way branch on one value, matching one or more literals per arm.", "kind": "keyword", "page": "kw-match.html", "section": "Control flow"}, "kw-machine": {"name": "machine", "sig": "machine store { state Name { … } }", "tip": "An explicit state machine over an int var — dispatches on the store's value.", "kind": "keyword", "page": "kw-machine.html", "section": "Control flow"}, "kw-state": {"name": "state", "sig": "state Name { … }", "tip": "One state of a machine — its body runs while the machine sits in it.", "kind": "keyword", "page": "kw-state.html", "section": "Control flow"}, "kw-become": {"name": "become", "sig": "become Name", "tip": "Transition to another state of the enclosing machine, or to another scene.", "kind": "keyword", "page": "kw-become.html", "section": "Control flow"}, "kw-break": {"name": "break", "sig": "break", "tip": "Leave the enclosing loop immediately.", "kind": "keyword", "page": "kw-break.html", "section": "Control flow"}, "kw-continue": {"name": "continue", "sig": "continue", "tip": "Skip to the next iteration of the loop.", "kind": "keyword", "page": "kw-continue.html", "section": "Control flow"}, "kw-where": {"name": "where", "sig": "for (Binds) in query [Comps] where cond { … }", "tip": "Filter a query loop to entities that satisfy a condition.", "kind": "keyword", "page": "kw-where.html", "section": "Control flow"}, "kw-try": {"name": "try", "sig": "try EXPR else { … }", "tip": "Recover a fallible result as a value, with a fallback — no exceptions, no unwinding.", "kind": "keyword", "page": "kw-try.html", "section": "Control flow"}, "kw-scene": {"name": "scene", "sig": "scene Name [start] { on enter{} on exit{} layer … }", "tip": "A mutually-exclusive game state — a title screen, the overworld, a battle.", "kind": "keyword", "page": "kw-scene.html", "section": "Scenes & layers"}, "kw-layer": {"name": "layer", "sig": "layer Name { handlers }", "tip": "A named group of handlers inside a scene; layers render in declaration order.", "kind": "keyword", "page": "kw-layer.html", "section": "Scenes & layers"}, "kw-on": {"name": "on enter / on exit", "sig": "on enter { … } on exit { … }", "tip": "Scene lifecycle hooks that fire as a scene becomes active or is left.", "kind": "keyword", "page": "kw-on.html", "section": "Scenes & layers"}, "kw-enter": {"name": "enter", "sig": "on enter { … }", "tip": "The scene-entry hook — runs once as a scene becomes active.", "kind": "keyword", "page": "kw-enter.html", "section": "Scenes & layers"}, "kw-exit": {"name": "exit", "sig": "on exit { … }", "tip": "The scene-exit hook — runs once as a scene is left.", "kind": "keyword", "page": "kw-exit.html", "section": "Scenes & layers"}, "kw-start": {"name": "start", "sig": "scene Name start { … }", "tip": "Marks the one scene the game begins in.", "kind": "keyword", "page": "kw-start.html", "section": "Scenes & layers"}, "kw-event": {"name": "event", "sig": "event Name { field: T = default }", "tip": "Declare a public event carrying a flat payload that listeners react to.", "kind": "keyword", "page": "kw-event.html", "section": "Events"}, "kw-emit": {"name": "emit", "sig": "emit Name(field: value)", "tip": "Fire an event, running every listener; as an expression it yields the veto flag.", "kind": "keyword", "page": "kw-emit.html", "section": "Events"}, "kw-cancel": {"name": "cancel", "sig": "cancel", "tip": "Inside a listener, veto the cancellable event being emitted.", "kind": "keyword", "page": "kw-cancel.html", "section": "Events"}, "kw-cancellable": {"name": "cancellable", "sig": "event cancellable Name { … }", "tip": "Marks an event whose listeners may veto it with cancel.", "kind": "keyword", "page": "kw-cancellable.html", "section": "Events"}, "screen-clear": {"name": "Screen.clear", "sig": "Screen.clear(color)", "tip": "Fill the whole framebuffer with one color to start a fresh frame.", "kind": "namespace-method", "page": "screen-clear.html", "section": "Screen — drawing"}, "screen-fill_rectangle": {"name": "Screen.fill_rectangle", "sig": "Screen.fill_rectangle(x, y, width, height, color)", "tip": "Draw a solid, filled rectangle at a pixel position.", "kind": "namespace-method", "page": "screen-fill_rectangle.html", "section": "Screen — drawing"}, "screen-draw_rectangle": {"name": "Screen.draw_rectangle", "sig": "Screen.draw_rectangle(x, y, width, height, color)", "tip": "Draw a one-pixel-thick rectangle outline (not filled).", "kind": "namespace-method", "page": "screen-draw_rectangle.html", "section": "Screen — drawing"}, "screen-put_pixel": {"name": "Screen.put_pixel", "sig": "Screen.put_pixel(x, y, color)", "tip": "Set a single pixel at a pixel coordinate to one color.", "kind": "namespace-method", "page": "screen-put_pixel.html", "section": "Screen — drawing"}, "screen-draw_text": {"name": "Screen.draw_text", "sig": "Screen.draw_text(x, y, text, color, scale)", "tip": "Draw a string with the built-in font at an integer scale.", "kind": "namespace-method", "page": "screen-draw_text.html", "section": "Screen — drawing"}, "screen-draw_number": {"name": "Screen.draw_number", "sig": "Screen.draw_number(x, y, value, color, scale)", "tip": "Draw an integer directly, with no string allocation or conversion.", "kind": "namespace-method", "page": "screen-draw_number.html", "section": "Screen — drawing"}, "screen-show": {"name": "Screen.show", "sig": "Screen.show()", "tip": "Present the finished frame — copy everything drawn this frame to the window.", "kind": "namespace-method", "page": "screen-show.html", "section": "Screen — drawing"}, "screen-width": {"name": "Screen.width", "sig": "Screen.width() -> int", "tip": "The framebuffer width in pixels.", "kind": "namespace-method", "page": "screen-width.html", "section": "Screen — drawing"}, "screen-height": {"name": "Screen.height", "sig": "Screen.height() -> int", "tip": "The framebuffer height in pixels.", "kind": "namespace-method", "page": "screen-height.html", "section": "Screen — drawing"}, "screen-status": {"name": "Screen.status", "sig": "Screen.status(text)", "tip": "Set the persistent one-line status/HUD string shown by the runtime.", "kind": "namespace-method", "page": "screen-status.html", "section": "Screen — drawing"}, "screen-line": {"name": "Screen.line", "sig": "Screen.line(x1, y1, x2, y2, color)", "tip": "Draw a straight line between two points.", "kind": "namespace-method", "page": "screen-line.html", "section": "Screen — drawing"}, "screen-circle": {"name": "Screen.circle", "sig": "Screen.circle(x, y, radius, color)", "tip": "Draw a circle outline.", "kind": "namespace-method", "page": "screen-circle.html", "section": "Screen — drawing"}, "screen-fill_circle": {"name": "Screen.fill_circle", "sig": "Screen.fill_circle(x, y, radius, color)", "tip": "Draw a filled disc.", "kind": "namespace-method", "page": "screen-fill_circle.html", "section": "Screen — drawing"}, "screen-triangle": {"name": "Screen.triangle", "sig": "Screen.triangle(x1, y1, x2, y2, x3, y3, color)", "tip": "Draw a triangle outline.", "kind": "namespace-method", "page": "screen-triangle.html", "section": "Screen — drawing"}, "screen-fill_triangle": {"name": "Screen.fill_triangle", "sig": "Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color)", "tip": "Draw a filled triangle.", "kind": "namespace-method", "page": "screen-fill_triangle.html", "section": "Screen — drawing"}, "screen-sprite": {"name": "Screen.sprite", "sig": "Screen.sprite(id, x, y)", "tip": "Blit a sprite at its natural size.", "kind": "namespace-method", "page": "screen-sprite.html", "section": "Screen — drawing"}, "screen-sprite_scaled": {"name": "Screen.sprite_scaled", "sig": "Screen.sprite_scaled(id, x, y, scale)", "tip": "Blit a sprite at an integer scale.", "kind": "namespace-method", "page": "screen-sprite_scaled.html", "section": "Screen — drawing"}, "screen-oval": {"name": "Screen.oval", "sig": "Screen.oval(x, y, rx, ry, color)", "tip": "Draw an axis-aligned ellipse outline.", "kind": "namespace-method", "page": "screen-oval.html", "section": "Screen — drawing"}, "screen-camera": {"name": "Screen.camera", "sig": "Screen.camera(x, y)", "tip": "Set the world-space camera offset for the draw path.", "kind": "namespace-method", "page": "screen-camera.html", "section": "Screen — drawing"}, "screen-clip": {"name": "Screen.clip", "sig": "Screen.clip(x, y, width, height)", "tip": "Restrict drawing to a screen-space rectangle.", "kind": "namespace-method", "page": "screen-clip.html", "section": "Screen — drawing"}, "screen-clip_reset": {"name": "Screen.clip_reset", "sig": "Screen.clip_reset()", "tip": "Lift the clip rectangle (draw to the whole screen again).", "kind": "namespace-method", "page": "screen-clip_reset.html", "section": "Screen — drawing"}, "screen-blend_mode": {"name": "Screen.blend_mode", "sig": "Screen.blend_mode(mode)", "tip": "Choose replace or additive pixel blending.", "kind": "namespace-method", "page": "screen-blend_mode.html", "section": "Screen — drawing"}, "screen-measure_text": {"name": "Screen.measure_text", "sig": "Screen.measure_text(text) -> int", "tip": "The pixel advance width of text in the built-in font.", "kind": "namespace-method", "page": "screen-measure_text.html", "section": "Screen — drawing"}, "screen-pixel": {"name": "Screen.pixel", "sig": "Screen.pixel(x, y) -> int", "tip": "Read a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.", "kind": "namespace-method", "page": "screen-pixel.html", "section": "Screen — drawing"}, "screen-bar": {"name": "Screen.bar", "sig": "Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:)", "tip": "A filled meter: value of max over a track.", "kind": "namespace-method", "page": "screen-bar.html", "section": "Screen — drawing"}, "clock-now": {"name": "Clock.now", "sig": "Clock.now() -> int", "tip": "The current simulated instant.", "kind": "namespace-method", "page": "clock-now.html", "section": "Clock"}, "clock-set": {"name": "Clock.set", "sig": "Clock.set(t)", "tip": "Set the clock to an instant.", "kind": "namespace-method", "page": "clock-set.html", "section": "Clock"}, "clock-advance": {"name": "Clock.advance", "sig": "Clock.advance(span)", "tip": "Move the clock forward by a Duration.", "kind": "namespace-method", "page": "clock-advance.html", "section": "Clock"}, "clock-reset": {"name": "Clock.reset", "sig": "Clock.reset()", "tip": "Reset the clock to the epoch (0).", "kind": "namespace-method", "page": "clock-reset.html", "section": "Clock"}, "collision-rects": {"name": "Collision.rects", "sig": "Collision.rects(ax, ay, aw, ah, bx, by, bw, bh) -> bool", "tip": "Do two rectangles overlap?", "kind": "namespace-method", "page": "collision-rects.html", "section": "Collision"}, "collision-point_rect": {"name": "Collision.point_rect", "sig": "Collision.point_rect(px, py, rx, ry, rw, rh) -> bool", "tip": "Is a point inside a rectangle?", "kind": "namespace-method", "page": "collision-point_rect.html", "section": "Collision"}, "collision-circles": {"name": "Collision.circles", "sig": "Collision.circles(ax, ay, ar, bx, by, br) -> bool", "tip": "Do two circles overlap?", "kind": "namespace-method", "page": "collision-circles.html", "section": "Collision"}, "collision-rect_circle": {"name": "Collision.rect_circle", "sig": "Collision.rect_circle(rx, ry, rw, rh, cx, cy, cr) -> bool", "tip": "Does a rectangle overlap a circle?", "kind": "namespace-method", "page": "collision-rect_circle.html", "section": "Collision"}, "color-rgb": {"name": "Color.rgb", "sig": "Color.rgb(r, g, b) -> int", "tip": "Build a color from red, green, blue.", "kind": "namespace-method", "page": "color-rgb.html", "section": "Color functions"}, "color-rgba": {"name": "Color.rgba", "sig": "Color.rgba(r, g, b, a) -> int", "tip": "Build a color with an alpha byte.", "kind": "namespace-method", "page": "color-rgba.html", "section": "Color functions"}, "color-lerp": {"name": "Color.lerp", "sig": "Color.lerp(c0, c1, t) -> int", "tip": "Blend between two colors.", "kind": "namespace-method", "page": "color-lerp.html", "section": "Color functions"}, "color-darken": {"name": "Color.darken", "sig": "Color.darken(c, amount) -> int", "tip": "Scale a color toward black.", "kind": "namespace-method", "page": "color-darken.html", "section": "Color functions"}, "color-lighten": {"name": "Color.lighten", "sig": "Color.lighten(c, amount) -> int", "tip": "Scale a color toward white.", "kind": "namespace-method", "page": "color-lighten.html", "section": "Color functions"}, "color-with_alpha": {"name": "Color.with_alpha", "sig": "Color.with_alpha(c, a) -> int", "tip": "Replace a color's alpha byte.", "kind": "namespace-method", "page": "color-with_alpha.html", "section": "Color functions"}, "crypto-sha256": {"name": "Crypto.sha256", "sig": "Crypto.sha256(s) -> string", "tip": "SHA-256 of a string, as 64 hex characters.", "kind": "namespace-method", "page": "crypto-sha256.html", "section": "Crypto"}, "crypto-hmac_sha256": {"name": "Crypto.hmac_sha256", "sig": "Crypto.hmac_sha256(key, msg) -> string", "tip": "Sign a message with a shared secret key.", "kind": "namespace-method", "page": "crypto-hmac_sha256.html", "section": "Crypto"}, "crypto-verify_hmac": {"name": "Crypto.verify_hmac", "sig": "Crypto.verify_hmac(key, msg, mac) -> bool", "tip": "Constant-time check that a MAC matches.", "kind": "namespace-method", "page": "crypto-verify_hmac.html", "section": "Crypto"}, "crypto-hex": {"name": "Crypto.hex", "sig": "Crypto.hex(s) -> string", "tip": "Lowercase hex of a string's bytes.", "kind": "namespace-method", "page": "crypto-hex.html", "section": "Crypto"}, "crypto-ct_equal": {"name": "Crypto.ct_equal", "sig": "Crypto.ct_equal(a, b) -> bool", "tip": "Constant-time string equality for secrets.", "kind": "namespace-method", "page": "crypto-ct_equal.html", "section": "Crypto"}, "crypto-random_bytes": {"name": "Crypto.random_bytes", "sig": "Crypto.random_bytes(n) -> string", "tip": "n bytes from the OS CSPRNG, as a 2n-character hex string.", "kind": "namespace-method", "page": "crypto-random_bytes.html", "section": "Crypto"}, "crypto-random_hex": {"name": "Crypto.random_hex", "sig": "Crypto.random_hex(n) -> string", "tip": "Alias for random_bytes — n secure bytes as a 2n-char hex string.", "kind": "namespace-method", "page": "crypto-random_hex.html", "section": "Crypto"}, "crypto-random_u32": {"name": "Crypto.random_u32", "sig": "Crypto.random_u32() -> int", "tip": "One CSPRNG-drawn 32-bit integer.", "kind": "namespace-method", "page": "crypto-random_u32.html", "section": "Crypto"}, "crypto-base64": {"name": "Crypto.base64", "sig": "Crypto.base64(s) -> string", "tip": "Standard base64 (RFC 4648) of a string's bytes.", "kind": "namespace-method", "page": "crypto-base64.html", "section": "Crypto"}, "date-new": {"name": "Date.new", "sig": "Date.new(year, month, day) -> int", "tip": "A calendar day as days-since-1970.", "kind": "namespace-method", "page": "date-new.html", "section": "Date"}, "date-year": {"name": "Date.year", "sig": "Date.year(date) -> int", "tip": "The calendar year of a date.", "kind": "namespace-method", "page": "date-year.html", "section": "Date"}, "date-month": {"name": "Date.month", "sig": "Date.month(date) -> int", "tip": "The month (1-12) of a date.", "kind": "namespace-method", "page": "date-month.html", "section": "Date"}, "date-day": {"name": "Date.day", "sig": "Date.day(date) -> int", "tip": "The day of the month (1-31) of a date.", "kind": "namespace-method", "page": "date-day.html", "section": "Date"}, "date-weekday": {"name": "Date.weekday", "sig": "Date.weekday(date) -> int", "tip": "Day of the week, 0=Sunday..6=Saturday.", "kind": "namespace-method", "page": "date-weekday.html", "section": "Date"}, "date-is_leap": {"name": "Date.is_leap", "sig": "Date.is_leap(year) -> bool", "tip": "True if the year is a leap year.", "kind": "namespace-method", "page": "date-is_leap.html", "section": "Date"}, "date-days_in_month": {"name": "Date.days_in_month", "sig": "Date.days_in_month(year, month) -> int", "tip": "Number of days in a given month.", "kind": "namespace-method", "page": "date-days_in_month.html", "section": "Date"}, "date-to_epoch": {"name": "Date.to_epoch", "sig": "Date.to_epoch(date) -> int", "tip": "Midnight UTC of the day, as a DateTime.", "kind": "namespace-method", "page": "date-to_epoch.html", "section": "Date"}, "date-add_days": {"name": "Date.add_days", "sig": "Date.add_days(date, n) -> int", "tip": "The date n days later (n may be negative).", "kind": "namespace-method", "page": "date-add_days.html", "section": "Date"}, "date-diff_days": {"name": "Date.diff_days", "sig": "Date.diff_days(a, b) -> int", "tip": "Whole days from b to a (a - b).", "kind": "namespace-method", "page": "date-diff_days.html", "section": "Date"}, "datetime-from": {"name": "DateTime.from", "sig": "DateTime.from(year, month, day, hour, minute, second) -> int", "tip": "Build an instant from its calendar parts.", "kind": "namespace-method", "page": "datetime-from.html", "section": "DateTime"}, "datetime-date": {"name": "DateTime.date", "sig": "DateTime.date(dt) -> int", "tip": "The calendar day the instant falls on.", "kind": "namespace-method", "page": "datetime-date.html", "section": "DateTime"}, "datetime-add": {"name": "DateTime.add", "sig": "DateTime.add(dt, span) -> int", "tip": "The instant `span` seconds later.", "kind": "namespace-method", "page": "datetime-add.html", "section": "DateTime"}, "datetime-year": {"name": "DateTime.year", "sig": "DateTime.year(dt) -> int", "tip": "The calendar year of an instant.", "kind": "namespace-method", "page": "datetime-year.html", "section": "DateTime"}, "datetime-month": {"name": "DateTime.month", "sig": "DateTime.month(dt) -> int", "tip": "The month (1-12) of an instant.", "kind": "namespace-method", "page": "datetime-month.html", "section": "DateTime"}, "datetime-day": {"name": "DateTime.day", "sig": "DateTime.day(dt) -> int", "tip": "The day of the month (1-31) of an instant.", "kind": "namespace-method", "page": "datetime-day.html", "section": "DateTime"}, "datetime-weekday": {"name": "DateTime.weekday", "sig": "DateTime.weekday(dt) -> int", "tip": "Day of the week, 0=Sunday..6=Saturday.", "kind": "namespace-method", "page": "datetime-weekday.html", "section": "DateTime"}, "datetime-hour": {"name": "DateTime.hour", "sig": "DateTime.hour(dt) -> int", "tip": "The hour of day (0-23) of an instant.", "kind": "namespace-method", "page": "datetime-hour.html", "section": "DateTime"}, "datetime-minute": {"name": "DateTime.minute", "sig": "DateTime.minute(dt) -> int", "tip": "The minute of the hour (0-59) of an instant.", "kind": "namespace-method", "page": "datetime-minute.html", "section": "DateTime"}, "datetime-second": {"name": "DateTime.second", "sig": "DateTime.second(dt) -> int", "tip": "The second of the minute (0-59) of an instant.", "kind": "namespace-method", "page": "datetime-second.html", "section": "DateTime"}, "datetime-format": {"name": "DateTime.format", "sig": "DateTime.format(dt, pattern) -> string", "tip": "Render an instant as text using a token pattern.", "kind": "namespace-method", "page": "datetime-format.html", "section": "DateTime"}, "datetime-parse": {"name": "DateTime.parse", "sig": "DateTime.parse(text, pattern) -> int", "tip": "Parse text into an instant; -1 on failure.", "kind": "namespace-method", "page": "datetime-parse.html", "section": "DateTime"}, "duration-seconds": {"name": "Duration.seconds", "sig": "Duration.seconds(n) -> int", "tip": "A span of n whole seconds.", "kind": "namespace-method", "page": "duration-seconds.html", "section": "Duration"}, "duration-minutes": {"name": "Duration.minutes", "sig": "Duration.minutes(n) -> int", "tip": "A span of n minutes, in seconds.", "kind": "namespace-method", "page": "duration-minutes.html", "section": "Duration"}, "duration-hours": {"name": "Duration.hours", "sig": "Duration.hours(n) -> int", "tip": "A span of n hours, in seconds.", "kind": "namespace-method", "page": "duration-hours.html", "section": "Duration"}, "duration-days": {"name": "Duration.days", "sig": "Duration.days(n) -> int", "tip": "A span of n days, in seconds.", "kind": "namespace-method", "page": "duration-days.html", "section": "Duration"}, "duration-as_seconds": {"name": "Duration.as_seconds", "sig": "Duration.as_seconds(span) -> int", "tip": "The span in whole seconds.", "kind": "namespace-method", "page": "duration-as_seconds.html", "section": "Duration"}, "duration-as_minutes": {"name": "Duration.as_minutes", "sig": "Duration.as_minutes(span) -> int", "tip": "The span in whole minutes.", "kind": "namespace-method", "page": "duration-as_minutes.html", "section": "Duration"}, "duration-as_hours": {"name": "Duration.as_hours", "sig": "Duration.as_hours(span) -> int", "tip": "The span in whole hours.", "kind": "namespace-method", "page": "duration-as_hours.html", "section": "Duration"}, "duration-as_days": {"name": "Duration.as_days", "sig": "Duration.as_days(span) -> int", "tip": "The span in whole days.", "kind": "namespace-method", "page": "duration-as_days.html", "section": "Duration"}, "ease-in": {"name": "Ease.in", "sig": "Ease.in(t) -> fixed", "tip": "Accelerate from rest (quadratic ease-in).", "kind": "namespace-method", "page": "ease-in.html", "section": "Ease"}, "ease-out": {"name": "Ease.out", "sig": "Ease.out(t) -> fixed", "tip": "Decelerate to rest (quadratic ease-out).", "kind": "namespace-method", "page": "ease-out.html", "section": "Ease"}, "ease-in_out": {"name": "Ease.in_out", "sig": "Ease.in_out(t) -> fixed", "tip": "Ease in and then out (smoothstep).", "kind": "namespace-method", "page": "ease-in_out.html", "section": "Ease"}, "ease-back": {"name": "Ease.back", "sig": "Ease.back(t) -> fixed", "tip": "Ease in with a small backward anticipation.", "kind": "namespace-method", "page": "ease-back.html", "section": "Ease"}, "ease-bounce": {"name": "Ease.bounce", "sig": "Ease.bounce(t) -> fixed", "tip": "Ease out with a settling bounce.", "kind": "namespace-method", "page": "ease-bounce.html", "section": "Ease"}, "ease-elastic": {"name": "Ease.elastic", "sig": "Ease.elastic(t) -> fixed", "tip": "Ease out with a springy wobble.", "kind": "namespace-method", "page": "ease-elastic.html", "section": "Ease"}, "fs-exists": {"name": "Fs.exists", "sig": "Fs.exists(p) -> bool", "tip": "Does a path exist?", "kind": "namespace-method", "page": "fs-exists.html", "section": "Fs"}, "fs-is_dir": {"name": "Fs.is_dir", "sig": "Fs.is_dir(p) -> bool", "tip": "Is a path a directory?", "kind": "namespace-method", "page": "fs-is_dir.html", "section": "Fs"}, "fs-read_text": {"name": "Fs.read_text", "sig": "Fs.read_text(p) -> string", "tip": "Read a whole file as a string.", "kind": "namespace-method", "page": "fs-read_text.html", "section": "Fs"}, "fs-write_text": {"name": "Fs.write_text", "sig": "Fs.write_text(p, s) -> bool", "tip": "Write a string to a file (atomically).", "kind": "namespace-method", "page": "fs-write_text.html", "section": "Fs"}, "fs-append_text": {"name": "Fs.append_text", "sig": "Fs.append_text(p, s) -> bool", "tip": "Append a string to a file.", "kind": "namespace-method", "page": "fs-append_text.html", "section": "Fs"}, "fs-remove": {"name": "Fs.remove", "sig": "Fs.remove(p) -> bool", "tip": "Delete a file.", "kind": "namespace-method", "page": "fs-remove.html", "section": "Fs"}, "fs-size": {"name": "Fs.size", "sig": "Fs.size(p) -> int", "tip": "File size in bytes.", "kind": "namespace-method", "page": "fs-size.html", "section": "Fs"}, "fs-mkdir": {"name": "Fs.mkdir", "sig": "Fs.mkdir(p) -> bool", "tip": "Create a directory and its parents.", "kind": "namespace-method", "page": "fs-mkdir.html", "section": "Fs"}, "fs-copy": {"name": "Fs.copy", "sig": "Fs.copy(src, dst) -> bool", "tip": "Copy a file byte-for-byte.", "kind": "namespace-method", "page": "fs-copy.html", "section": "Fs"}, "fs-list": {"name": "Fs.list", "sig": "Fs.list(dir) -> []string", "tip": "Directory entries, sorted.", "kind": "namespace-method", "page": "fs-list.html", "section": "Fs"}, "hash-of": {"name": "Hash.of", "sig": "Hash.of(s) -> int", "tip": "The fast default string hash.", "kind": "namespace-method", "page": "hash-of.html", "section": "Hash"}, "hash-fnv1a": {"name": "Hash.fnv1a", "sig": "Hash.fnv1a(s) -> int", "tip": "FNV-1a 32-bit, named explicitly.", "kind": "namespace-method", "page": "hash-fnv1a.html", "section": "Hash"}, "hash-crc32": {"name": "Hash.crc32", "sig": "Hash.crc32(s) -> int", "tip": "CRC-32 checksum for corruption detection.", "kind": "namespace-method", "page": "hash-crc32.html", "section": "Hash"}, "hash-mix": {"name": "Hash.mix", "sig": "Hash.mix(x) -> int", "tip": "Avalanche one integer into a well-scrambled value.", "kind": "namespace-method", "page": "hash-mix.html", "section": "Hash"}, "hash-combine": {"name": "Hash.combine", "sig": "Hash.combine(...) -> int", "tip": "Fold several ints into one deterministic seed.", "kind": "namespace-method", "page": "hash-combine.html", "section": "Hash"}, "hash-of64": {"name": "Hash.of64", "sig": "Hash.of64(s) -> long", "tip": "The fast default 64-bit string hash.", "kind": "namespace-method", "page": "hash-of64.html", "section": "Hash"}, "hash-fnv1a_64": {"name": "Hash.fnv1a_64", "sig": "Hash.fnv1a_64(s) -> long", "tip": "FNV-1a 64-bit, named explicitly.", "kind": "namespace-method", "page": "hash-fnv1a_64.html", "section": "Hash"}, "hash-mix64": {"name": "Hash.mix64", "sig": "Hash.mix64(x) -> long", "tip": "Avalanche one 64-bit integer into a well-scrambled value.", "kind": "namespace-method", "page": "hash-mix64.html", "section": "Hash"}, "input-key": {"name": "Input.key", "sig": "Input.key() -> int", "tip": "The key pressed this frame as a character code (0 when nothing is pressed).", "kind": "namespace-method", "page": "input-key.html", "section": "Input"}, "input-bind": {"name": "Input.bind", "sig": "Input.bind(action: str, key: int) -> void", "tip": "Bind a physical key to a named action, so gameplay reads the action, not the key.", "kind": "namespace-method", "page": "input-bind.html", "section": "Input"}, "input-rebind": {"name": "Input.rebind", "sig": "Input.rebind(action: str, from: int, to: int) -> void", "tip": "Remap an action from one key to another at runtime (rebinding menus).", "kind": "namespace-method", "page": "input-rebind.html", "section": "Input"}, "input-poll": {"name": "Input.poll", "sig": "Input.poll() -> int", "tip": "Advance one frame of input; the single per-frame read behind actions and replay.", "kind": "namespace-method", "page": "input-poll.html", "section": "Input"}, "input-down": {"name": "Input.down", "sig": "Input.down(action: str) -> bool", "tip": "Is a named action held on the frame last polled?", "kind": "namespace-method", "page": "input-down.html", "section": "Input"}, "input-pressed": {"name": "Input.pressed", "sig": "Input.pressed(action: str) -> bool", "tip": "Did a named action go down this frame (a one-shot edge)?", "kind": "namespace-method", "page": "input-pressed.html", "section": "Input"}, "input-record": {"name": "Input.record", "sig": "Input.record() -> void", "tip": "Start recording polled input each frame (for deterministic replay).", "kind": "namespace-method", "page": "input-record.html", "section": "Input"}, "input-replay": {"name": "Input.replay", "sig": "Input.replay() -> void", "tip": "Replay recorded input; poll then reads the tape, not the device.", "kind": "namespace-method", "page": "input-replay.html", "section": "Input"}, "input-key_down": {"name": "Input.key_down", "sig": "Input.key_down(key) -> bool", "tip": "Is a physical key held this frame?", "kind": "namespace-method", "page": "input-key_down.html", "section": "Input"}, "input-key_pressed": {"name": "Input.key_pressed", "sig": "Input.key_pressed(key) -> bool", "tip": "Did a key go down this frame (edge)?", "kind": "namespace-method", "page": "input-key_pressed.html", "section": "Input"}, "input-key_released": {"name": "Input.key_released", "sig": "Input.key_released(key) -> bool", "tip": "Did a key go up this frame (edge)?", "kind": "namespace-method", "page": "input-key_released.html", "section": "Input"}, "input-key_label": {"name": "Input.key_label", "sig": "Input.key_label(key) -> string", "tip": "The name to show a player for a key - in their own keyboard layout.", "kind": "namespace-method", "page": "input-key_label.html", "section": "Input"}, "input-press": {"name": "Input.press", "sig": "Input.press(key)", "tip": "Inject a held key (AI, tutorial, testing, network).", "kind": "namespace-method", "page": "input-press.html", "section": "Input"}, "input-release": {"name": "Input.release", "sig": "Input.release(key)", "tip": "Release an injected key.", "kind": "namespace-method", "page": "input-release.html", "section": "Input"}, "input-axis": {"name": "Input.axis", "sig": "Input.axis(neg, pos) -> fixed", "tip": "A -1..+1 axis from two keys.", "kind": "namespace-method", "page": "input-axis.html", "section": "Input"}, "input-axis_i": {"name": "Input.axis_i", "sig": "Input.axis_i(neg, pos) -> int", "tip": "A -1/0/1 directional intent as a plain int, no bool->int glue.", "kind": "namespace-method", "page": "input-axis_i.html", "section": "Input"}, "input-vector": {"name": "Input.vector", "sig": "Input.vector(left, right, up, down) -> Vector", "tip": "A normalized 2D vector from four keys.", "kind": "namespace-method", "page": "input-vector.html", "section": "Input"}, "input-strength": {"name": "Input.strength", "sig": "Input.strength(action) -> fixed", "tip": "0..1 strength of a named action.", "kind": "namespace-method", "page": "input-strength.html", "section": "Input"}, "input-mouse_x": {"name": "Input.mouse_x", "sig": "Input.mouse_x() -> int", "tip": "The mouse x position this frame.", "kind": "namespace-method", "page": "input-mouse_x.html", "section": "Input"}, "input-mouse_y": {"name": "Input.mouse_y", "sig": "Input.mouse_y() -> int", "tip": "The mouse y position this frame.", "kind": "namespace-method", "page": "input-mouse_y.html", "section": "Input"}, "input-mouse_dx": {"name": "Input.mouse_dx", "sig": "Input.mouse_dx() -> int", "tip": "Mouse x movement since the last poll.", "kind": "namespace-method", "page": "input-mouse_dx.html", "section": "Input"}, "input-mouse_dy": {"name": "Input.mouse_dy", "sig": "Input.mouse_dy() -> int", "tip": "Mouse y movement since the last poll.", "kind": "namespace-method", "page": "input-mouse_dy.html", "section": "Input"}, "input-mouse_down": {"name": "Input.mouse_down", "sig": "Input.mouse_down(button) -> bool", "tip": "Is a mouse button held?", "kind": "namespace-method", "page": "input-mouse_down.html", "section": "Input"}, "input-wheel": {"name": "Input.wheel", "sig": "Input.wheel() -> int", "tip": "Scroll-wheel delta this frame.", "kind": "namespace-method", "page": "input-wheel.html", "section": "Input"}, "input-set_mouse": {"name": "Input.set_mouse", "sig": "Input.set_mouse(x, y, buttons, wheel)", "tip": "Inject the mouse state (headless / AI / testing).", "kind": "namespace-method", "page": "input-set_mouse.html", "section": "Input"}, "input-pad_connected": {"name": "Input.pad_connected", "sig": "Input.pad_connected(pad) -> bool", "tip": "Is a gamepad connected?", "kind": "namespace-method", "page": "input-pad_connected.html", "section": "Input"}, "input-pad_button": {"name": "Input.pad_button", "sig": "Input.pad_button(pad, button) -> bool", "tip": "Is a gamepad button held?", "kind": "namespace-method", "page": "input-pad_button.html", "section": "Input"}, "input-pad_axis": {"name": "Input.pad_axis", "sig": "Input.pad_axis(pad, axis) -> fixed", "tip": "A gamepad analog axis, -1..+1.", "kind": "namespace-method", "page": "input-pad_axis.html", "section": "Input"}, "input-set_pad": {"name": "Input.set_pad", "sig": "Input.set_pad(pad, connected, buttons, lx, ly, rx, ry)", "tip": "Inject a gamepad's whole state.", "kind": "namespace-method", "page": "input-set_pad.html", "section": "Input"}, "input-touch_count": {"name": "Input.touch_count", "sig": "Input.touch_count() -> int", "tip": "How many touch points are active?", "kind": "namespace-method", "page": "input-touch_count.html", "section": "Input"}, "input-touch_x": {"name": "Input.touch_x", "sig": "Input.touch_x(index) -> int", "tip": "The x of a touch point.", "kind": "namespace-method", "page": "input-touch_x.html", "section": "Input"}, "input-touch_y": {"name": "Input.touch_y", "sig": "Input.touch_y(index) -> int", "tip": "The y of a touch point.", "kind": "namespace-method", "page": "input-touch_y.html", "section": "Input"}, "input-set_touch": {"name": "Input.set_touch", "sig": "Input.set_touch(index, x, y, active)", "tip": "Inject a touch point.", "kind": "namespace-method", "page": "input-set_touch.html", "section": "Input"}, "input-action": {"name": "Input.action", "sig": "Input.action(action: str, key: int) -> void", "tip": "Register a default key binding for an action (kept if already bound).", "kind": "namespace-method", "page": "input-action.html", "section": "Input"}, "input-bind_pad": {"name": "Input.bind_pad", "sig": "Input.bind_pad(action: str, button: int) -> void", "tip": "Also fire a named action from a gamepad button (device-agnostic).", "kind": "namespace-method", "page": "input-bind_pad.html", "section": "Input"}, "input-active": {"name": "Input.active", "sig": "Input.active(action: str) -> bool", "tip": "Is a named action active right now (any bound key held, or pad button down)?", "kind": "namespace-method", "page": "input-active.html", "section": "Input"}, "input-just_pressed": {"name": "Input.just_pressed", "sig": "Input.just_pressed(action: str) -> bool", "tip": "Did a named action go active this frame (the deterministic on-press)?", "kind": "namespace-method", "page": "input-just_pressed.html", "section": "Input"}, "input-just_released": {"name": "Input.just_released", "sig": "Input.just_released(action: str) -> bool", "tip": "Did a named action go inactive this frame (the on-release edge)?", "kind": "namespace-method", "page": "input-just_released.html", "section": "Input"}, "input-cursor_mode": {"name": "Input.cursor_mode", "sig": "Input.cursor_mode(mode)", "tip": "Hide / lock / confine the OS mouse cursor to the window (windowed).", "kind": "namespace-method", "page": "input-cursor_mode.html", "section": "Input"}, "input-move_i": {"name": "Input.move_i", "sig": "Input.move_i() -> IVec2", "tip": "The standard top-down movement intent, -1/0/1 per axis.", "kind": "namespace-method", "page": "input-move_i.html", "section": "Input"}, "list-len": {"name": "List.len", "sig": "List.len(s) -> int", "tip": "The number of elements in a slice.", "kind": "namespace-method", "page": "list-len.html", "section": "List"}, "list-push": {"name": "List.push", "sig": "List.push(s, v) -> void", "tip": "Append an element to the end of a slice.", "kind": "namespace-method", "page": "list-push.html", "section": "List"}, "list-clear": {"name": "List.clear", "sig": "List.clear(s) -> void", "tip": "Remove all elements, keeping capacity.", "kind": "namespace-method", "page": "list-clear.html", "section": "List"}, "list-first": {"name": "List.first", "sig": "List.first(s) -> T", "tip": "The first element of a slice.", "kind": "namespace-method", "page": "list-first.html", "section": "List"}, "list-last": {"name": "List.last", "sig": "List.last(s) -> T", "tip": "The last element of a slice.", "kind": "namespace-method", "page": "list-last.html", "section": "List"}, "list-pop": {"name": "List.pop", "sig": "List.pop(s) -> T", "tip": "Remove and return the last element.", "kind": "namespace-method", "page": "list-pop.html", "section": "List"}, "list-swap": {"name": "List.swap", "sig": "List.swap(s, i, j) -> void", "tip": "Exchange the elements at two indices.", "kind": "namespace-method", "page": "list-swap.html", "section": "List"}, "list-contains": {"name": "List.contains", "sig": "List.contains(s, v) -> bool", "tip": "Whether a value appears in a slice.", "kind": "namespace-method", "page": "list-contains.html", "section": "List"}, "list-index_of": {"name": "List.index_of", "sig": "List.index_of(s, v) -> int", "tip": "The index of a value in a slice, or -1 if absent.", "kind": "namespace-method", "page": "list-index_of.html", "section": "List"}, "list-reverse": {"name": "List.reverse", "sig": "List.reverse(s) -> void", "tip": "Reverse the order of a slice in place.", "kind": "namespace-method", "page": "list-reverse.html", "section": "List"}, "list-insert": {"name": "List.insert", "sig": "List.insert(s, i, v) -> void", "tip": "Insert an element at an index, shifting the rest up.", "kind": "namespace-method", "page": "list-insert.html", "section": "List"}, "list-remove_at": {"name": "List.remove_at", "sig": "List.remove_at(s, i) -> void", "tip": "Remove the element at an index, shifting the rest down.", "kind": "namespace-method", "page": "list-remove_at.html", "section": "List"}, "list-remove": {"name": "List.remove", "sig": "List.remove(s, v) -> void", "tip": "Remove the first element equal to a value.", "kind": "namespace-method", "page": "list-remove.html", "section": "List"}, "list-sort": {"name": "List.sort", "sig": "List.sort(s) -> void", "tip": "Sort a slice in ascending order, in place.", "kind": "namespace-method", "page": "list-sort.html", "section": "List"}, "list-sort_by": {"name": "List.sort_by", "sig": "List.sort_by(s, key) -> void", "tip": "Sort a slice ascending by a key each element maps to.", "kind": "namespace-method", "page": "list-sort_by.html", "section": "List"}, "list-sort_desc_by": {"name": "List.sort_desc_by", "sig": "List.sort_desc_by(s, key) -> void", "tip": "Sort a slice descending by a key each element maps to.", "kind": "namespace-method", "page": "list-sort_desc_by.html", "section": "List"}, "list-sort_with": {"name": "List.sort_with", "sig": "List.sort_with(s, cmp) -> void", "tip": "Sort a slice with a full two-argument comparator.", "kind": "namespace-method", "page": "list-sort_with.html", "section": "List"}, "list-sample": {"name": "List.sample", "sig": "List.sample(pool, count) -> []int", "tip": "Random picks from an int slice, distinct while it can.", "kind": "namespace-method", "page": "list-sample.html", "section": "List"}, "log-trace": {"name": "Log.trace", "sig": "Log.trace(msg, [key, value]...) -> void", "tip": "Log at the trace level (0) — the most verbose.", "kind": "namespace-method", "page": "log-trace.html", "section": "Log"}, "log-debug": {"name": "Log.debug", "sig": "Log.debug(msg, [key, value]...) -> void", "tip": "Log at the debug level (1) — development detail.", "kind": "namespace-method", "page": "log-debug.html", "section": "Log"}, "log-info": {"name": "Log.info", "sig": "Log.info(msg, [key, value]...) -> void", "tip": "Log at the info level (2) — normal operation.", "kind": "namespace-method", "page": "log-info.html", "section": "Log"}, "log-warn": {"name": "Log.warn", "sig": "Log.warn(msg, [key, value]...) -> void", "tip": "Log at the warn level (3) — something looks wrong.", "kind": "namespace-method", "page": "log-warn.html", "section": "Log"}, "log-error": {"name": "Log.error", "sig": "Log.error(msg, [key, value]...) -> void", "tip": "Log at the error level (4) — a failure.", "kind": "namespace-method", "page": "log-error.html", "section": "Log"}, "log-set_level": {"name": "Log.set_level", "sig": "Log.set_level(n) -> void", "tip": "Show only messages at level n or above (0 = all).", "kind": "namespace-method", "page": "log-set_level.html", "section": "Log"}, "log-level": {"name": "Log.level", "sig": "Log.level() -> int", "tip": "The current logging threshold.", "kind": "namespace-method", "page": "log-level.html", "section": "Log"}, "math-min": {"name": "Math.min", "sig": "Math.min(a, b) -> int", "tip": "The smaller of two values.", "kind": "namespace-method", "page": "math-min.html", "section": "Math"}, "math-max": {"name": "Math.max", "sig": "Math.max(a, b) -> int", "tip": "The larger of two values.", "kind": "namespace-method", "page": "math-max.html", "section": "Math"}, "math-abs": {"name": "Math.abs", "sig": "Math.abs(x) -> int", "tip": "The magnitude of a value, dropping its sign.", "kind": "namespace-method", "page": "math-abs.html", "section": "Math"}, "math-clamp": {"name": "Math.clamp", "sig": "Math.clamp(v, lo, hi) -> int", "tip": "Constrain a value to the range [lo, hi].", "kind": "namespace-method", "page": "math-clamp.html", "section": "Math"}, "math-sign": {"name": "Math.sign", "sig": "Math.sign(x) -> int", "tip": "The sign of a value as -1, 0, or 1.", "kind": "namespace-method", "page": "math-sign.html", "section": "Math"}, "math-floor": {"name": "Math.floor", "sig": "Math.floor(x) -> int", "tip": "Round a fixed value down to the nearest whole int.", "kind": "namespace-method", "page": "math-floor.html", "section": "Math"}, "math-ceil": {"name": "Math.ceil", "sig": "Math.ceil(x) -> int", "tip": "Round a fixed value up to the nearest whole int.", "kind": "namespace-method", "page": "math-ceil.html", "section": "Math"}, "math-round": {"name": "Math.round", "sig": "Math.round(x) -> int", "tip": "Round a fixed value to the nearest whole int.", "kind": "namespace-method", "page": "math-round.html", "section": "Math"}, "math-lerp": {"name": "Math.lerp", "sig": "Math.lerp(a, b, t) -> fixed", "tip": "Blend between two values by a 0..1 amount.", "kind": "namespace-method", "page": "math-lerp.html", "section": "Math"}, "math-inverse_lerp": {"name": "Math.inverse_lerp", "sig": "Math.inverse_lerp(a, b, v) -> fixed", "tip": "Find where a value sits between two endpoints as a 0..1 fraction.", "kind": "namespace-method", "page": "math-inverse_lerp.html", "section": "Math"}, "math-remap": {"name": "Math.remap", "sig": "Math.remap(v, in0, in1, out0, out1) -> fixed", "tip": "Rescale a value from one range into another.", "kind": "namespace-method", "page": "math-remap.html", "section": "Math"}, "math-sqrt": {"name": "Math.sqrt", "sig": "Math.sqrt(x) -> fixed", "tip": "The square root of a fixed value.", "kind": "namespace-method", "page": "math-sqrt.html", "section": "Math"}, "math-sin": {"name": "Math.sin", "sig": "Math.sin(radians) -> fixed", "tip": "Sine of an angle in radians.", "kind": "namespace-method", "page": "math-sin.html", "section": "Math"}, "math-cos": {"name": "Math.cos", "sig": "Math.cos(radians) -> fixed", "tip": "Cosine of an angle in radians.", "kind": "namespace-method", "page": "math-cos.html", "section": "Math"}, "math-tan": {"name": "Math.tan", "sig": "Math.tan(radians) -> fixed", "tip": "Tangent of an angle in radians.", "kind": "namespace-method", "page": "math-tan.html", "section": "Math"}, "math-hypot": {"name": "Math.hypot", "sig": "Math.hypot(x, y) -> fixed", "tip": "Length of the vector (x, y).", "kind": "namespace-method", "page": "math-hypot.html", "section": "Math"}, "math-dist": {"name": "Math.dist", "sig": "Math.dist(x0, y0, x1, y1) -> fixed", "tip": "Distance between two points.", "kind": "namespace-method", "page": "math-dist.html", "section": "Math"}, "math-dist2": {"name": "Math.dist2", "sig": "Math.dist2(x0, y0, x1, y1) -> fixed", "tip": "Squared distance between two points.", "kind": "namespace-method", "page": "math-dist2.html", "section": "Math"}, "math-deg_to_rad": {"name": "Math.deg_to_rad", "sig": "Math.deg_to_rad(degrees) -> fixed", "tip": "Convert degrees to radians.", "kind": "namespace-method", "page": "math-deg_to_rad.html", "section": "Math"}, "math-rad_to_deg": {"name": "Math.rad_to_deg", "sig": "Math.rad_to_deg(radians) -> fixed", "tip": "Convert radians to degrees.", "kind": "namespace-method", "page": "math-rad_to_deg.html", "section": "Math"}, "math-posmod": {"name": "Math.posmod", "sig": "Math.posmod(a, m) -> int", "tip": "Modulo that is always non-negative.", "kind": "namespace-method", "page": "math-posmod.html", "section": "Math"}, "math-wrap": {"name": "Math.wrap", "sig": "Math.wrap(v, lo, hi) -> int", "tip": "Wrap a value into the range [lo, hi).", "kind": "namespace-method", "page": "math-wrap.html", "section": "Math"}, "math-ping_pong": {"name": "Math.ping_pong", "sig": "Math.ping_pong(t, len) -> int", "tip": "Bounce a counter back and forth in [0, len].", "kind": "namespace-method", "page": "math-ping_pong.html", "section": "Math"}, "math-snapped": {"name": "Math.snapped", "sig": "Math.snapped(v, step) -> fixed", "tip": "Round a value to the nearest multiple of step.", "kind": "namespace-method", "page": "math-snapped.html", "section": "Math"}, "math-move_toward": {"name": "Math.move_toward", "sig": "Math.move_toward(from, to, delta) -> fixed", "tip": "Step from one value toward another by at most delta.", "kind": "namespace-method", "page": "math-move_toward.html", "section": "Math"}, "math-smoothstep": {"name": "Math.smoothstep", "sig": "Math.smoothstep(e0, e1, x) -> fixed", "tip": "A smooth 0..1 ramp between two edges.", "kind": "namespace-method", "page": "math-smoothstep.html", "section": "Math"}, "math-atan2": {"name": "Math.atan2", "sig": "Math.atan2(y, x) -> fixed", "tip": "The angle of the vector (x, y), in radians.", "kind": "namespace-method", "page": "math-atan2.html", "section": "Math"}, "math-asin": {"name": "Math.asin", "sig": "Math.asin(x) -> fixed", "tip": "The arcsine of a value, in radians.", "kind": "namespace-method", "page": "math-asin.html", "section": "Math"}, "math-acos": {"name": "Math.acos", "sig": "Math.acos(x) -> fixed", "tip": "The arccosine of a value, in radians.", "kind": "namespace-method", "page": "math-acos.html", "section": "Math"}, "math-exp": {"name": "Math.exp", "sig": "Math.exp(x) -> fixed", "tip": "e raised to the power x.", "kind": "namespace-method", "page": "math-exp.html", "section": "Math"}, "math-log": {"name": "Math.log", "sig": "Math.log(x) -> fixed", "tip": "The natural logarithm of x.", "kind": "namespace-method", "page": "math-log.html", "section": "Math"}, "math-pow": {"name": "Math.pow", "sig": "Math.pow(base, exp) -> fixed", "tip": "base raised to a fixed exponent.", "kind": "namespace-method", "page": "math-pow.html", "section": "Math"}, "memory-bytes": {"name": "Memory.bytes", "sig": "Memory.bytes(n) -> pointer", "tip": "Allocate n bytes.", "kind": "namespace-method", "page": "memory-bytes.html", "section": "Memory"}, "memory-words": {"name": "Memory.words", "sig": "Memory.words(n) -> words", "tip": "Allocate n 32-bit words.", "kind": "namespace-method", "page": "memory-words.html", "section": "Memory"}, "memory-copy": {"name": "Memory.copy", "sig": "Memory.copy(dst, src, n) -> void", "tip": "Copy n bytes between buffers.", "kind": "namespace-method", "page": "memory-copy.html", "section": "Memory"}, "memory-fill": {"name": "Memory.fill", "sig": "Memory.fill(buf, value, n) -> void", "tip": "Set n bytes to a value.", "kind": "namespace-method", "page": "memory-fill.html", "section": "Memory"}, "memory-peek": {"name": "Memory.peek", "sig": "Memory.peek(buf, i) -> int", "tip": "Read one byte.", "kind": "namespace-method", "page": "memory-peek.html", "section": "Memory"}, "memory-poke": {"name": "Memory.poke", "sig": "Memory.poke(buf, i, value) -> void", "tip": "Write one byte.", "kind": "namespace-method", "page": "memory-poke.html", "section": "Memory"}, "mime-of": {"name": "Mime.of", "sig": "Mime.of(path) -> string", "tip": "MIME type from a file extension.", "kind": "namespace-method", "page": "mime-of.html", "section": "Mime"}, "mime-sniff": {"name": "Mime.sniff", "sig": "Mime.sniff(path) -> string", "tip": "MIME type refined by magic bytes.", "kind": "namespace-method", "page": "mime-sniff.html", "section": "Mime"}, "network-send": {"name": "Network.send", "sig": "Network.send(peer, buf, len) -> void", "tip": "Put a datagram on the wire.", "kind": "namespace-method", "page": "network-send.html", "section": "Network"}, "network-poll": {"name": "Network.poll", "sig": "Network.poll(buf, cap) -> int", "tip": "Read an inbound datagram.", "kind": "namespace-method", "page": "network-poll.html", "section": "Network"}, "network-serialize": {"name": "Network.serialize", "sig": "Network.serialize(entity, buf) -> int", "tip": "Serialize an entity's synced state.", "kind": "namespace-method", "page": "network-serialize.html", "section": "Network"}, "network-apply": {"name": "Network.apply", "sig": "Network.apply(entity, buf) -> int", "tip": "Apply serialized state to an entity.", "kind": "namespace-method", "page": "network-apply.html", "section": "Network"}, "network-owner": {"name": "Network.owner", "sig": "Network.owner(entity) -> int", "tip": "The peer that owns an entity.", "kind": "namespace-method", "page": "network-owner.html", "section": "Network"}, "network-set_owner": {"name": "Network.set_owner", "sig": "Network.set_owner(entity, peer) -> void", "tip": "Assign ownership of an entity.", "kind": "namespace-method", "page": "network-set_owner.html", "section": "Network"}, "network-is_server": {"name": "Network.is_server", "sig": "Network.is_server() -> bool", "tip": "Is this peer the server?", "kind": "namespace-method", "page": "network-is_server.html", "section": "Network"}, "network-is_owner": {"name": "Network.is_owner", "sig": "Network.is_owner(entity) -> bool", "tip": "Does this peer own the entity?", "kind": "namespace-method", "page": "network-is_owner.html", "section": "Network"}, "network-local_id": {"name": "Network.local_id", "sig": "Network.local_id() -> int", "tip": "This peer's own id.", "kind": "namespace-method", "page": "network-local_id.html", "section": "Network"}, "noise-value2": {"name": "Noise.value2", "sig": "Noise.value2(x, y, seed) -> fixed", "tip": "2D value (lattice) noise, deterministic, in [-1, 1].", "kind": "namespace-method", "page": "noise-value2.html", "section": "Noise"}, "noise-perlin2": {"name": "Noise.perlin2", "sig": "Noise.perlin2(x, y, seed) -> fixed", "tip": "2D Perlin gradient noise, deterministic, in [-1, 1].", "kind": "namespace-method", "page": "noise-perlin2.html", "section": "Noise"}, "noise-simplex2": {"name": "Noise.simplex2", "sig": "Noise.simplex2(x, y, seed) -> fixed", "tip": "2D simplex noise, the organic default, in [-1, 1].", "kind": "namespace-method", "page": "noise-simplex2.html", "section": "Noise"}, "noise-fbm2": {"name": "Noise.fbm2", "sig": "Noise.fbm2(x, y, seed, octaves) -> fixed", "tip": "Fractal Brownian motion — octaves of simplex, in [-1, 1].", "kind": "namespace-method", "page": "noise-fbm2.html", "section": "Noise"}, "noise-cellular2": {"name": "Noise.cellular2", "sig": "Noise.cellular2(x, y, seed) -> fixed", "tip": "Worley (cellular) F1 distance to the nearest cell point.", "kind": "namespace-method", "page": "noise-cellular2.html", "section": "Noise"}, "noise-cellular2_id": {"name": "Noise.cellular2_id", "sig": "Noise.cellular2_id(x, y, seed) -> int", "tip": "The id of the nearest Worley cell — stable per cell.", "kind": "namespace-method", "page": "noise-cellular2_id.html", "section": "Noise"}, "noise-unit": {"name": "Noise.unit", "sig": "Noise.unit(n) -> fixed", "tip": "Remap a [-1,1] noise sample to [0,1].", "kind": "namespace-method", "page": "noise-unit.html", "section": "Noise"}, "os-args": {"name": "Os.args", "sig": "Os.args() -> []string", "tip": "Every command-line argument, as a list.", "kind": "namespace-method", "page": "os-args.html", "section": "Os"}, "os-arg_count": {"name": "Os.arg_count", "sig": "Os.arg_count() -> int", "tip": "How many command-line arguments there are.", "kind": "namespace-method", "page": "os-arg_count.html", "section": "Os"}, "os-arg": {"name": "Os.arg", "sig": "Os.arg(i) -> string", "tip": "The i-th command-line argument.", "kind": "namespace-method", "page": "os-arg.html", "section": "Os"}, "os-env": {"name": "Os.env", "sig": "Os.env(name) -> string", "tip": "Read an environment variable (null if unset).", "kind": "namespace-method", "page": "os-env.html", "section": "Os"}, "os-env_or": {"name": "Os.env_or", "sig": "Os.env_or(name, fallback) -> string", "tip": "Read an environment variable, or a fallback.", "kind": "namespace-method", "page": "os-env_or.html", "section": "Os"}, "os-has_env": {"name": "Os.has_env", "sig": "Os.has_env(name) -> bool", "tip": "Is an environment variable set?", "kind": "namespace-method", "page": "os-has_env.html", "section": "Os"}, "os-set_env": {"name": "Os.set_env", "sig": "Os.set_env(name, value) -> bool", "tip": "Set an environment variable.", "kind": "namespace-method", "page": "os-set_env.html", "section": "Os"}, "os-unset_env": {"name": "Os.unset_env", "sig": "Os.unset_env(name) -> bool", "tip": "Remove an environment variable.", "kind": "namespace-method", "page": "os-unset_env.html", "section": "Os"}, "os-exit": {"name": "Os.exit", "sig": "Os.exit(code)", "tip": "Terminate the process with a status code.", "kind": "namespace-method", "page": "os-exit.html", "section": "Os"}, "os-platform": {"name": "Os.platform", "sig": "Os.platform() -> string", "tip": "The host platform id.", "kind": "namespace-method", "page": "os-platform.html", "section": "Os"}, "os-arch": {"name": "Os.arch", "sig": "Os.arch() -> string", "tip": "The host CPU architecture.", "kind": "namespace-method", "page": "os-arch.html", "section": "Os"}, "os-stdout_write": {"name": "Os.stdout_write", "sig": "Os.stdout_write(s)", "tip": "Write a string to standard output.", "kind": "namespace-method", "page": "os-stdout_write.html", "section": "Os"}, "os-stderr_write": {"name": "Os.stderr_write", "sig": "Os.stderr_write(s)", "tip": "Write a string to standard error.", "kind": "namespace-method", "page": "os-stderr_write.html", "section": "Os"}, "os-save_dir": {"name": "Os.save_dir", "sig": "Os.save_dir(app) -> string", "tip": "Per-user save directory for an app.", "kind": "namespace-method", "page": "os-save_dir.html", "section": "Os"}, "os-config_dir": {"name": "Os.config_dir", "sig": "Os.config_dir(app) -> string", "tip": "Per-user config directory for an app.", "kind": "namespace-method", "page": "os-config_dir.html", "section": "Os"}, "os-cache_dir": {"name": "Os.cache_dir", "sig": "Os.cache_dir(app) -> string", "tip": "Per-user cache directory for an app.", "kind": "namespace-method", "page": "os-cache_dir.html", "section": "Os"}, "os-temp_dir": {"name": "Os.temp_dir", "sig": "Os.temp_dir() -> string", "tip": "The system temporary directory.", "kind": "namespace-method", "page": "os-temp_dir.html", "section": "Os"}, "os-pid": {"name": "Os.pid", "sig": "Os.pid() -> int", "tip": "This process's id.", "kind": "namespace-method", "page": "os-pid.html", "section": "Os"}, "path-join": {"name": "Path.join", "sig": "Path.join(a, b) -> string", "tip": "Join two path segments with the separator.", "kind": "namespace-method", "page": "path-join.html", "section": "Path"}, "path-dir": {"name": "Path.dir", "sig": "Path.dir(p) -> string", "tip": "The directory part of a path.", "kind": "namespace-method", "page": "path-dir.html", "section": "Path"}, "path-base": {"name": "Path.base", "sig": "Path.base(p) -> string", "tip": "The final component of a path.", "kind": "namespace-method", "page": "path-base.html", "section": "Path"}, "path-ext": {"name": "Path.ext", "sig": "Path.ext(p) -> string", "tip": "The file extension, including the dot.", "kind": "namespace-method", "page": "path-ext.html", "section": "Path"}, "path-stem": {"name": "Path.stem", "sig": "Path.stem(p) -> string", "tip": "The final component without its extension.", "kind": "namespace-method", "page": "path-stem.html", "section": "Path"}, "path-normalize": {"name": "Path.normalize", "sig": "Path.normalize(p) -> string", "tip": "Collapse . / .. / duplicate separators.", "kind": "namespace-method", "page": "path-normalize.html", "section": "Path"}, "save-write": {"name": "Save.write", "sig": "Save.write() -> void", "tip": "Save a snapshot of the world.", "kind": "namespace-method", "page": "save-write.html", "section": "Save"}, "save-read": {"name": "Save.read", "sig": "Save.read() -> bool", "tip": "Restore the saved snapshot.", "kind": "namespace-method", "page": "save-read.html", "section": "Save"}, "system-run": {"name": "System.run", "sig": "System.run(command) -> int", "tip": "Run a shell command.", "kind": "namespace-method", "page": "system-run.html", "section": "System"}, "system-read_char": {"name": "System.read_char", "sig": "System.read_char() -> int", "tip": "Read one byte from standard input.", "kind": "namespace-method", "page": "system-read_char.html", "section": "System"}, "system-file_open": {"name": "System.file_open", "sig": "System.file_open(path, mode) -> pointer", "tip": "Open a file.", "kind": "namespace-method", "page": "system-file_open.html", "section": "System"}, "system-file_read": {"name": "System.file_read", "sig": "System.file_read(file, buf, n) -> int", "tip": "Read bytes from a file.", "kind": "namespace-method", "page": "system-file_read.html", "section": "System"}, "system-file_write": {"name": "System.file_write", "sig": "System.file_write(file, buf, n) -> int", "tip": "Write bytes to a file.", "kind": "namespace-method", "page": "system-file_write.html", "section": "System"}, "system-file_seek": {"name": "System.file_seek", "sig": "System.file_seek(file, offset, whence) -> int", "tip": "Move a file's read/write position.", "kind": "namespace-method", "page": "system-file_seek.html", "section": "System"}, "system-file_tell": {"name": "System.file_tell", "sig": "System.file_tell(file) -> int", "tip": "The current file position.", "kind": "namespace-method", "page": "system-file_tell.html", "section": "System"}, "system-file_close": {"name": "System.file_close", "sig": "System.file_close(file) -> void", "tip": "Close a file.", "kind": "namespace-method", "page": "system-file_close.html", "section": "System"}, "text-length": {"name": "Text.length", "sig": "Text.length(s) -> int", "tip": "The number of bytes in a string.", "kind": "namespace-method", "page": "text-length.html", "section": "Text"}, "text-char_at": {"name": "Text.char_at", "sig": "Text.char_at(s, i) -> int", "tip": "The byte value at an index, 0..255.", "kind": "namespace-method", "page": "text-char_at.html", "section": "Text"}, "text-slice": {"name": "Text.slice", "sig": "Text.slice(s, a, b) -> string", "tip": "A substring covering bytes [a, b).", "kind": "namespace-method", "page": "text-slice.html", "section": "Text"}, "text-starts_with": {"name": "Text.starts_with", "sig": "Text.starts_with(s, prefix) -> bool", "tip": "Whether a string begins with a given prefix.", "kind": "namespace-method", "page": "text-starts_with.html", "section": "Text"}, "text-ends_with": {"name": "Text.ends_with", "sig": "Text.ends_with(s, suffix) -> bool", "tip": "Whether a string ends with a given suffix.", "kind": "namespace-method", "page": "text-ends_with.html", "section": "Text"}, "text-contains": {"name": "Text.contains", "sig": "Text.contains(s, sub) -> bool", "tip": "Whether a substring appears anywhere in a string.", "kind": "namespace-method", "page": "text-contains.html", "section": "Text"}, "text-index_of": {"name": "Text.index_of", "sig": "Text.index_of(s, sub) -> int", "tip": "The byte index of a substring, or -1 if absent.", "kind": "namespace-method", "page": "text-index_of.html", "section": "Text"}, "text-equals": {"name": "Text.equals", "sig": "Text.equals(a, b) -> bool", "tip": "Whether two strings have identical bytes.", "kind": "namespace-method", "page": "text-equals.html", "section": "Text"}, "text-concat": {"name": "Text.concat", "sig": "Text.concat(a, b) -> string", "tip": "Join two strings into a new one.", "kind": "namespace-method", "page": "text-concat.html", "section": "Text"}, "text-to_int": {"name": "Text.to_int", "sig": "Text.to_int(s) -> int", "tip": "Parse a leading integer from a string.", "kind": "namespace-method", "page": "text-to_int.html", "section": "Text"}, "text-from_int": {"name": "Text.from_int", "sig": "Text.from_int(n) -> string", "tip": "Render an integer as a string.", "kind": "namespace-method", "page": "text-from_int.html", "section": "Text"}, "text-upper": {"name": "Text.upper", "sig": "Text.upper(s) -> string", "tip": "An uppercased copy of a string.", "kind": "namespace-method", "page": "text-upper.html", "section": "Text"}, "text-lower": {"name": "Text.lower", "sig": "Text.lower(s) -> string", "tip": "A lowercased copy of a string.", "kind": "namespace-method", "page": "text-lower.html", "section": "Text"}, "text-trim": {"name": "Text.trim", "sig": "Text.trim(s) -> string", "tip": "A copy with surrounding whitespace removed.", "kind": "namespace-method", "page": "text-trim.html", "section": "Text"}, "text-repeat": {"name": "Text.repeat", "sig": "Text.repeat(s, n) -> string", "tip": "A string repeated n times.", "kind": "namespace-method", "page": "text-repeat.html", "section": "Text"}, "text-pad_left": {"name": "Text.pad_left", "sig": "Text.pad_left(s, width) -> string", "tip": "Pad with spaces on the left to a width.", "kind": "namespace-method", "page": "text-pad_left.html", "section": "Text"}, "text-pad_right": {"name": "Text.pad_right", "sig": "Text.pad_right(s, width) -> string", "tip": "Pad with spaces on the right to a width.", "kind": "namespace-method", "page": "text-pad_right.html", "section": "Text"}, "text-split": {"name": "Text.split", "sig": "Text.split(s, sep) -> []string", "tip": "Split a string on a separator into a list.", "kind": "namespace-method", "page": "text-split.html", "section": "Text"}, "text-join": {"name": "Text.join", "sig": "Text.join(parts, sep) -> string", "tip": "Join a list of strings with a separator.", "kind": "namespace-method", "page": "text-join.html", "section": "Text"}, "text-replace": {"name": "Text.replace", "sig": "Text.replace(s, from, to) -> string", "tip": "Replace every occurrence of a substring.", "kind": "namespace-method", "page": "text-replace.html", "section": "Text"}, "time-frame": {"name": "Time.frame", "sig": "Time.frame() -> int", "tip": "The number of frames elapsed since start.", "kind": "namespace-method", "page": "time-frame.html", "section": "Time"}, "time-delta": {"name": "Time.delta", "sig": "Time.delta() -> fixed", "tip": "Seconds per frame (a fixed 1/60).", "kind": "namespace-method", "page": "time-delta.html", "section": "Time"}, "time-elapsed": {"name": "Time.elapsed", "sig": "Time.elapsed() -> fixed", "tip": "Seconds since start (deterministic).", "kind": "namespace-method", "page": "time-elapsed.html", "section": "Time"}, "time-now": {"name": "Time.now", "sig": "Time.now() -> int", "tip": "Wall-clock seconds (non-deterministic).", "kind": "namespace-method", "page": "time-now.html", "section": "Time"}, "time-since": {"name": "Time.since", "sig": "Time.since(past) -> int", "tip": "Seconds elapsed from a past instant to now (non-deterministic).", "kind": "namespace-method", "page": "time-since.html", "section": "Time"}, "unicode-len": {"name": "Unicode.len", "sig": "Unicode.len(s) -> int", "tip": "Number of code points in a string (not bytes).", "kind": "namespace-method", "page": "unicode-len.html", "section": "Unicode"}, "unicode-byte_len": {"name": "Unicode.byte_len", "sig": "Unicode.byte_len(s) -> int", "tip": "Number of bytes in a string.", "kind": "namespace-method", "page": "unicode-byte_len.html", "section": "Unicode"}, "unicode-is_valid_utf8": {"name": "Unicode.is_valid_utf8", "sig": "Unicode.is_valid_utf8(s) -> bool", "tip": "Is a string well-formed UTF-8?", "kind": "namespace-method", "page": "unicode-is_valid_utf8.html", "section": "Unicode"}, "unicode-char_at": {"name": "Unicode.char_at", "sig": "Unicode.char_at(s, i) -> int", "tip": "The i-th code point of a string.", "kind": "namespace-method", "page": "unicode-char_at.html", "section": "Unicode"}, "unicode-chars": {"name": "Unicode.chars", "sig": "Unicode.chars(s) -> []int", "tip": "Every code point of a string, in order.", "kind": "namespace-method", "page": "unicode-chars.html", "section": "Unicode"}, "unicode-upper": {"name": "Unicode.upper", "sig": "Unicode.upper(s) -> string", "tip": "Uppercase a string.", "kind": "namespace-method", "page": "unicode-upper.html", "section": "Unicode"}, "unicode-lower": {"name": "Unicode.lower", "sig": "Unicode.lower(s) -> string", "tip": "Lowercase a string.", "kind": "namespace-method", "page": "unicode-lower.html", "section": "Unicode"}, "unicode-truncate": {"name": "Unicode.truncate", "sig": "Unicode.truncate(s, n) -> string", "tip": "First n code points of a string.", "kind": "namespace-method", "page": "unicode-truncate.html", "section": "Unicode"}, "unicode-grapheme_len": {"name": "Unicode.grapheme_len", "sig": "Unicode.grapheme_len(s) -> int", "tip": "Number of user-perceived characters.", "kind": "namespace-method", "page": "unicode-grapheme_len.html", "section": "Unicode"}, "uuid-new": {"name": "Uuid.new", "sig": "Uuid.new() -> string", "tip": "A new random (v4) UUID as a canonical 36-char string.", "kind": "namespace-method", "page": "uuid-new.html", "section": "Uuid"}, "uuid-v4": {"name": "Uuid.v4", "sig": "Uuid.v4() -> string", "tip": "Explicit alias for Uuid.new — a random (v4) UUID.", "kind": "namespace-method", "page": "uuid-v4.html", "section": "Uuid"}, "uuid-new_v7": {"name": "Uuid.new_v7", "sig": "Uuid.new_v7() -> string", "tip": "A new time-ordered (v7) UUID; sorts by creation time.", "kind": "namespace-method", "page": "uuid-new_v7.html", "section": "Uuid"}, "uuid-v7": {"name": "Uuid.v7", "sig": "Uuid.v7() -> string", "tip": "Explicit alias for Uuid.new_v7 — a time-ordered UUID.", "kind": "namespace-method", "page": "uuid-v7.html", "section": "Uuid"}, "uuid-parse": {"name": "Uuid.parse", "sig": "Uuid.parse(s) -> string", "tip": "Normalise an untrusted string to a lowercase UUID, or the nil UUID.", "kind": "namespace-method", "page": "uuid-parse.html", "section": "Uuid"}, "uuid-is_valid": {"name": "Uuid.is_valid", "sig": "Uuid.is_valid(s) -> bool", "tip": "Is s a well-formed UUID string?", "kind": "namespace-method", "page": "uuid-is_valid.html", "section": "Uuid"}, "uuid-to_text": {"name": "Uuid.to_text", "sig": "Uuid.to_text(id) -> string", "tip": "The canonical text form of a UUID.", "kind": "namespace-method", "page": "uuid-to_text.html", "section": "Uuid"}, "uuid-equals": {"name": "Uuid.equals", "sig": "Uuid.equals(a, b) -> bool", "tip": "Case-insensitive UUID equality.", "kind": "namespace-method", "page": "uuid-equals.html", "section": "Uuid"}, "uuid-nil": {"name": "Uuid.nil", "sig": "Uuid.nil() -> string", "tip": "The all-zero UUID.", "kind": "namespace-method", "page": "uuid-nil.html", "section": "Uuid"}, "world-get": {"name": "World.get", "sig": "World.get(prop_id, field_id, entity) -> int", "tip": "Read a component field by id.", "kind": "namespace-method", "page": "world-get.html", "section": "World"}, "world-set": {"name": "World.set", "sig": "World.set(prop_id, field_id, entity, value) -> void", "tip": "Write a component field by id.", "kind": "namespace-method", "page": "world-set.html", "section": "World"}, "world-has": {"name": "World.has", "sig": "World.has(entity, prop_id) -> int", "tip": "Does an entity have a component?", "kind": "namespace-method", "page": "world-has.html", "section": "World"}, "world-count": {"name": "World.count", "sig": "World.count() -> int", "tip": "The number of live entities.", "kind": "namespace-method", "page": "world-count.html", "section": "World"}, "world-size": {"name": "World.size", "sig": "World.size() -> int", "tip": "The serialized size of the world.", "kind": "namespace-method", "page": "world-size.html", "section": "World"}, "world-spawn": {"name": "World.spawn", "sig": "World.spawn(model_id) -> entity", "tip": "Spawn an entity of a model by id.", "kind": "namespace-method", "page": "world-spawn.html", "section": "World"}, "world-save": {"name": "World.save", "sig": "World.save(buf) -> int", "tip": "Serialize the world into a buffer.", "kind": "namespace-method", "page": "world-save.html", "section": "World"}, "world-load": {"name": "World.load", "sig": "World.load(buf, len) -> void", "tip": "Restore the world from a buffer.", "kind": "namespace-method", "page": "world-load.html", "section": "World"}, "world-prop_id": {"name": "World.prop_id", "sig": "World.prop_id(name) -> int", "tip": "Look up a component's id by name.", "kind": "namespace-method", "page": "world-prop_id.html", "section": "World"}, "world-field_id": {"name": "World.field_id", "sig": "World.field_id(prop_id, name) -> int", "tip": "Look up a field's id by name.", "kind": "namespace-method", "page": "world-field_id.html", "section": "World"}, "world-model_id": {"name": "World.model_id", "sig": "World.model_id(name) -> int", "tip": "Look up a model's id by name.", "kind": "namespace-method", "page": "world-model_id.html", "section": "World"}, "world-kind": {"name": "World.kind", "sig": "World.kind(entity) -> int", "tip": "The model id of an entity.", "kind": "namespace-method", "page": "world-kind.html", "section": "World"}, "world-register_prop": {"name": "World.register_prop", "sig": "World.register_prop(name, size) -> int", "tip": "Register a new component at runtime.", "kind": "namespace-method", "page": "world-register_prop.html", "section": "World"}, "world-attach": {"name": "World.attach", "sig": "World.attach(entity, prop_id) -> void", "tip": "Attach a component to an entity at runtime.", "kind": "namespace-method", "page": "world-attach.html", "section": "World"}, "world-detach": {"name": "World.detach", "sig": "World.detach(entity, prop_id) -> void", "tip": "Detach a component from an entity at runtime.", "kind": "namespace-method", "page": "world-detach.html", "section": "World"}, "world-query_next": {"name": "World.query_next", "sig": "World.query_next(query_id, cursor) -> int", "tip": "Advance a reflective query.", "kind": "namespace-method", "page": "world-query_next.html", "section": "World"}, "world-despawn": {"name": "World.despawn", "sig": "World.despawn(entity)", "tip": "Despawn an entity by id — runs its @OnDespawn hooks and frees the slot.", "kind": "namespace-method", "page": "world-despawn.html", "section": "World"}, "audio-load": {"name": "Audio.load", "sig": "Audio.load(path) -> int", "tip": "Load a sound file into a handle.", "kind": "namespace-method", "page": "audio-load.html", "section": "Audio"}, "audio-play": {"name": "Audio.play", "sig": "Audio.play(id) -> void", "tip": "Fire a one-shot sound.", "kind": "namespace-method", "page": "audio-play.html", "section": "Audio"}, "audio-play_at": {"name": "Audio.play_at", "sig": "Audio.play_at(id, gain, pitch, pan) -> void", "tip": "Fire a one-shot with its own gain, pitch and stereo position.", "kind": "namespace-method", "page": "audio-play_at.html", "section": "Audio"}, "audio-play_sound": {"name": "Audio.play_sound", "sig": "Audio.play_sound(id) -> void", "tip": "Fire a one-shot sound (alias of play).", "kind": "namespace-method", "page": "audio-play_sound.html", "section": "Audio"}, "audio-play_music": {"name": "Audio.play_music", "sig": "Audio.play_music(id) -> void", "tip": "Loop a sound as background music.", "kind": "namespace-method", "page": "audio-play_music.html", "section": "Audio"}, "audio-stop": {"name": "Audio.stop", "sig": "Audio.stop(id) -> void", "tip": "Stop one sound.", "kind": "namespace-method", "page": "audio-stop.html", "section": "Audio"}, "audio-stop_music": {"name": "Audio.stop_music", "sig": "Audio.stop_music() -> void", "tip": "Stop the music channel.", "kind": "namespace-method", "page": "audio-stop_music.html", "section": "Audio"}, "audio-stop_all": {"name": "Audio.stop_all", "sig": "Audio.stop_all() -> void", "tip": "Stop every sound.", "kind": "namespace-method", "page": "audio-stop_all.html", "section": "Audio"}, "audio-volume": {"name": "Audio.volume", "sig": "Audio.volume(v) -> void", "tip": "Set the master volume.", "kind": "namespace-method", "page": "audio-volume.html", "section": "Audio"}, "audio-pitch": {"name": "Audio.pitch", "sig": "Audio.pitch(v) -> void", "tip": "Set the playback rate / pitch.", "kind": "namespace-method", "page": "audio-pitch.html", "section": "Audio"}, "audio-is_playing": {"name": "Audio.is_playing", "sig": "Audio.is_playing(id) -> bool", "tip": "Is this sound playing?", "kind": "namespace-method", "page": "audio-is_playing.html", "section": "Audio"}, "audio-define": {"name": "Audio.define", "sig": "Audio.define(name: s, path: p) -> int", "tip": "Load a sound into the bank under a name.", "kind": "namespace-method", "page": "audio-define.html", "section": "Audio"}, "audio-named": {"name": "Audio.named", "sig": "Audio.named(name: s) -> int", "tip": "The handle registered under a name, or 0.", "kind": "namespace-method", "page": "audio-named.html", "section": "Audio"}, "random-range": {"name": "Random.range", "sig": "Random.range(low, high) -> int", "tip": "A random integer in the inclusive range [low, high].", "kind": "namespace-method", "page": "random-range.html", "section": "Random"}, "random-chance": {"name": "Random.chance", "sig": "Random.chance(percent) -> bool", "tip": "Return true with the given percent probability.", "kind": "namespace-method", "page": "random-chance.html", "section": "Random"}, "random-seed": {"name": "Random.seed", "sig": "Random.seed(value)", "tip": "Seed the random generator so runs are reproducible.", "kind": "namespace-method", "page": "random-seed.html", "section": "Random"}, "random-value": {"name": "Random.value", "sig": "Random.value() -> fixed", "tip": "A random fixed value in [0, 1).", "kind": "namespace-method", "page": "random-value.html", "section": "Random"}, "random-int": {"name": "Random.int", "sig": "Random.int(max) -> int", "tip": "A random integer in [0, max).", "kind": "namespace-method", "page": "random-int.html", "section": "Random"}, "random-sign": {"name": "Random.sign", "sig": "Random.sign() -> int", "tip": "A random +1 or -1.", "kind": "namespace-method", "page": "random-sign.html", "section": "Random"}, "random-weighted": {"name": "Random.weighted", "sig": "Random.weighted(weights: []int) -> int", "tip": "An index drawn in proportion to its weight.", "kind": "namespace-method", "page": "random-weighted.html", "section": "Random"}, "vector-make": {"name": "Vector.make", "sig": "Vector.make(x, y) -> Vector", "tip": "Build a vector from x and y components.", "kind": "namespace-method", "page": "vector-make.html", "section": "Vector"}, "vector-zero": {"name": "Vector.zero", "sig": "Vector.zero() -> Vector", "tip": "The zero vector, (0, 0).", "kind": "namespace-method", "page": "vector-zero.html", "section": "Vector"}, "vector-x": {"name": "Vector.x", "sig": "Vector.x(v) -> fixed", "tip": "The x component of a vector.", "kind": "namespace-method", "page": "vector-x.html", "section": "Vector"}, "vector-y": {"name": "Vector.y", "sig": "Vector.y(v) -> fixed", "tip": "The y component of a vector.", "kind": "namespace-method", "page": "vector-y.html", "section": "Vector"}, "vector-add": {"name": "Vector.add", "sig": "Vector.add(a, b) -> Vector", "tip": "Component-wise sum a + b.", "kind": "namespace-method", "page": "vector-add.html", "section": "Vector"}, "vector-sub": {"name": "Vector.sub", "sig": "Vector.sub(a, b) -> Vector", "tip": "Component-wise difference a - b.", "kind": "namespace-method", "page": "vector-sub.html", "section": "Vector"}, "vector-scale": {"name": "Vector.scale", "sig": "Vector.scale(v, s) -> Vector", "tip": "Scale a vector by a scalar.", "kind": "namespace-method", "page": "vector-scale.html", "section": "Vector"}, "vector-dot": {"name": "Vector.dot", "sig": "Vector.dot(a, b) -> fixed", "tip": "The dot product of two vectors.", "kind": "namespace-method", "page": "vector-dot.html", "section": "Vector"}, "vector-length": {"name": "Vector.length", "sig": "Vector.length(v) -> fixed", "tip": "The length (magnitude) of a vector.", "kind": "namespace-method", "page": "vector-length.html", "section": "Vector"}, "vector-distance": {"name": "Vector.distance", "sig": "Vector.distance(a, b) -> fixed", "tip": "The distance between two points.", "kind": "namespace-method", "page": "vector-distance.html", "section": "Vector"}, "vector-normalize": {"name": "Vector.normalize", "sig": "Vector.normalize(v) -> Vector", "tip": "A unit vector in the same direction.", "kind": "namespace-method", "page": "vector-normalize.html", "section": "Vector"}, "vector-lerp": {"name": "Vector.lerp", "sig": "Vector.lerp(a, b, t) -> Vector", "tip": "Linear interpolation between two vectors.", "kind": "namespace-method", "page": "vector-lerp.html", "section": "Vector"}, "vector-rotate": {"name": "Vector.rotate", "sig": "Vector.rotate(v, angle) -> Vector", "tip": "Rotate a vector by an angle in radians.", "kind": "namespace-method", "page": "vector-rotate.html", "section": "Vector"}, "vector-angle": {"name": "Vector.angle", "sig": "Vector.angle(v) -> fixed", "tip": "The angle of a vector in radians.", "kind": "namespace-method", "page": "vector-angle.html", "section": "Vector"}, "vector-from_angle": {"name": "Vector.from_angle", "sig": "Vector.from_angle(a) -> Vector", "tip": "A unit vector pointing at an angle.", "kind": "namespace-method", "page": "vector-from_angle.html", "section": "Vector"}, "http-get": {"name": "Http.get", "sig": "Http.get(url) -> int", "tip": "Start an async GET request.", "kind": "namespace-method", "page": "http-get.html", "section": "Http"}, "http-post": {"name": "Http.post", "sig": "Http.post(url, body) -> int", "tip": "Start an async POST with a body.", "kind": "namespace-method", "page": "http-post.html", "section": "Http"}, "http-request": {"name": "Http.request", "sig": "Http.request(method, url, body) -> int", "tip": "Start a request with any method.", "kind": "namespace-method", "page": "http-request.html", "section": "Http"}, "http-open": {"name": "Http.open", "sig": "Http.open(method, url) -> int", "tip": "Open a request to configure before sending.", "kind": "namespace-method", "page": "http-open.html", "section": "Http"}, "http-set": {"name": "Http.set", "sig": "Http.set(handle, name, value) -> void", "tip": "Set a request header before sending.", "kind": "namespace-method", "page": "http-set.html", "section": "Http"}, "http-body": {"name": "Http.body", "sig": "Http.body(handle, body) -> void", "tip": "Set a string request body.", "kind": "namespace-method", "page": "http-body.html", "section": "Http"}, "http-body_bytes": {"name": "Http.body_bytes", "sig": "Http.body_bytes(handle, bytes, len) -> void", "tip": "Set a raw byte request body.", "kind": "namespace-method", "page": "http-body_bytes.html", "section": "Http"}, "http-send": {"name": "Http.send", "sig": "Http.send(handle) -> void", "tip": "Dispatch an opened request.", "kind": "namespace-method", "page": "http-send.html", "section": "Http"}, "http-poll": {"name": "Http.poll", "sig": "Http.poll(handle) -> int", "tip": "Poll a request; -1 pending, 0 error, else status.", "kind": "namespace-method", "page": "http-poll.html", "section": "Http"}, "http-status": {"name": "Http.status", "sig": "Http.status(handle) -> int", "tip": "The response HTTP status code.", "kind": "namespace-method", "page": "http-status.html", "section": "Http"}, "http-ok": {"name": "Http.ok", "sig": "Http.ok(handle) -> bool", "tip": "Was the response status 2xx?", "kind": "namespace-method", "page": "http-ok.html", "section": "Http"}, "http-text": {"name": "Http.text", "sig": "Http.text(handle) -> str", "tip": "The response body as text.", "kind": "namespace-method", "page": "http-text.html", "section": "Http"}, "http-body_len": {"name": "Http.body_len", "sig": "Http.body_len(handle) -> int", "tip": "The response body length in bytes.", "kind": "namespace-method", "page": "http-body_len.html", "section": "Http"}, "http-header": {"name": "Http.header", "sig": "Http.header(handle, name) -> str", "tip": "A response header value (case-insensitive).", "kind": "namespace-method", "page": "http-header.html", "section": "Http"}, "http-free": {"name": "Http.free", "sig": "Http.free(handle) -> void", "tip": "Release a request's resources.", "kind": "namespace-method", "page": "http-free.html", "section": "Http"}, "http-parse": {"name": "Http.parse", "sig": "Http.parse(bytes, len) -> int", "tip": "Parse a raw HTTP response into a handle.", "kind": "namespace-method", "page": "http-parse.html", "section": "Http"}, "http-save_to": {"name": "Http.save_to", "sig": "Http.save_to(handle, path) -> void", "tip": "Stream the response body to a file.", "kind": "namespace-method", "page": "http-save_to.html", "section": "Http"}, "http-received": {"name": "Http.received", "sig": "Http.received(handle) -> int", "tip": "Body bytes received so far.", "kind": "namespace-method", "page": "http-received.html", "section": "Http"}, "http-expected": {"name": "Http.expected", "sig": "Http.expected(handle) -> int", "tip": "The body length the server announced, or -1.", "kind": "namespace-method", "page": "http-expected.html", "section": "Http"}, "ivec2-make": {"name": "IVec2.make", "sig": "IVec2.make(x, y) -> IVec2", "tip": "Build an integer vector from x and y components.", "kind": "namespace-method", "page": "ivec2-make.html", "section": "IVec2"}, "ivec2-zero": {"name": "IVec2.zero", "sig": "IVec2.zero() -> IVec2", "tip": "The origin cell, (0, 0).", "kind": "namespace-method", "page": "ivec2-zero.html", "section": "IVec2"}, "ivec2-x": {"name": "IVec2.x", "sig": "IVec2.x(v) -> int", "tip": "The x component of an integer vector.", "kind": "namespace-method", "page": "ivec2-x.html", "section": "IVec2"}, "ivec2-y": {"name": "IVec2.y", "sig": "IVec2.y(v) -> int", "tip": "The y component of an integer vector.", "kind": "namespace-method", "page": "ivec2-y.html", "section": "IVec2"}, "ivec2-add": {"name": "IVec2.add", "sig": "IVec2.add(a, b) -> IVec2", "tip": "Component-wise sum of two integer vectors.", "kind": "namespace-method", "page": "ivec2-add.html", "section": "IVec2"}, "ivec2-sub": {"name": "IVec2.sub", "sig": "IVec2.sub(a, b) -> IVec2", "tip": "Component-wise difference of two integer vectors.", "kind": "namespace-method", "page": "ivec2-sub.html", "section": "IVec2"}, "ivec2-scale": {"name": "IVec2.scale", "sig": "IVec2.scale(v, s) -> IVec2", "tip": "Multiply both components by an integer.", "kind": "namespace-method", "page": "ivec2-scale.html", "section": "IVec2"}, "ivec2-dot": {"name": "IVec2.dot", "sig": "IVec2.dot(a, b) -> int", "tip": "The dot product ax*bx + ay*by.", "kind": "namespace-method", "page": "ivec2-dot.html", "section": "IVec2"}, "ivec2-equal": {"name": "IVec2.equal", "sig": "IVec2.equal(a, b) -> bool", "tip": "True when both components match.", "kind": "namespace-method", "page": "ivec2-equal.html", "section": "IVec2"}, "ivec2-manhattan": {"name": "IVec2.manhattan", "sig": "IVec2.manhattan(a, b) -> int", "tip": "Grid distance |dx| + |dy|.", "kind": "namespace-method", "page": "ivec2-manhattan.html", "section": "IVec2"}, "ivec2-to_vector": {"name": "IVec2.to_vector", "sig": "IVec2.to_vector(v) -> Vector", "tip": "Widen to a fixed-point Vector.", "kind": "namespace-method", "page": "ivec2-to_vector.html", "section": "IVec2"}, "ivec2-distance2": {"name": "IVec2.distance2", "sig": "IVec2.distance2(a, b) -> int", "tip": "The squared distance between two points.", "kind": "namespace-method", "page": "ivec2-distance2.html", "section": "IVec2"}, "ivec2-within": {"name": "IVec2.within", "sig": "IVec2.within(a, b, radius) -> bool", "tip": "Are two points within a radius of each other?", "kind": "namespace-method", "page": "ivec2-within.html", "section": "IVec2"}, "ivec2-heading": {"name": "IVec2.heading", "sig": "IVec2.heading(from, to) -> int", "tip": "The direction from one point to another, in degrees.", "kind": "namespace-method", "page": "ivec2-heading.html", "section": "IVec2"}, "ivec2-along": {"name": "IVec2.along", "sig": "IVec2.along(origin, degrees, distance) -> IVec2", "tip": "The point a distance along a heading.", "kind": "namespace-method", "page": "ivec2-along.html", "section": "IVec2"}, "ivec2-step": {"name": "IVec2.step", "sig": "IVec2.step(degrees) -> IVec2", "tip": "A -1/0/1 unit step along a heading.", "kind": "namespace-method", "page": "ivec2-step.html", "section": "IVec2"}, "map-size": {"name": "Map.size", "sig": "Map.size(width, height)", "tip": "Set the tilemap dimensions in cells before filling rows.", "kind": "namespace-method", "page": "map-size.html", "section": "Map — tilemap"}, "map-row": {"name": "Map.row", "sig": "Map.row(y, cells)", "tip": "Fill one row of the tilemap from a string of tile characters.", "kind": "namespace-method", "page": "map-row.html", "section": "Map — tilemap"}, "map-tile": {"name": "Map.tile", "sig": "Map.tile(x, y) -> int", "tip": "Read the tile character at a cell (out-of-bounds reads answer '#').", "kind": "namespace-method", "page": "map-tile.html", "section": "Map — tilemap"}, "map-get": {"name": "Map.get", "sig": "Map.get(x: tx, y: ty) -> int", "tip": "The glyph at a cell ('#' outside the map).", "kind": "namespace-method", "page": "map-get.html", "section": "Map — tilemap"}, "map-set": {"name": "Map.set", "sig": "Map.set(x: tx, y: ty, glyph: g)", "tip": "Write one cell in place.", "kind": "namespace-method", "page": "map-set.html", "section": "Map — tilemap"}, "map-fill": {"name": "Map.fill", "sig": "Map.fill(glyph: g)", "tip": "Every cell becomes the glyph.", "kind": "namespace-method", "page": "map-fill.html", "section": "Map — tilemap"}, "map-rect": {"name": "Map.rect", "sig": "Map.rect(x: tx, y: ty, width: w, height: h, glyph: g)", "tip": "Fill a rectangle of cells.", "kind": "namespace-method", "page": "map-rect.html", "section": "Map — tilemap"}, "map-border": {"name": "Map.border", "sig": "Map.border(glyph: g)", "tip": "The outermost ring of cells.", "kind": "namespace-method", "page": "map-border.html", "section": "Map — tilemap"}, "map-random_cell": {"name": "Map.random_cell", "sig": "Map.random_cell(glyph: g) -> IVec2", "tip": "A random cell holding the glyph (seeded RNG).", "kind": "namespace-method", "page": "map-random_cell.html", "section": "Map — tilemap"}, "map-is_solid": {"name": "Map.is_solid", "sig": "Map.is_solid(x: tx, y: ty) -> bool", "tip": "Is a cell solid, per the Solids config?", "kind": "namespace-method", "page": "map-is_solid.html", "section": "Map — tilemap"}, "map-is_solid_at": {"name": "Map.is_solid_at", "sig": "Map.is_solid_at(x: px, y: py) -> bool", "tip": "Is the cell under a pixel position solid?", "kind": "namespace-method", "page": "map-is_solid_at.html", "section": "Map — tilemap"}, "map-width": {"name": "Map.width", "sig": "Map.width() -> int", "tip": "The map's width in cells.", "kind": "namespace-method", "page": "map-width.html", "section": "Map — tilemap"}, "map-height": {"name": "Map.height", "sig": "Map.height() -> int", "tip": "The map's height in cells.", "kind": "namespace-method", "page": "map-height.html", "section": "Map — tilemap"}, "map-random_cell_far": {"name": "Map.random_cell_far", "sig": "Map.random_cell_far(glyph:, from:, min_tiles:) -> IVec2", "tip": "A random cell with the glyph, at least a distance from a point.", "kind": "namespace-method", "page": "map-random_cell_far.html", "section": "Map — tilemap"}, "map-to_tile": {"name": "Map.to_tile", "sig": "Map.to_tile(pixel: IVec2) -> IVec2", "tip": "The tile under a pixel position.", "kind": "namespace-method", "page": "map-to_tile.html", "section": "Map — tilemap"}, "rect-make": {"name": "Rect.make", "sig": "Rect.make(x, y, w, h) -> Rect", "tip": "Build a rectangle from a corner and a size.", "kind": "namespace-method", "page": "rect-make.html", "section": "Rect"}, "rect-x": {"name": "Rect.x", "sig": "Rect.x(r) -> fixed", "tip": "The left edge (x position).", "kind": "namespace-method", "page": "rect-x.html", "section": "Rect"}, "rect-y": {"name": "Rect.y", "sig": "Rect.y(r) -> fixed", "tip": "The top edge (y position).", "kind": "namespace-method", "page": "rect-y.html", "section": "Rect"}, "rect-w": {"name": "Rect.w", "sig": "Rect.w(r) -> fixed", "tip": "The width.", "kind": "namespace-method", "page": "rect-w.html", "section": "Rect"}, "rect-h": {"name": "Rect.h", "sig": "Rect.h(r) -> fixed", "tip": "The height.", "kind": "namespace-method", "page": "rect-h.html", "section": "Rect"}, "rect-right": {"name": "Rect.right", "sig": "Rect.right(r) -> fixed", "tip": "The right edge, x + w.", "kind": "namespace-method", "page": "rect-right.html", "section": "Rect"}, "rect-bottom": {"name": "Rect.bottom", "sig": "Rect.bottom(r) -> fixed", "tip": "The bottom edge, y + h.", "kind": "namespace-method", "page": "rect-bottom.html", "section": "Rect"}, "rect-center": {"name": "Rect.center", "sig": "Rect.center(r) -> Vector", "tip": "The center point as a Vector.", "kind": "namespace-method", "page": "rect-center.html", "section": "Rect"}, "rect-contains": {"name": "Rect.contains", "sig": "Rect.contains(r, px, py) -> bool", "tip": "True when the point is inside.", "kind": "namespace-method", "page": "rect-contains.html", "section": "Rect"}, "rect-intersects": {"name": "Rect.intersects", "sig": "Rect.intersects(a, b) -> bool", "tip": "True when two rectangles overlap.", "kind": "namespace-method", "page": "rect-intersects.html", "section": "Rect"}, "udp-open": {"name": "Udp.open", "sig": "Udp.open(port) -> int", "tip": "Open a socket on a port.", "kind": "namespace-method", "page": "udp-open.html", "section": "Udp"}, "udp-port": {"name": "Udp.port", "sig": "Udp.port(socket) -> int", "tip": "The port a socket is bound to.", "kind": "namespace-method", "page": "udp-port.html", "section": "Udp"}, "udp-send": {"name": "Udp.send", "sig": "Udp.send(socket, ip, port, bytes, len) -> int", "tip": "Send one datagram.", "kind": "namespace-method", "page": "udp-send.html", "section": "Udp"}, "udp-recv": {"name": "Udp.recv", "sig": "Udp.recv(socket, bytes, cap) -> int", "tip": "Read the next waiting datagram.", "kind": "namespace-method", "page": "udp-recv.html", "section": "Udp"}, "udp-from_ip": {"name": "Udp.from_ip", "sig": "Udp.from_ip(socket) -> int", "tip": "The sender of the last datagram read.", "kind": "namespace-method", "page": "udp-from_ip.html", "section": "Udp"}, "udp-from_port": {"name": "Udp.from_port", "sig": "Udp.from_port(socket) -> int", "tip": "The sender's port.", "kind": "namespace-method", "page": "udp-from_port.html", "section": "Udp"}, "udp-close": {"name": "Udp.close", "sig": "Udp.close(socket) -> void", "tip": "Close a socket.", "kind": "namespace-method", "page": "udp-close.html", "section": "Udp"}, "udp-resolve": {"name": "Udp.resolve", "sig": "Udp.resolve(name) -> int", "tip": "Look a host name up.", "kind": "namespace-method", "page": "udp-resolve.html", "section": "Udp"}, "udp-local_ip": {"name": "Udp.local_ip", "sig": "Udp.local_ip() -> int", "tip": "This machine's address.", "kind": "namespace-method", "page": "udp-local_ip.html", "section": "Udp"}, "udp-ip": {"name": "Udp.ip", "sig": "Udp.ip(text) -> int", "tip": "Parse a dotted address.", "kind": "namespace-method", "page": "udp-ip.html", "section": "Udp"}, "udp-ip_text": {"name": "Udp.ip_text", "sig": "Udp.ip_text(ip) -> string", "tip": "Format an address.", "kind": "namespace-method", "page": "udp-ip_text.html", "section": "Udp"}, "bigint-from": {"name": "BigInt.from", "sig": "BigInt.from(value) -> BigInt", "tip": "Turn a plain int into a BigInt.", "kind": "namespace-method", "page": "bigint-from.html", "section": "BigInt"}, "bigint-parse": {"name": "BigInt.parse", "sig": "BigInt.parse(text) -> BigInt", "tip": "Parse decimal text into a BigInt.", "kind": "namespace-method", "page": "bigint-parse.html", "section": "BigInt"}, "bigint-add": {"name": "BigInt.add", "sig": "BigInt.add(a, b) -> BigInt", "tip": "Exact sum of two big integers.", "kind": "namespace-method", "page": "bigint-add.html", "section": "BigInt"}, "bigint-sub": {"name": "BigInt.sub", "sig": "BigInt.sub(a, b) -> BigInt", "tip": "Exact difference of two big integers.", "kind": "namespace-method", "page": "bigint-sub.html", "section": "BigInt"}, "bigint-mul": {"name": "BigInt.mul", "sig": "BigInt.mul(a, b) -> BigInt", "tip": "Exact product of two big integers.", "kind": "namespace-method", "page": "bigint-mul.html", "section": "BigInt"}, "bigint-neg": {"name": "BigInt.neg", "sig": "BigInt.neg(a) -> BigInt", "tip": "Negate a big integer.", "kind": "namespace-method", "page": "bigint-neg.html", "section": "BigInt"}, "bigint-pow": {"name": "BigInt.pow", "sig": "BigInt.pow(a, exp) -> BigInt", "tip": "Raise a big integer to an int power.", "kind": "namespace-method", "page": "bigint-pow.html", "section": "BigInt"}, "bigint-div": {"name": "BigInt.div", "sig": "BigInt.div(a, d) -> BigInt", "tip": "Divide a big integer by an int (toward zero).", "kind": "namespace-method", "page": "bigint-div.html", "section": "BigInt"}, "bigint-mod": {"name": "BigInt.mod", "sig": "BigInt.mod(a, d) -> int", "tip": "Remainder of a big integer divided by an int.", "kind": "namespace-method", "page": "bigint-mod.html", "section": "BigInt"}, "bigint-cmp": {"name": "BigInt.cmp", "sig": "BigInt.cmp(a, b) -> int", "tip": "Compare two big integers: -1, 0, or 1.", "kind": "namespace-method", "page": "bigint-cmp.html", "section": "BigInt"}, "bigint-eq": {"name": "BigInt.eq", "sig": "BigInt.eq(a, b) -> bool", "tip": "True when two big integers are equal.", "kind": "namespace-method", "page": "bigint-eq.html", "section": "BigInt"}, "bigint-is_zero": {"name": "BigInt.is_zero", "sig": "BigInt.is_zero(a) -> bool", "tip": "True when a big integer is zero.", "kind": "namespace-method", "page": "bigint-is_zero.html", "section": "BigInt"}, "bigint-to_int": {"name": "BigInt.to_int", "sig": "BigInt.to_int(a) -> int", "tip": "Narrow a big integer to a plain int (clamped).", "kind": "namespace-method", "page": "bigint-to_int.html", "section": "BigInt"}, "bigint-str": {"name": "BigInt.str", "sig": "BigInt.str(a) -> string", "tip": "Render a big integer as decimal text.", "kind": "namespace-method", "page": "bigint-str.html", "section": "BigInt"}, "process-spawn": {"name": "Process.spawn", "sig": "Process.spawn(path, args) -> int", "tip": "Start a program; never blocks.", "kind": "namespace-method", "page": "process-spawn.html", "section": "Process"}, "process-poll": {"name": "Process.poll", "sig": "Process.poll(handle) -> int", "tip": "-1 while running, else the exit code.", "kind": "namespace-method", "page": "process-poll.html", "section": "Process"}, "process-kill": {"name": "Process.kill", "sig": "Process.kill(handle) -> void", "tip": "End a running child.", "kind": "namespace-method", "page": "process-kill.html", "section": "Process"}, "process-free": {"name": "Process.free", "sig": "Process.free(handle) -> void", "tip": "Let a process handle go.", "kind": "namespace-method", "page": "process-free.html", "section": "Process"}, "decimal-from": {"name": "Decimal.from", "sig": "Decimal.from(value) -> Decimal", "tip": "Turn a whole int into a Decimal.", "kind": "namespace-method", "page": "decimal-from.html", "section": "Decimal"}, "decimal-parse": {"name": "Decimal.parse", "sig": "Decimal.parse(text) -> Decimal", "tip": "Parse decimal text like \"19.99\".", "kind": "namespace-method", "page": "decimal-parse.html", "section": "Decimal"}, "decimal-add": {"name": "Decimal.add", "sig": "Decimal.add(a, b) -> Decimal", "tip": "Exact sum of two decimals.", "kind": "namespace-method", "page": "decimal-add.html", "section": "Decimal"}, "decimal-sub": {"name": "Decimal.sub", "sig": "Decimal.sub(a, b) -> Decimal", "tip": "Exact difference of two decimals.", "kind": "namespace-method", "page": "decimal-sub.html", "section": "Decimal"}, "decimal-mul": {"name": "Decimal.mul", "sig": "Decimal.mul(a, b) -> Decimal", "tip": "Exact product of two decimals.", "kind": "namespace-method", "page": "decimal-mul.html", "section": "Decimal"}, "decimal-neg": {"name": "Decimal.neg", "sig": "Decimal.neg(a) -> Decimal", "tip": "Negate a decimal.", "kind": "namespace-method", "page": "decimal-neg.html", "section": "Decimal"}, "decimal-cmp": {"name": "Decimal.cmp", "sig": "Decimal.cmp(a, b) -> int", "tip": "Compare two decimals: -1, 0, or 1.", "kind": "namespace-method", "page": "decimal-cmp.html", "section": "Decimal"}, "decimal-eq": {"name": "Decimal.eq", "sig": "Decimal.eq(a, b) -> bool", "tip": "True when two decimals are equal in value.", "kind": "namespace-method", "page": "decimal-eq.html", "section": "Decimal"}, "decimal-scale": {"name": "Decimal.scale", "sig": "Decimal.scale(d) -> int", "tip": "The number of digits after the point.", "kind": "namespace-method", "page": "decimal-scale.html", "section": "Decimal"}, "decimal-rescale": {"name": "Decimal.rescale", "sig": "Decimal.rescale(d, places) -> Decimal", "tip": "Change precision (truncates toward zero).", "kind": "namespace-method", "page": "decimal-rescale.html", "section": "Decimal"}, "decimal-str": {"name": "Decimal.str", "sig": "Decimal.str(d) -> string", "tip": "Render a decimal as text.", "kind": "namespace-method", "page": "decimal-str.html", "section": "Decimal"}, "type-int": {"name": "int", "sig": "int", "tip": "The default 32-bit signed integer — also how colors, keys, and tiles are carried.", "kind": "type", "page": "type-int.html", "section": "Types"}, "type-fixed": {"name": "fixed", "sig": "fixed", "tip": "Q16.16 fixed-point for deterministic fractional math — no floats.", "kind": "type", "page": "type-fixed.html", "section": "Types"}, "type-long": {"name": "long", "sig": "long", "tip": "A 64-bit signed integer for values that overflow a 32-bit int.", "kind": "type", "page": "type-long.html", "section": "Types"}, "type-bool": {"name": "bool", "sig": "bool", "tip": "A truth value — the result of comparisons and of and / or / not.", "kind": "type", "page": "type-bool.html", "section": "Types"}, "type-float": {"name": "float", "sig": "float", "tip": "A 32-bit IEEE floating-point number — what the GPU uses.", "kind": "type", "page": "type-float.html", "section": "Types"}, "type-string": {"name": "string", "sig": "string", "tip": "An immutable string — compared by content, sliceable, and interpolatable.", "kind": "type", "page": "type-string.html", "section": "Types"}, "type-double": {"name": "double", "sig": "double", "tip": "A 64-bit IEEE floating-point number for precise math.", "kind": "type", "page": "type-double.html", "section": "Types"}, "type-entity": {"name": "entity", "sig": "entity", "tip": "The id/handle of a spawned model instance, as returned by self().", "kind": "type", "page": "type-entity.html", "section": "Types"}, "type-pointer": {"name": "pointer", "sig": "pointer", "tip": "A raw address into memory — a byte buffer from bytes(n), or an FFI handle.", "kind": "type", "page": "type-pointer.html", "section": "Types"}, "type-words": {"name": "words", "sig": "words", "tip": "A raw buffer indexed as 32-bit words — each buffer[i] reads or writes an int.", "kind": "type", "page": "type-words.html", "section": "Types"}, "type-byte": {"name": "byte", "sig": "byte", "tip": "A raw buffer indexed one byte at a time — each buffer[i] reads or writes a byte.", "kind": "type", "page": "type-byte.html", "section": "Types"}, "type-slices": {"name": "[]T (slices)", "sig": "[]T", "tip": "A growable slice of T — new []T makes one, push appends, len counts, s[i] indexes.", "kind": "type", "page": "type-slices.html", "section": "Types"}, "type-fixeds": {"name": "fixeds", "sig": "fixeds", "tip": "A buffer of fixed-point values — f[i] reads and writes a fixed.", "kind": "type", "page": "type-fixeds.html", "section": "Types"}, "type-countdown": {"name": "countdown", "sig": "countdown", "tip": "An int component field the engine steps toward 0 once per Update.", "kind": "type", "page": "type-countdown.html", "section": "Types"}, "type-floats": {"name": "floats", "sig": "floats / doubles", "tip": "A buffer of floats (or doubles) — v[i] reads and writes one.", "kind": "type", "page": "type-floats.html", "section": "Types"}, "type-pointers": {"name": "pointers", "sig": "pointers", "tip": "A buffer of pointers — p[i] reads and writes a pointer.", "kind": "type", "page": "type-pointers.html", "section": "Types"}, "type-vector": {"name": "Vector", "sig": "Vector", "tip": "A 2D vector — two fixed components (x, y), copied by value.", "kind": "type", "page": "type-vector.html", "section": "Types"}, "type-ivec2": {"name": "IVec2", "sig": "IVec2", "tip": "An integer 2D vector — two int components (x, y), copied by value.", "kind": "type", "page": "type-ivec2.html", "section": "Types"}, "type-rect": {"name": "Rect", "sig": "Rect", "tip": "A rectangle — position (x, y) and size (w, h), copied by value.", "kind": "type", "page": "type-rect.html", "section": "Types"}, "type-void": {"name": "void", "sig": "void", "tip": "The absence of a value — the return type of a function that returns nothing.", "kind": "type", "page": "type-void.html", "section": "Types"}, "dict-new": {"name": "Dict.new", "sig": "Dict.new() -> Dict", "tip": "Create an empty string-keyed map.", "kind": "namespace-method", "page": "dict-new.html", "section": "Dict"}, "dict-set": {"name": "Dict.set", "sig": "Dict.set(d, key, value)", "tip": "Insert or update a key.", "kind": "namespace-method", "page": "dict-set.html", "section": "Dict"}, "dict-get": {"name": "Dict.get", "sig": "Dict.get(d, key) -> int", "tip": "Look up a key (0 if absent).", "kind": "namespace-method", "page": "dict-get.html", "section": "Dict"}, "dict-get_or": {"name": "Dict.get_or", "sig": "Dict.get_or(d, key, fallback) -> int", "tip": "Look up a key with an explicit default.", "kind": "namespace-method", "page": "dict-get_or.html", "section": "Dict"}, "dict-has": {"name": "Dict.has", "sig": "Dict.has(d, key) -> bool", "tip": "Is a key present?", "kind": "namespace-method", "page": "dict-has.html", "section": "Dict"}, "dict-remove": {"name": "Dict.remove", "sig": "Dict.remove(d, key)", "tip": "Delete a key.", "kind": "namespace-method", "page": "dict-remove.html", "section": "Dict"}, "dict-size": {"name": "Dict.size", "sig": "Dict.size(d) -> int", "tip": "How many keys are stored.", "kind": "namespace-method", "page": "dict-size.html", "section": "Dict"}, "dict-clear": {"name": "Dict.clear", "sig": "Dict.clear(d)", "tip": "Remove every key.", "kind": "namespace-method", "page": "dict-clear.html", "section": "Dict"}, "dict-keys": {"name": "Dict.keys", "sig": "Dict.keys(d) -> []pointer", "tip": "Every key, as a slice of strings.", "kind": "namespace-method", "page": "dict-keys.html", "section": "Dict"}, "op-arith": {"name": "Arithmetic", "sig": "+ - * / %", "tip": "Add, subtract, multiply, integer-divide, and remainder — on int and fixed.", "kind": "operator", "page": "op-arith.html", "section": "Operators & tokens"}, "op-compare": {"name": "Comparison", "sig": "== != < <= > >=", "tip": "Compare two values and yield a bool; on strings == compares contents.", "kind": "operator", "page": "op-compare.html", "section": "Operators & tokens"}, "op-logical": {"name": "Logical", "sig": "and or not", "tip": "The boolean combinators, spelled as words — never && or || or a bare !.", "kind": "operator", "page": "op-logical.html", "section": "Operators & tokens"}, "op-bitwise": {"name": "Bitwise", "sig": "& | ^ ~ << >>", "tip": "Bit-level and, or, xor, not, and shifts — with Go-style precedence.", "kind": "operator", "page": "op-bitwise.html", "section": "Operators & tokens"}, "op-range": {"name": "Range", "sig": "a .. b", "tip": "A half-open range for numeric for loops — from a up to but not including b.", "kind": "operator", "page": "op-range.html", "section": "Operators & tokens"}, "op-assign": {"name": "Assignment", "sig": "name = value", "tip": "Store into a var, a field, or an element — a statement, not an expression.", "kind": "operator", "page": "op-assign.html", "section": "Operators & tokens"}, "op-access": {"name": "Member & index", "sig": "x.field buffer[i] text[a..b]", "tip": "Field access, element index, and string slice — usable as value or target.", "kind": "operator", "page": "op-access.html", "section": "Operators & tokens"}, "op-interp": {"name": "String interpolation", "sig": "`text {expr} more`", "tip": "A backtick string with {expr} holes, each stringified and concatenated.", "kind": "operator", "page": "op-interp.html", "section": "Operators & tokens"}, "op-literals": {"name": "Literals", "sig": "42 0x1E90FF 'w' \"text\" true null", "tip": "Integer, hex, character, string, boolean, and null-pointer literals.", "kind": "operator", "page": "op-literals.html", "section": "Operators & tokens"}, "op-comment": {"name": "Comment", "sig": "# to end of line", "tip": "Everything after # on a line is a comment, ignored by the compiler.", "kind": "operator", "page": "op-comment.html", "section": "Operators & tokens"}, "fn-print": {"name": "print", "sig": "print(x)", "tip": "Print an int or string followed by a newline — for headless tests and debugging.", "kind": "builtin", "page": "fn-print.html", "section": "Builtin functions"}, "fn-string": {"name": "string", "sig": "string(x) -> string", "tip": "Convert an int, bool, or fixed value to text; a string passes through unchanged.", "kind": "builtin", "page": "fn-string.html", "section": "Builtin functions"}, "fn-quit": {"name": "quit", "sig": "quit()", "tip": "Stop the game loop cleanly after the current frame finishes.", "kind": "builtin", "page": "fn-quit.html", "section": "Builtin functions"}, "fn-save": {"name": "save", "sig": "save()", "tip": "Serialize the whole world — every model instance, property, and program var — in one call.", "kind": "builtin", "page": "fn-save.html", "section": "Builtin functions"}, "fn-load": {"name": "load", "sig": "load() -> bool", "tip": "Restore the world from a snapshot previously written by save().", "kind": "builtin", "page": "fn-load.html", "section": "Builtin functions"}, "fn-len": {"name": "len", "sig": "len(x) -> int", "tip": "The number of elements in a slice, or the byte length of a string.", "kind": "builtin", "page": "fn-len.html", "section": "Builtin functions"}, "fn-push": {"name": "push", "sig": "push(slice, x)", "tip": "Append one element to the end of a growable slice.", "kind": "builtin", "page": "fn-push.html", "section": "Builtin functions"}, "fn-abs": {"name": "abs", "sig": "abs(a) -> int", "tip": "The absolute value of an integer (its magnitude, never negative).", "kind": "builtin", "page": "fn-abs.html", "section": "Builtin functions"}, "fn-clamp": {"name": "clamp", "sig": "clamp(v, lo, hi) -> int", "tip": "Constrain a value to the inclusive range [lo, hi].", "kind": "builtin", "page": "fn-clamp.html", "section": "Builtin functions"}, "fn-panic": {"name": "panic", "sig": "panic(msg: string)", "tip": "Abort with a located error message instead of crashing.", "kind": "builtin", "page": "fn-panic.html", "section": "Builtin functions"}, "fn-assert": {"name": "assert", "sig": "assert(cond: bool, msg: string)", "tip": "Abort with a located message when an invariant is false.", "kind": "builtin", "page": "fn-assert.html", "section": "Builtin functions"}, "fn-ok": {"name": "ok", "sig": "ok(v) -> result", "tip": "Wrap a success payload in a result — the happy half of try/else.", "kind": "builtin", "page": "fn-ok.html", "section": "Builtin functions"}, "fn-err": {"name": "err", "sig": "err(msg: string) -> result", "tip": "Wrap a failure message in a result — the sad half, recovered by try/else.", "kind": "builtin", "page": "fn-err.html", "section": "Builtin functions"}, "fn-is_ok": {"name": "is_ok", "sig": "is_ok(r: result) -> bool", "tip": "True when a result carries a success payload.", "kind": "builtin", "page": "fn-is_ok.html", "section": "Builtin functions"}, "fn-is_err": {"name": "is_err", "sig": "is_err(r: result) -> bool", "tip": "True when a result carries a failure.", "kind": "builtin", "page": "fn-is_err.html", "section": "Builtin functions"}, "fn-some": {"name": "some", "sig": "some(v) -> option", "tip": "Wrap a present value in an option.", "kind": "builtin", "page": "fn-some.html", "section": "Builtin functions"}, "fn-none": {"name": "none", "sig": "none() -> option", "tip": "The empty option — a missing value.", "kind": "builtin", "page": "fn-none.html", "section": "Builtin functions"}, "fn-is_some": {"name": "is_some", "sig": "is_some(o: option) -> bool", "tip": "True when an option holds a value.", "kind": "builtin", "page": "fn-is_some.html", "section": "Builtin functions"}, "fn-is_none": {"name": "is_none", "sig": "is_none(o: option) -> bool", "tip": "True when an option is empty.", "kind": "builtin", "page": "fn-is_none.html", "section": "Builtin functions"}, "fn-unwrap_or": {"name": "unwrap_or", "sig": "unwrap_or(o: option, fallback) -> int", "tip": "The option value, or a fallback if empty.", "kind": "builtin", "page": "fn-unwrap_or.html", "section": "Builtin functions"}, "fn-exit": {"name": "exit", "sig": "exit(code)", "tip": "Terminate the process immediately with a status code.", "kind": "builtin", "page": "fn-exit.html", "section": "Builtin functions"}, "fn-run": {"name": "run", "sig": "run(cmd)", "tip": "Run a string as a shell command.", "kind": "builtin", "page": "fn-run.html", "section": "Builtin functions"}, "fn-getenv": {"name": "getenv", "sig": "getenv(name) -> string", "tip": "Read an environment variable, returning empty when it is unset.", "kind": "builtin", "page": "fn-getenv.html", "section": "Builtin functions"}, "fn-read_char": {"name": "read_char", "sig": "read_char() -> int", "tip": "Read one byte from standard input, or -1 at end of input.", "kind": "builtin", "page": "fn-read_char.html", "section": "Builtin functions"}, "fn-arg": {"name": "arg", "sig": "arg(i) -> string", "tip": "The i-th command-line argument as a string.", "kind": "builtin", "page": "fn-arg.html", "section": "Builtin functions"}, "fn-arg_count": {"name": "arg_count", "sig": "arg_count() -> int", "tip": "The number of command-line arguments, counting the program name.", "kind": "builtin", "page": "fn-arg_count.html", "section": "Builtin functions"}, "fn-bytes": {"name": "bytes", "sig": "bytes(n) -> pointer", "tip": "Allocate a raw buffer of n bytes and return a pointer to it.", "kind": "builtin", "page": "fn-bytes.html", "section": "Builtin functions"}, "fn-file_stderr": {"name": "file_stderr", "sig": "file_stderr() -> pointer", "tip": "The standard-error stream handle for use with file_write.", "kind": "builtin", "page": "fn-file_stderr.html", "section": "Builtin functions"}, "fn-file_stdout": {"name": "file_stdout", "sig": "file_stdout() -> pointer", "tip": "The standard-output stream handle for use with file_write.", "kind": "builtin", "page": "fn-file_stdout.html", "section": "Builtin functions"}, "fn-file_write": {"name": "file_write", "sig": "file_write(handle, buf, len)", "tip": "Write a run of raw bytes to a stream handle.", "kind": "builtin", "page": "fn-file_write.html", "section": "Builtin functions"}, "fn-fixed": {"name": "fixed", "sig": "fixed(x) -> fixed", "tip": "Convert an int (or a float) into a Q16.16 fixed-point value.", "kind": "builtin", "page": "fn-fixed.html", "section": "Builtin functions"}, "fn-floor": {"name": "floor", "sig": "floor(x) -> int", "tip": "Floor a fixed-point value down to the nearest integer.", "kind": "builtin", "page": "fn-floor.html", "section": "Builtin functions"}, "fn-max": {"name": "max", "sig": "max(a, b) -> int", "tip": "The larger of two integers.", "kind": "builtin", "page": "fn-max.html", "section": "Builtin functions"}, "fn-min": {"name": "min", "sig": "min(a, b) -> int", "tip": "The smaller of two integers.", "kind": "builtin", "page": "fn-min.html", "section": "Builtin functions"}, "fn-ui_build": {"name": "ui_build", "sig": "ui_build()", "tip": "Build the declared UI tree so it can be opened and rendered.", "kind": "builtin", "page": "fn-ui_build.html", "section": "Builtin functions"}, "fn-words": {"name": "words", "sig": "words(n) -> words", "tip": "Allocate a raw buffer of n 32-bit words, indexable with [i].", "kind": "builtin", "page": "fn-words.html", "section": "Builtin functions"}, "fn-float": {"name": "float", "sig": "float(x) -> float / double(x) -> double", "tip": "Convert any number to a float (or double).", "kind": "builtin", "page": "fn-float.html", "section": "Builtin functions"}, "fn-int": {"name": "int", "sig": "int(x) -> int / long(x) -> long", "tip": "Convert a number to a whole number, truncating toward zero.", "kind": "builtin", "page": "fn-int.html", "section": "Builtin functions"}, "fn-float_bits": {"name": "float_bits", "sig": "float_bits(x) -> int / float_from_bits(bits) -> float", "tip": "A float's IEEE bit pattern as an int, and back.", "kind": "builtin", "page": "fn-float_bits.html", "section": "Builtin functions"}, "set-new": {"name": "Set.new", "sig": "Set.new() -> Set", "tip": "Create an empty string set.", "kind": "namespace-method", "page": "set-new.html", "section": "Set"}, "set-add": {"name": "Set.add", "sig": "Set.add(s, key)", "tip": "Add a member (duplicates ignored).", "kind": "namespace-method", "page": "set-add.html", "section": "Set"}, "set-has": {"name": "Set.has", "sig": "Set.has(s, key) -> bool", "tip": "Is a member present?", "kind": "namespace-method", "page": "set-has.html", "section": "Set"}, "set-remove": {"name": "Set.remove", "sig": "Set.remove(s, key)", "tip": "Remove a member.", "kind": "namespace-method", "page": "set-remove.html", "section": "Set"}, "set-size": {"name": "Set.size", "sig": "Set.size(s) -> int", "tip": "How many members.", "kind": "namespace-method", "page": "set-size.html", "section": "Set"}, "set-clear": {"name": "Set.clear", "sig": "Set.clear(s)", "tip": "Remove every member.", "kind": "namespace-method", "page": "set-clear.html", "section": "Set"}, "set-members": {"name": "Set.members", "sig": "Set.members(s) -> []pointer", "tip": "Every member, as a slice of strings.", "kind": "namespace-method", "page": "set-members.html", "section": "Set"}, "annot-queries": {"name": "@Queries", "sig": "@Queries(these: [Position, Velocity])", "tip": "Declare the properties a handler operates on, binding their fields by name in the body.", "kind": "annotation", "page": "annot-queries.html", "section": "Annotations"}, "annot-computed": {"name": "@Computed", "sig": "@Computed on a property field", "tip": "A derived field expanded inline wherever it is read, never stored.", "kind": "annotation", "page": "annot-computed.html", "section": "Annotations"}, "annot-on": {"name": "@On", "sig": "@On(Event) handler Name { … }", "tip": "Register a compile-time listener that runs whenever an event is emitted.", "kind": "annotation", "page": "annot-on.html", "section": "Annotations"}, "annot-export": {"name": "@export", "sig": "@export function name(…) -> R", "tip": "Expose a function as a native symbol so a host can call it.", "kind": "annotation", "page": "annot-export.html", "section": "Annotations"}, "annot-sync": {"name": "@Sync", "sig": "@Sync property … / @Owned", "tip": "Mark fields as replicated and models as owned so the compiler generates networking.", "kind": "annotation", "page": "annot-sync.html", "section": "Annotations"}, "annot-handles": {"name": "@Handles", "sig": "@Handles(Event) handler Name { … }", "tip": "Declarative hint naming the event or subsystem a handler is responsible for.", "kind": "annotation", "page": "annot-handles.html", "section": "Annotations"}, "annot-onattach": {"name": "@OnAttach", "sig": "@OnAttach(Property) handler Name { … }", "tip": "Run a handler when a property is structurally attached to a live instance.", "kind": "annotation", "page": "annot-onattach.html", "section": "Annotations"}, "annot-ondespawn": {"name": "@OnDespawn", "sig": "@OnDespawn(Model, reason: r) handler Name { … }", "tip": "Run a handler when a model instance is torn down, optionally knowing why.", "kind": "annotation", "page": "annot-ondespawn.html", "section": "Annotations"}, "annot-ondetach": {"name": "@OnDetach", "sig": "@OnDetach(Property) handler Name { … }", "tip": "Run a handler when a property is structurally detached from a live instance.", "kind": "annotation", "page": "annot-ondetach.html", "section": "Annotations"}, "annot-ondisable": {"name": "@OnDisable", "sig": "@OnDisable(Property) handler Name { … }", "tip": "Run a handler when a property is paused (disabled) on an instance.", "kind": "annotation", "page": "annot-ondisable.html", "section": "Annotations"}, "annot-onenable": {"name": "@OnEnable", "sig": "@OnEnable(Property) handler Name { … }", "tip": "Run a handler when a paused property is re-enabled on an instance.", "kind": "annotation", "page": "annot-onenable.html", "section": "Annotations"}, "annot-onquit": {"name": "@OnQuit", "sig": "@OnQuit handler Name { … }", "tip": "Run a handler once at shutdown, after the last frame.", "kind": "annotation", "page": "annot-onquit.html", "section": "Annotations"}, "annot-onspawn": {"name": "@OnSpawn", "sig": "@OnSpawn(Model) handler Name { … }", "tip": "Run a handler once each time an instance of a model is spawned.", "kind": "annotation", "page": "annot-onspawn.html", "section": "Annotations"}, "annot-onstart": {"name": "@OnStart", "sig": "@OnStart handler Name { … }", "tip": "Run a handler once at boot instead of assigning it a frame phase.", "kind": "annotation", "page": "annot-onstart.html", "section": "Annotations"}, "annot-owned": {"name": "@Owned", "sig": "@Owned model Name { … }", "tip": "Give a model a network owner slot so its instances can be assigned to a peer.", "kind": "annotation", "page": "annot-owned.html", "section": "Annotations"}, "annot-predicted": {"name": "@Predicted", "sig": "@Predicted handler Name { … }", "tip": "Run a control handler on the owning client speculatively and on the server authoritatively.", "kind": "annotation", "page": "annot-predicted.html", "section": "Annotations"}, "annot-public": {"name": "@Public", "sig": "@Public @OnSpawn(Model) handler … { … }", "tip": "Promote a lifecycle hook to a public event other modules can listen for.", "kind": "annotation", "page": "annot-public.html", "section": "Annotations"}, "annot-reads": {"name": "@Reads", "sig": "@Reads(Property)", "tip": "Declare that a handler reads a property — an analysis and scheduling hint.", "kind": "annotation", "page": "annot-reads.html", "section": "Annotations"}, "annot-server": {"name": "@Server", "sig": "@Server handler Name { … }", "tip": "Run a handler only on the authority; clients receive the result via @Sync.", "kind": "annotation", "page": "annot-server.html", "section": "Annotations"}, "annot-toclients": {"name": "@ToClients", "sig": "@ToClients event Name { … }", "tip": "A remote event broadcast from the server to clients — a server→clients notification.", "kind": "annotation", "page": "annot-toclients.html", "section": "Annotations"}, "annot-toserver": {"name": "@ToServer", "sig": "@ToServer event Name { … }", "tip": "A remote event sent from a client to the server — a client→server request.", "kind": "annotation", "page": "annot-toserver.html", "section": "Annotations"}, "annot-writes": {"name": "@Writes", "sig": "@Writes(Property)", "tip": "Declare that a handler writes a property — an analysis and scheduling hint.", "kind": "annotation", "page": "annot-writes.html", "section": "Annotations"}, "annot-enginesystem": {"name": "@EngineSystem", "sig": "\"@EngineSystem(Component, Phase) function esys_name() { … }\"", "tip": "Register a package's engine-owned system so the frame loop runs it each phase.", "kind": "annotation", "page": "annot-enginesystem.html", "section": "Annotations"}, "annot-namespace": {"name": "@Namespace", "sig": "\"@Namespace(Name) function name_method(…) -> T { … }\"", "tip": "Let a package provide a Name.method(…) namespace that dispatches to name_method.", "kind": "annotation", "page": "annot-namespace.html", "section": "Annotations"}, "annot-clearcolor": {"name": "\"@ClearColor\"", "sig": "\"@ClearColor(0xRRGGBB) handler Draw phase Render { … }\"", "tip": "Declare a clear colour so the Render phase auto-clears + auto-presents for you.", "kind": "annotation", "page": "annot-clearcolor.html", "section": "Annotations"}, "annot-system": {"name": "@System", "sig": "\"@System(Phase) function name() { … }\"", "tip": "Register a prebuilt binary module's system with the host at load, per phase.", "kind": "annotation", "page": "annot-system.html", "section": "Annotations"}, "huge-from": {"name": "Huge.from", "sig": "Huge.from(value) -> Huge", "tip": "Turn an int into a Huge.", "kind": "namespace-method", "page": "huge-from.html", "section": "Huge"}, "huge-add": {"name": "Huge.add", "sig": "Huge.add(a, b) -> Huge", "tip": "Sum of two big numbers.", "kind": "namespace-method", "page": "huge-add.html", "section": "Huge"}, "huge-sub": {"name": "Huge.sub", "sig": "Huge.sub(a, b) -> Huge", "tip": "Difference of two big numbers.", "kind": "namespace-method", "page": "huge-sub.html", "section": "Huge"}, "huge-mul": {"name": "Huge.mul", "sig": "Huge.mul(a, b) -> Huge", "tip": "Product of two big numbers.", "kind": "namespace-method", "page": "huge-mul.html", "section": "Huge"}, "huge-neg": {"name": "Huge.neg", "sig": "Huge.neg(a) -> Huge", "tip": "Negate a big number.", "kind": "namespace-method", "page": "huge-neg.html", "section": "Huge"}, "huge-cmp": {"name": "Huge.cmp", "sig": "Huge.cmp(a, b) -> int", "tip": "Compare two big numbers: -1, 0, or 1.", "kind": "namespace-method", "page": "huge-cmp.html", "section": "Huge"}, "huge-sign": {"name": "Huge.sign", "sig": "Huge.sign(a) -> int", "tip": "The sign: -1, 0, or 1.", "kind": "namespace-method", "page": "huge-sign.html", "section": "Huge"}, "huge-mantissa": {"name": "Huge.mantissa", "sig": "Huge.mantissa(a) -> fixed", "tip": "The mantissa in [1, 10).", "kind": "namespace-method", "page": "huge-mantissa.html", "section": "Huge"}, "huge-exp": {"name": "Huge.exp", "sig": "Huge.exp(a) -> int", "tip": "The base-10 exponent.", "kind": "namespace-method", "page": "huge-exp.html", "section": "Huge"}, "huge-str": {"name": "Huge.str", "sig": "Huge.str(a) -> string", "tip": "Render as scientific text like 1.23e45.", "kind": "namespace-method", "page": "huge-str.html", "section": "Huge"}, "angle-from_degrees": {"name": "Angle.from_degrees", "sig": "Angle.from_degrees(d) -> fixed", "tip": "Degrees to a wrapped radian angle.", "kind": "namespace-method", "page": "angle-from_degrees.html", "section": "Angle"}, "angle-to_degrees": {"name": "Angle.to_degrees", "sig": "Angle.to_degrees(a) -> fixed", "tip": "Radians back to degrees.", "kind": "namespace-method", "page": "angle-to_degrees.html", "section": "Angle"}, "angle-wrap": {"name": "Angle.wrap", "sig": "Angle.wrap(a) -> fixed", "tip": "Normalize any radian value to [-pi, pi).", "kind": "namespace-method", "page": "angle-wrap.html", "section": "Angle"}, "angle-sin": {"name": "Angle.sin", "sig": "Angle.sin(a) -> fixed", "tip": "Sine of an angle.", "kind": "namespace-method", "page": "angle-sin.html", "section": "Angle"}, "angle-cos": {"name": "Angle.cos", "sig": "Angle.cos(a) -> fixed", "tip": "Cosine of an angle.", "kind": "namespace-method", "page": "angle-cos.html", "section": "Angle"}, "angle-add": {"name": "Angle.add", "sig": "Angle.add(a, b) -> fixed", "tip": "Add two angles (wrapped).", "kind": "namespace-method", "page": "angle-add.html", "section": "Angle"}, "angle-diff": {"name": "Angle.diff", "sig": "Angle.diff(a, b) -> fixed", "tip": "Shortest signed rotation from a to b.", "kind": "namespace-method", "page": "angle-diff.html", "section": "Angle"}, "angle-lerp": {"name": "Angle.lerp", "sig": "Angle.lerp(a, b, t) -> fixed", "tip": "Interpolate along the shortest arc.", "kind": "namespace-method", "page": "angle-lerp.html", "section": "Angle"}, "angle-diff_degrees": {"name": "Angle.diff_degrees", "sig": "Angle.diff_degrees(a: int, b: int) -> int", "tip": "The signed difference between two headings in whole degrees.", "kind": "namespace-method", "page": "angle-diff_degrees.html", "section": "Angle"}, "percent-clamp": {"name": "Percent.clamp", "sig": "Percent.clamp(v) -> fixed", "tip": "Clamp any fixed into [0, 1].", "kind": "namespace-method", "page": "percent-clamp.html", "section": "Percent"}, "percent-of": {"name": "Percent.of", "sig": "Percent.of(num, den) -> fixed", "tip": "Clamped ratio num / den.", "kind": "namespace-method", "page": "percent-of.html", "section": "Percent"}, "percent-lerp": {"name": "Percent.lerp", "sig": "Percent.lerp(a, b, t) -> fixed", "tip": "Interpolate a..b by a clamped t.", "kind": "namespace-method", "page": "percent-lerp.html", "section": "Percent"}, "percent-apply": {"name": "Percent.apply", "sig": "Percent.apply(value, p) -> fixed", "tip": "Scale a value by a clamped percent.", "kind": "namespace-method", "page": "percent-apply.html", "section": "Percent"}, "regex-compile": {"name": "Regex.compile", "sig": "Regex.compile(pattern) -> Regex", "tip": "Compile a pattern once for reuse; returns an error value (null) if it is invalid.", "kind": "namespace-method", "page": "regex-compile.html", "section": "Regex"}, "regex-valid": {"name": "Regex.valid", "sig": "Regex.valid(pattern) -> bool", "tip": "True if the pattern is well-formed (compiles without error).", "kind": "namespace-method", "page": "regex-valid.html", "section": "Regex"}, "regex-matches": {"name": "Regex.matches", "sig": "Regex.matches(text, pattern) -> bool", "tip": "True if the pattern matches anywhere in the text (a search).", "kind": "namespace-method", "page": "regex-matches.html", "section": "Regex"}, "regex-test": {"name": "Regex.test", "sig": "Regex.test(text, re) -> bool", "tip": "Like matches, but reuses an already-compiled Regex.", "kind": "namespace-method", "page": "regex-test.html", "section": "Regex"}, "regex-find": {"name": "Regex.find", "sig": "Regex.find(text, pattern) -> Match", "tip": "The first match of the pattern in the text (a Match, or null if none).", "kind": "namespace-method", "page": "regex-find.html", "section": "Regex"}, "regex-exec": {"name": "Regex.exec", "sig": "Regex.exec(text, re) -> Match", "tip": "Like find, but with an already-compiled Regex.", "kind": "namespace-method", "page": "regex-exec.html", "section": "Regex"}, "regex-next": {"name": "Regex.next", "sig": "Regex.next(text, re, from) -> Match", "tip": "The next match at or after byte offset from — the basis of a find-all loop.", "kind": "namespace-method", "page": "regex-next.html", "section": "Regex"}, "regex-replace": {"name": "Regex.replace", "sig": "Regex.replace(text, pattern, replacement) -> string", "tip": "Replace every match; the replacement expands \\0..\\9 group references.", "kind": "namespace-method", "page": "regex-replace.html", "section": "Regex"}, "regex-group": {"name": "Regex.group", "sig": "Regex.group(match, n) -> string", "tip": "The text of capture group n (group 0 is the whole match); empty if unset.", "kind": "namespace-method", "page": "regex-group.html", "section": "Regex"}, "regex-group_count": {"name": "Regex.group_count", "sig": "Regex.group_count(match) -> int", "tip": "How many capture groups the match's pattern has.", "kind": "namespace-method", "page": "regex-group_count.html", "section": "Regex"}, "regex-start": {"name": "Regex.start", "sig": "Regex.start(match, n) -> int", "tip": "The start byte offset of group n, or -1 if it did not participate.", "kind": "namespace-method", "page": "regex-start.html", "section": "Regex"}, "regex-end": {"name": "Regex.end", "sig": "Regex.end(match, n) -> int", "tip": "The end byte offset of group n, or -1 if it did not participate.", "kind": "namespace-method", "page": "regex-end.html", "section": "Regex"}, "regex-ok": {"name": "Regex.ok", "sig": "Regex.ok(match) -> bool", "tip": "True if the match succeeded (the Match is non-null).", "kind": "namespace-method", "page": "regex-ok.html", "section": "Regex"}, "grid-line": {"name": "Grid.line", "sig": "Grid.line(x0, y0, x1, y1) -> []Cell", "tip": "Every cell a straight line from (x0,y0) to (x1,y1) crosses (Bresenham).", "kind": "namespace-method", "page": "grid-line.html", "section": "Grid"}, "grid-blocked": {"name": "Grid.blocked", "sig": "Grid.blocked(x, y, wall) -> bool", "tip": "True if the cell is out of bounds or holds the wall tile.", "kind": "namespace-method", "page": "grid-blocked.html", "section": "Grid"}, "grid-line_of_sight": {"name": "Grid.line_of_sight", "sig": "Grid.line_of_sight(x0, y0, x1, y1, wall) -> bool", "tip": "True if the straight line between two cells crosses no wall.", "kind": "namespace-method", "page": "grid-line_of_sight.html", "section": "Grid"}, "grid-flood": {"name": "Grid.flood", "sig": "Grid.flood(x, y, wall) -> []Cell", "tip": "Every passable cell reachable from (x,y), 4-connected, in BFS order.", "kind": "namespace-method", "page": "grid-flood.html", "section": "Grid"}, "grid-a_star": {"name": "Grid.a_star", "sig": "Grid.a_star(x0, y0, x1, y1, wall) -> []Cell", "tip": "The shortest 4-connected path between two cells (A*), or an empty list.", "kind": "namespace-method", "page": "grid-a_star.html", "section": "Grid"}, "anim-frame": {"name": "Anim.frame", "sig": "Anim.frame(timer, fps, count) -> int", "tip": "The looping frame index for an elapsed timer at a given fps.", "kind": "namespace-method", "page": "anim-frame.html", "section": "Anim"}, "anim-once": {"name": "Anim.once", "sig": "Anim.once(timer, fps, count) -> int", "tip": "A non-looping frame index that clamps on the last frame.", "kind": "namespace-method", "page": "anim-once.html", "section": "Anim"}, "anim-pingpong": {"name": "Anim.pingpong", "sig": "Anim.pingpong(timer, fps, count) -> int", "tip": "A frame index that bounces 0..count-1..0 and repeats.", "kind": "namespace-method", "page": "anim-pingpong.html", "section": "Anim"}, "anim-finished": {"name": "Anim.finished", "sig": "Anim.finished(timer, fps, count) -> bool", "tip": "True once a one-shot clip has run past its last frame.", "kind": "namespace-method", "page": "anim-finished.html", "section": "Anim"}, "anim-duration": {"name": "Anim.duration", "sig": "Anim.duration(fps, count) -> fixed", "tip": "Seconds for one full cycle of a clip: count / fps.", "kind": "namespace-method", "page": "anim-duration.html", "section": "Anim"}, "anim-cell_x": {"name": "Anim.cell_x", "sig": "Anim.cell_x(frame, cols, cell_w) -> int", "tip": "The source x (pixels) of a frame on a grid spritesheet.", "kind": "namespace-method", "page": "anim-cell_x.html", "section": "Anim"}, "anim-cell_y": {"name": "Anim.cell_y", "sig": "Anim.cell_y(frame, cols, cell_h) -> int", "tip": "The source y (pixels) of a frame on a grid spritesheet.", "kind": "namespace-method", "page": "anim-cell_y.html", "section": "Anim"}, "anim-play": {"name": "Anim.play", "sig": "Anim.play(entity, clip) | Anim.play(entity, fps, frames, mode)", "tip": "Start (or restart) a spritesheet clip on an entity in one call.", "kind": "namespace-method", "page": "anim-play.html", "section": "Anim"}, "anim-clip": {"name": "Anim.clip", "sig": "Anim.clip(name, frames, fps, mode)", "tip": "Register a named spritesheet clip so Anim.play can play it by name.", "kind": "namespace-method", "page": "anim-clip.html", "section": "Anim"}, "anim-on_frame": {"name": "Anim.on_frame", "sig": "Anim.on_frame(entity, frame)", "tip": "Arm a frame event — the engine flags the tick a clip lands on this frame.", "kind": "namespace-method", "page": "anim-on_frame.html", "section": "Anim"}, "anim-fired": {"name": "Anim.fired", "sig": "Anim.fired(entity) -> bool", "tip": "Did the entity's clip land on its armed frame event this tick?", "kind": "namespace-method", "page": "anim-fired.html", "section": "Anim"}, "motion-to": {"name": "Motion.to", "sig": "Motion.to(entity, from, to, dur, ease)", "tip": "Start a value tween on an entity's Motion component in one call.", "kind": "namespace-method", "page": "motion-to.html", "section": "Motion"}, "tween-progress": {"name": "Tween.progress", "sig": "Tween.progress(timer, duration) -> fixed", "tip": "A one-shot 0..1 amount, clamped, for timer over duration.", "kind": "namespace-method", "page": "tween-progress.html", "section": "Tween"}, "tween-loop": {"name": "Tween.loop", "sig": "Tween.loop(timer, duration) -> fixed", "tip": "A repeating 0..1 sawtooth amount over duration.", "kind": "namespace-method", "page": "tween-loop.html", "section": "Tween"}, "tween-yoyo": {"name": "Tween.yoyo", "sig": "Tween.yoyo(timer, duration) -> fixed", "tip": "A repeating 0..1..0 triangle amount over duration.", "kind": "namespace-method", "page": "tween-yoyo.html", "section": "Tween"}, "tween-done": {"name": "Tween.done", "sig": "Tween.done(timer, duration) -> bool", "tip": "True once a one-shot tween's timer reaches its duration.", "kind": "namespace-method", "page": "tween-done.html", "section": "Tween"}, "tween-ease": {"name": "Tween.ease", "sig": "Tween.ease(t, mode) -> fixed", "tip": "Shape a 0..1 amount through an easing curve chosen by a literal mode.", "kind": "namespace-method", "page": "tween-ease.html", "section": "Tween"}, "tween-number": {"name": "Tween.number", "sig": "Tween.number(from, to, t) -> fixed", "tip": "Linear blend of two fixeds by amount t.", "kind": "namespace-method", "page": "tween-number.html", "section": "Tween"}, "tween-round": {"name": "Tween.round", "sig": "Tween.round(from, to, t) -> int", "tip": "Linear blend of two ints by amount t, rounded to the nearest int.", "kind": "namespace-method", "page": "tween-round.html", "section": "Tween"}, "tween-point": {"name": "Tween.point", "sig": "Tween.point(from, to, t) -> Vector", "tip": "Component-wise blend of two Vectors by amount t.", "kind": "namespace-method", "page": "tween-point.html", "section": "Tween"}, "tween-tint": {"name": "Tween.tint", "sig": "Tween.tint(from, to, t) -> int", "tip": "Per-channel blend of two colors by amount t.", "kind": "namespace-method", "page": "tween-tint.html", "section": "Tween"}, "tween-to": {"name": "Tween.to", "sig": "Tween.to(from, to, dur, ease) -> handle", "tip": "Start a fluent, engine-advanced tween and return a handle.", "kind": "namespace-method", "page": "tween-to.html", "section": "Tween"}, "tween-chain": {"name": "Tween.chain", "sig": "Tween.chain(handle, to, dur, ease) -> handle", "tip": "Append a tween segment that runs after the handle's current queue.", "kind": "namespace-method", "page": "tween-chain.html", "section": "Tween"}, "tween-delay": {"name": "Tween.delay", "sig": "Tween.delay(handle, ticks) -> handle", "tip": "Append a pause to a tween handle's sequence.", "kind": "namespace-method", "page": "tween-delay.html", "section": "Tween"}, "tween-value": {"name": "Tween.value", "sig": "Tween.value(handle) -> int", "tip": "The current value of a fluent tween handle this frame.", "kind": "namespace-method", "page": "tween-value.html", "section": "Tween"}, "tween-stop": {"name": "Tween.stop", "sig": "Tween.stop(handle)", "tip": "Stop and dispose a tween handle, freezing its value.", "kind": "namespace-method", "page": "tween-stop.html", "section": "Tween"}, "tween-parallel": {"name": "Tween.parallel", "sig": "Tween.parallel(a, b) -> bool", "tip": "Are two tween handles both finished? — a parallel completion query.", "kind": "namespace-method", "page": "tween-parallel.html", "section": "Tween"}, "query-count": {"name": "Query.count", "sig": "Query.count(prop) -> int", "tip": "How many live entities carry a property.", "kind": "namespace-method", "page": "query-count.html", "section": "Query"}, "query-first": {"name": "Query.first", "sig": "Query.first(prop) -> int", "tip": "The lowest-id live entity carrying a property, or -1.", "kind": "namespace-method", "page": "query-first.html", "section": "Query"}, "query-nearest": {"name": "Query.nearest", "sig": "Query.nearest(prop, pos, x_field, y_field, x, y) -> int", "tip": "The bearer of prop closest to a point, by squared distance.", "kind": "namespace-method", "page": "query-nearest.html", "section": "Query"}, "query-within": {"name": "Query.within", "sig": "Query.within(prop, pos, x, y, radius, x_field, y_field) -> []int", "tip": "Every bearer of prop inside a radius of a point.", "kind": "namespace-method", "page": "query-within.html", "section": "Query"}, "reflect-prop": {"name": "Reflect.prop", "sig": "Reflect.prop(name) -> int", "tip": "The property id for a property name, or -1 if unknown.", "kind": "namespace-method", "page": "reflect-prop.html", "section": "Reflect"}, "reflect-field": {"name": "Reflect.field", "sig": "Reflect.field(prop, name) -> int", "tip": "The field id of a named field within a property, or -1.", "kind": "namespace-method", "page": "reflect-field.html", "section": "Reflect"}, "reflect-prop_count": {"name": "Reflect.prop_count", "sig": "Reflect.prop_count() -> int", "tip": "How many properties the world schema defines.", "kind": "namespace-method", "page": "reflect-prop_count.html", "section": "Reflect"}, "reflect-prop_name": {"name": "Reflect.prop_name", "sig": "Reflect.prop_name(index) -> string", "tip": "The name of the property at an index (or \"\").", "kind": "namespace-method", "page": "reflect-prop_name.html", "section": "Reflect"}, "reflect-field_count": {"name": "Reflect.field_count", "sig": "Reflect.field_count(prop) -> int", "tip": "How many fields a property has.", "kind": "namespace-method", "page": "reflect-field_count.html", "section": "Reflect"}, "reflect-field_name": {"name": "Reflect.field_name", "sig": "Reflect.field_name(prop, index) -> string", "tip": "The name of the field at an index within a property.", "kind": "namespace-method", "page": "reflect-field_name.html", "section": "Reflect"}, "reflect-field_type": {"name": "Reflect.field_type", "sig": "Reflect.field_type(prop, index) -> string", "tip": "The type name of the field at an index within a property.", "kind": "namespace-method", "page": "reflect-field_type.html", "section": "Reflect"}, "reflect-get": {"name": "Reflect.get", "sig": "Reflect.get(entity, prop, field) -> int", "tip": "Read one field of an entity by (prop, field) id.", "kind": "namespace-method", "page": "reflect-get.html", "section": "Reflect"}, "reflect-set": {"name": "Reflect.set", "sig": "Reflect.set(entity, prop, field, value)", "tip": "Write one field of an entity by (prop, field) id.", "kind": "namespace-method", "page": "reflect-set.html", "section": "Reflect"}, "reflect-has": {"name": "Reflect.has", "sig": "Reflect.has(entity, prop) -> bool", "tip": "Does an entity carry a property?", "kind": "namespace-method", "page": "reflect-has.html", "section": "Reflect"}, "reflect-kind": {"name": "Reflect.kind", "sig": "Reflect.kind(entity) -> int", "tip": "The model id of an entity.", "kind": "namespace-method", "page": "reflect-kind.html", "section": "Reflect"}, "reflect-model": {"name": "Reflect.model", "sig": "Reflect.model(name) -> int", "tip": "The model id for a model name, or -1 if unknown.", "kind": "namespace-method", "page": "reflect-model.html", "section": "Reflect"}, "reflect-serialize": {"name": "Reflect.serialize", "sig": "Reflect.serialize(entity) -> value", "tip": "Walk an entity's components into a value tree.", "kind": "namespace-method", "page": "reflect-serialize.html", "section": "Reflect"}, "reflect-apply": {"name": "Reflect.apply", "sig": "Reflect.apply(entity, value) -> int", "tip": "Write a value tree's fields back into an entity.", "kind": "namespace-method", "page": "reflect-apply.html", "section": "Reflect"}, "camera-set": {"name": "Camera.set", "sig": "Camera.set(x, y)", "tip": "Place the camera at a world-space offset.", "kind": "namespace-method", "page": "camera-set.html", "section": "Camera"}, "camera-follow": {"name": "Camera.follow", "sig": "Camera.follow(x, y, lerp)", "tip": "Ease the camera toward centring a target point.", "kind": "namespace-method", "page": "camera-follow.html", "section": "Camera"}, "camera-shake": {"name": "Camera.shake", "sig": "Camera.shake(amount)", "tip": "Jitter the camera by up to +/- amount pixels (seeded RNG).", "kind": "namespace-method", "page": "camera-shake.html", "section": "Camera"}, "camera-zoom": {"name": "Camera.zoom", "sig": "Camera.zoom(scale)", "tip": "Scale the whole view about the screen centre by a Q16.16 factor (1.0 = none).", "kind": "namespace-method", "page": "camera-zoom.html", "section": "Camera"}, "camera-shake_for": {"name": "Camera.shake_for", "sig": "Camera.shake_for(amount: px, frames: n)", "tip": "Shake for a number of frames, then stop, with no bookkeeping.", "kind": "namespace-method", "page": "camera-shake_for.html", "section": "Camera"}, "light-ambient": {"name": "Light.ambient", "sig": "Light.ambient(color)", "tip": "Multiply the whole scene by a tint — the night/cave modulate.", "kind": "namespace-method", "page": "light-ambient.html", "section": "Light"}, "light-point": {"name": "Light.point", "sig": "Light.point(x, y, radius, color, energy)", "tip": "Add a radial glow that falls off with distance.", "kind": "namespace-method", "page": "light-point.html", "section": "Light"}, "light-occlude": {"name": "Light.occlude", "sig": "Light.occlude(x, y, width, height)", "tip": "Register a rectangle that blocks light — a hard shadow caster.", "kind": "namespace-method", "page": "light-occlude.html", "section": "Light"}, "light-clear_occluders": {"name": "Light.clear_occluders", "sig": "Light.clear_occluders()", "tip": "Forget every occluder — call once per frame before re-registering.", "kind": "namespace-method", "page": "light-clear_occluders.html", "section": "Light"}, "light-spot": {"name": "Light.spot", "sig": "Light.spot(x, y, radius, color, energy, direction, spread)", "tip": "A cone / flashlight light aimed at a direction with a half-angle spread.", "kind": "namespace-method", "page": "light-spot.html", "section": "Light"}, "light-falloff": {"name": "Light.falloff", "sig": "Light.falloff(exponent)", "tip": "Set the brightness-ramp exponent for later lights (1 linear, 2 quadratic…).", "kind": "namespace-method", "page": "light-falloff.html", "section": "Light"}, "light-soft": {"name": "Light.soft", "sig": "Light.soft(radius)", "tip": "Soft shadows — an occluder edge fades through a penumbra of this radius.", "kind": "namespace-method", "page": "light-soft.html", "section": "Light"}, "light-gel": {"name": "Light.gel", "sig": "Light.gel(color)", "tip": "A colour cookie — later lights gel from their centre colour to this rim colour.", "kind": "namespace-method", "page": "light-gel.html", "section": "Light"}, "light-clear_gel": {"name": "Light.clear_gel", "sig": "Light.clear_gel()", "tip": "Clear the gel — later lights are a flat single colour again.", "kind": "namespace-method", "page": "light-clear_gel.html", "section": "Light"}, "light-normal": {"name": "Light.normal", "sig": "Light.normal(x, y, width, height, nx, ny)", "tip": "Stamp a surface normal over a rectangle so lights shade it by facing (N·L).", "kind": "namespace-method", "page": "light-normal.html", "section": "Light"}, "light-clear_normals": {"name": "Light.clear_normals", "sig": "Light.clear_normals()", "tip": "Forget every stamped normal — call once per frame before re-stamping.", "kind": "namespace-method", "page": "light-clear_normals.html", "section": "Light"}, "light-height": {"name": "Light.height", "sig": "Light.height(height)", "tip": "Set the virtual height of lights above the surface, for normal-map shading.", "kind": "namespace-method", "page": "light-height.html", "section": "Light"}, "light-time_of_day": {"name": "Light.time_of_day", "sig": "Light.time_of_day(t)", "tip": "Set the ambient tint from a 0..1 time-of-day — a day/night cycle in one value.", "kind": "namespace-method", "page": "light-time_of_day.html", "section": "Light"}, "sprite-sheet": {"name": "Sprite.sheet", "sig": "Sprite.sheet(path, cellw, cellh) -> int", "tip": "Load a spritesheet and remember its cell grid; returns a sheet handle.", "kind": "namespace-method", "page": "sprite-sheet.html", "section": "Sprite"}, "sprite-cell": {"name": "Sprite.cell", "sig": "Sprite.cell(sheet, col, row) -> int", "tip": "One cell of a sheet, by grid coords; returns a sprite id.", "kind": "namespace-method", "page": "sprite-cell.html", "section": "Sprite"}, "sprite-cell_span": {"name": "Sprite.cell_span", "sig": "Sprite.cell_span(sheet, col, row, cols, rows) -> int", "tip": "A sprite spanning cols x rows cells (tall/wide art).", "kind": "namespace-method", "page": "sprite-cell_span.html", "section": "Sprite"}, "sprite-define": {"name": "Sprite.define", "sig": "Sprite.define(name, sheet, col, row) -> int", "tip": "Name a cell for later lookup; returns its sprite id.", "kind": "namespace-method", "page": "sprite-define.html", "section": "Sprite"}, "sprite-named": {"name": "Sprite.named", "sig": "Sprite.named(name) -> int", "tip": "Look up a named sprite's id, or -1.", "kind": "namespace-method", "page": "sprite-named.html", "section": "Sprite"}, "sprite-draw": {"name": "Sprite.draw", "sig": "Sprite.draw(id, x, y)", "tip": "Blit an atlas sprite at (x, y) through the camera/zoom/clip.", "kind": "namespace-method", "page": "sprite-draw.html", "section": "Sprite"}, "sprite-draw_scaled": {"name": "Sprite.draw_scaled", "sig": "Sprite.draw_scaled(id, x, y, scale)", "tip": "Blit an atlas sprite scaled by an integer factor.", "kind": "namespace-method", "page": "sprite-draw_scaled.html", "section": "Sprite"}, "sprite-width": {"name": "Sprite.width", "sig": "Sprite.width(id) -> int", "tip": "The atlas sprite's width in pixels.", "kind": "namespace-method", "page": "sprite-width.html", "section": "Sprite"}, "sprite-height": {"name": "Sprite.height", "sig": "Sprite.height(id) -> int", "tip": "The atlas sprite's height in pixels.", "kind": "namespace-method", "page": "sprite-height.html", "section": "Sprite"}, "sprite-strip": {"name": "Sprite.strip", "sig": "Sprite.strip(sheet, col, row, count, rows) -> int", "tip": "A run of animation frames as consecutive sprite ids.", "kind": "namespace-method", "page": "sprite-strip.html", "section": "Sprite"}, "sprite-draw_meter": {"name": "Sprite.draw_meter", "sig": "Sprite.draw_meter(x:, y:, value:, max:, per_icon:, spacing:, full:, half:, empty:)", "tip": "A value as a row of full / half / empty icons.", "kind": "namespace-method", "page": "sprite-draw_meter.html", "section": "Sprite"}, "assets-image": {"name": "Assets.image", "sig": "Assets.image(path) -> int", "tip": "Load a whole image file as one sprite; returns its id.", "kind": "namespace-method", "page": "assets-image.html", "section": "Assets"}, "assets-load": {"name": "Assets.load", "sig": "Assets.load(path) -> int", "tip": "Alias of Assets.image.", "kind": "namespace-method", "page": "assets-load.html", "section": "Assets"}, "assets-get": {"name": "Assets.get", "sig": "Assets.get(name) -> int", "tip": "Look up a named sprite's id (alias of Sprite.named).", "kind": "namespace-method", "page": "assets-get.html", "section": "Assets"}, "assets-enqueue": {"name": "Assets.enqueue", "sig": "Assets.enqueue(name, path)", "tip": "Queue a named image file to preload later.", "kind": "namespace-method", "page": "assets-enqueue.html", "section": "Assets"}, "assets-pump": {"name": "Assets.pump", "sig": "Assets.pump(max) -> int", "tip": "Load up to max queued assets this frame; returns how many it loaded.", "kind": "namespace-method", "page": "assets-pump.html", "section": "Assets"}, "assets-total": {"name": "Assets.total", "sig": "Assets.total() -> int", "tip": "How many assets are enqueued.", "kind": "namespace-method", "page": "assets-total.html", "section": "Assets"}, "assets-loaded": {"name": "Assets.loaded", "sig": "Assets.loaded() -> int", "tip": "How many enqueued assets have loaded so far.", "kind": "namespace-method", "page": "assets-loaded.html", "section": "Assets"}, "assets-ready": {"name": "Assets.ready", "sig": "Assets.ready() -> int", "tip": "1 once every enqueued asset has loaded.", "kind": "namespace-method", "page": "assets-ready.html", "section": "Assets"}, "assets-font": {"name": "Assets.font", "sig": "Assets.font(name: s) -> int", "tip": "A font loaded through the preload queue, by name.", "kind": "namespace-method", "page": "assets-font.html", "section": "Assets"}, "assets-progress": {"name": "Assets.progress", "sig": "Assets.progress() -> int", "tip": "Loading progress as a 0..100 percent.", "kind": "namespace-method", "page": "assets-progress.html", "section": "Assets"}, "assets-enqueue_dir": {"name": "Assets.enqueue_dir", "sig": "Assets.enqueue_dir(path: dir)", "tip": "Enqueue every file of a directory, named by its file name.", "kind": "namespace-method", "page": "assets-enqueue_dir.html", "section": "Assets"}, "kw-test": {"name": "test", "sig": "test \"name\" { … }", "tip": "A named test block, run automatically with pass/fail reporting.", "kind": "keyword", "page": "kw-test.html", "section": "Testing"}, "pool-live": {"name": "Pool.live", "sig": "Pool.live() -> int", "tip": "How many entities are currently alive.", "kind": "namespace-method", "page": "pool-live.html", "section": "Pool"}, "pool-free": {"name": "Pool.free", "sig": "Pool.free() -> int", "tip": "How many freed slots are waiting to be reused.", "kind": "namespace-method", "page": "pool-free.html", "section": "Pool"}, "pool-reserved": {"name": "Pool.reserved", "sig": "Pool.reserved() -> int", "tip": "High-water: how many slots have ever been allocated.", "kind": "namespace-method", "page": "pool-reserved.html", "section": "Pool"}, "pool-capacity": {"name": "Pool.capacity", "sig": "Pool.capacity() -> int", "tip": "The maximum number of entities.", "kind": "namespace-method", "page": "pool-capacity.html", "section": "Pool"}, "value-null": {"name": "Value.null", "sig": "Value.null() -> value", "tip": "An empty null node.", "kind": "namespace-method", "page": "value-null.html", "section": "Value"}, "value-int": {"name": "Value.int", "sig": "Value.int(n: int) -> value", "tip": "Wrap an int in a value node.", "kind": "namespace-method", "page": "value-int.html", "section": "Value"}, "value-fixed": {"name": "Value.fixed", "sig": "Value.fixed(f: fixed) -> value", "tip": "Wrap a fixed in a value node.", "kind": "namespace-method", "page": "value-fixed.html", "section": "Value"}, "value-bool": {"name": "Value.bool", "sig": "Value.bool(b: bool) -> value", "tip": "Wrap a bool in a value node.", "kind": "namespace-method", "page": "value-bool.html", "section": "Value"}, "value-str": {"name": "Value.str", "sig": "Value.str(s: string) -> value", "tip": "Wrap a string in a value node.", "kind": "namespace-method", "page": "value-str.html", "section": "Value"}, "value-list": {"name": "Value.list", "sig": "Value.list() -> value", "tip": "An empty list node.", "kind": "namespace-method", "page": "value-list.html", "section": "Value"}, "value-object": {"name": "Value.object", "sig": "Value.object() -> value", "tip": "An empty object node.", "kind": "namespace-method", "page": "value-object.html", "section": "Value"}, "value-add": {"name": "Value.add", "sig": "Value.add(list, item) -> value", "tip": "Append an item to a list; returns the list.", "kind": "namespace-method", "page": "value-add.html", "section": "Value"}, "value-put": {"name": "Value.put", "sig": "Value.put(obj, key: string, item) -> value", "tip": "Set a key on an object; returns the object.", "kind": "namespace-method", "page": "value-put.html", "section": "Value"}, "value-get": {"name": "Value.get", "sig": "Value.get(obj, key: string) -> value", "tip": "Read a member of an object by key.", "kind": "namespace-method", "page": "value-get.html", "section": "Value"}, "value-has": {"name": "Value.has", "sig": "Value.has(obj, key: string) -> bool", "tip": "Whether an object has a member.", "kind": "namespace-method", "page": "value-has.html", "section": "Value"}, "value-at": {"name": "Value.at", "sig": "Value.at(list, index: int) -> value", "tip": "Read a list item by index.", "kind": "namespace-method", "page": "value-at.html", "section": "Value"}, "value-key_at": {"name": "Value.key_at", "sig": "Value.key_at(obj, index: int) -> string", "tip": "The key of an object member by position.", "kind": "namespace-method", "page": "value-key_at.html", "section": "Value"}, "value-count": {"name": "Value.count", "sig": "Value.count(value) -> int", "tip": "Number of items/members in a list or object.", "kind": "namespace-method", "page": "value-count.html", "section": "Value"}, "value-kind": {"name": "Value.kind", "sig": "Value.kind(value) -> int", "tip": "The node's kind tag.", "kind": "namespace-method", "page": "value-kind.html", "section": "Value"}, "value-as_int": {"name": "Value.as_int", "sig": "Value.as_int(value) -> int", "tip": "Read a scalar node as an int.", "kind": "namespace-method", "page": "value-as_int.html", "section": "Value"}, "value-as_str": {"name": "Value.as_str", "sig": "Value.as_str(value) -> string", "tip": "Read a str node's text.", "kind": "namespace-method", "page": "value-as_str.html", "section": "Value"}, "json-encode": {"name": "Json.encode", "sig": "Json.encode(value) -> string", "tip": "Serialize a value tree to compact JSON text.", "kind": "namespace-method", "page": "json-encode.html", "section": "Json"}, "json-parse": {"name": "Json.parse", "sig": "Json.parse(text: string) -> value", "tip": "Parse JSON text into a value tree.", "kind": "namespace-method", "page": "json-parse.html", "section": "Json"}, "job-defer": {"name": "Job.defer", "sig": "Job.defer() -> Job", "tip": "A future you resolve yourself later.", "kind": "namespace-method", "page": "job-defer.html", "section": "Job"}, "job-run": {"name": "Job.run", "sig": "Job.run(kind, arg) -> Job", "tip": "Start a background compute job.", "kind": "namespace-method", "page": "job-run.html", "section": "Job"}, "job-fulfill": {"name": "Job.fulfill", "sig": "Job.fulfill(handle, value) -> void", "tip": "Resolve a pending job with a value.", "kind": "namespace-method", "page": "job-fulfill.html", "section": "Job"}, "job-fail": {"name": "Job.fail", "sig": "Job.fail(handle, error) -> void", "tip": "Resolve a pending job as failed.", "kind": "namespace-method", "page": "job-fail.html", "section": "Job"}, "job-cancel": {"name": "Job.cancel", "sig": "Job.cancel(handle) -> void", "tip": "Cancel a job before it finishes.", "kind": "namespace-method", "page": "job-cancel.html", "section": "Job"}, "job-pump": {"name": "Job.pump", "sig": "Job.pump(budget) -> int", "tip": "Advance background jobs; collect results.", "kind": "namespace-method", "page": "job-pump.html", "section": "Job"}, "job-done": {"name": "Job.done", "sig": "Job.done(handle) -> bool", "tip": "Has the job resolved (any outcome)?", "kind": "namespace-method", "page": "job-done.html", "section": "Job"}, "job-ok": {"name": "Job.ok", "sig": "Job.ok(handle) -> bool", "tip": "Did the job succeed?", "kind": "namespace-method", "page": "job-ok.html", "section": "Job"}, "job-failed": {"name": "Job.failed", "sig": "Job.failed(handle) -> bool", "tip": "Did the job fail?", "kind": "namespace-method", "page": "job-failed.html", "section": "Job"}, "job-cancelled": {"name": "Job.cancelled", "sig": "Job.cancelled(handle) -> bool", "tip": "Was the job cancelled?", "kind": "namespace-method", "page": "job-cancelled.html", "section": "Job"}, "job-result": {"name": "Job.result", "sig": "Job.result(handle) -> int", "tip": "The success value of a done job.", "kind": "namespace-method", "page": "job-result.html", "section": "Job"}, "job-error": {"name": "Job.error", "sig": "Job.error(handle) -> int", "tip": "The error code of a failed job.", "kind": "namespace-method", "page": "job-error.html", "section": "Job"}, "job-pending": {"name": "Job.pending", "sig": "Job.pending() -> int", "tip": "How many jobs are still unresolved.", "kind": "namespace-method", "page": "job-pending.html", "section": "Job"}, "job-free": {"name": "Job.free", "sig": "Job.free(handle) -> void", "tip": "Release a job slot back to the pool.", "kind": "namespace-method", "page": "job-free.html", "section": "Job"}, "job-parallel_for": {"name": "Job.parallel_for", "sig": "Job.parallel_for(count, work, ctx) -> void", "tip": "Run work(i, ctx) for every i in [0, count) across all cores; returns when every call is done.", "kind": "namespace-method", "page": "job-parallel_for.html", "section": "Job"}, "job-is_worker": {"name": "Job.is_worker", "sig": "Job.is_worker() -> bool", "tip": "True on a Job.parallel_for pool thread, false on the main thread.", "kind": "namespace-method", "page": "job-is_worker.html", "section": "Job"}, "promise-all": {"name": "Promise.all", "sig": "Promise.all(handles) -> Job", "tip": "Succeeds when every member succeeds.", "kind": "namespace-method", "page": "promise-all.html", "section": "Promise"}, "promise-race": {"name": "Promise.race", "sig": "Promise.race(handles) -> Job", "tip": "Succeeds when the first member does.", "kind": "namespace-method", "page": "promise-race.html", "section": "Promise"}, "promise-count_done": {"name": "Promise.count_done", "sig": "Promise.count_done(handles) -> int", "tip": "How many members have resolved.", "kind": "namespace-method", "page": "promise-count_done.html", "section": "Promise"}, "promise-all_done": {"name": "Promise.all_done", "sig": "Promise.all_done(handles) -> bool", "tip": "Have all members resolved?", "kind": "namespace-method", "page": "promise-all_done.html", "section": "Promise"}, "sync-mutex": {"name": "Sync.mutex", "sig": "Sync.mutex() -> int", "tip": "Create a cooperative lock.", "kind": "namespace-method", "page": "sync-mutex.html", "section": "Sync"}, "sync-lock": {"name": "Sync.lock", "sig": "Sync.lock(mutex) -> void", "tip": "Take the lock.", "kind": "namespace-method", "page": "sync-lock.html", "section": "Sync"}, "sync-unlock": {"name": "Sync.unlock", "sig": "Sync.unlock(mutex) -> void", "tip": "Release the lock.", "kind": "namespace-method", "page": "sync-unlock.html", "section": "Sync"}, "sync-try_lock": {"name": "Sync.try_lock", "sig": "Sync.try_lock(mutex) -> bool", "tip": "Take the lock only if it is free.", "kind": "namespace-method", "page": "sync-try_lock.html", "section": "Sync"}, "sync-atomic": {"name": "Sync.atomic", "sig": "Sync.atomic() -> int", "tip": "Create an atomic counter (starts at 0).", "kind": "namespace-method", "page": "sync-atomic.html", "section": "Sync"}, "sync-get": {"name": "Sync.get", "sig": "Sync.get(atomic) -> int", "tip": "Read the counter.", "kind": "namespace-method", "page": "sync-get.html", "section": "Sync"}, "sync-set": {"name": "Sync.set", "sig": "Sync.set(atomic, value) -> void", "tip": "Store a value in the counter.", "kind": "namespace-method", "page": "sync-set.html", "section": "Sync"}, "sync-add": {"name": "Sync.add", "sig": "Sync.add(atomic, delta) -> int", "tip": "Add to the counter; return the new value.", "kind": "namespace-method", "page": "sync-add.html", "section": "Sync"}, "sync-cas": {"name": "Sync.cas", "sig": "Sync.cas(atomic, expect, next) -> bool", "tip": "Compare-and-set the counter.", "kind": "namespace-method", "page": "sync-cas.html", "section": "Sync"}, "sync-channel": {"name": "Sync.channel", "sig": "Sync.channel() -> int", "tip": "Create a bounded int FIFO channel.", "kind": "namespace-method", "page": "sync-channel.html", "section": "Sync"}, "sync-send": {"name": "Sync.send", "sig": "Sync.send(channel, value) -> bool", "tip": "Enqueue a value (false if full).", "kind": "namespace-method", "page": "sync-send.html", "section": "Sync"}, "sync-recv": {"name": "Sync.recv", "sig": "Sync.recv(channel) -> int", "tip": "Dequeue the oldest value.", "kind": "namespace-method", "page": "sync-recv.html", "section": "Sync"}, "sync-can_recv": {"name": "Sync.can_recv", "sig": "Sync.can_recv(channel) -> bool", "tip": "Is there a value waiting?", "kind": "namespace-method", "page": "sync-can_recv.html", "section": "Sync"}, "sync-len": {"name": "Sync.len", "sig": "Sync.len(channel) -> int", "tip": "How many values are queued.", "kind": "namespace-method", "page": "sync-len.html", "section": "Sync"}, "sync-cpu_count": {"name": "Sync.cpu_count", "sig": "Sync.cpu_count() -> int", "tip": "The machine's logical cores (1 to 64); Job.parallel_for uses one thread per core.", "kind": "namespace-method", "page": "sync-cpu_count.html", "section": "Sync"}, "xml-parse": {"name": "Xml.parse", "sig": "Xml.parse(text: string) -> xml", "tip": "Parse XML text into an element tree.", "kind": "namespace-method", "page": "xml-parse.html", "section": "Xml"}, "xml-tag": {"name": "Xml.tag", "sig": "Xml.tag(node) -> string", "tip": "The element's tag name.", "kind": "namespace-method", "page": "xml-tag.html", "section": "Xml"}, "xml-text": {"name": "Xml.text", "sig": "Xml.text(node) -> string", "tip": "The element's character data.", "kind": "namespace-method", "page": "xml-text.html", "section": "Xml"}, "xml-attr": {"name": "Xml.attr", "sig": "Xml.attr(node, key: string) -> string", "tip": "An attribute's value by name.", "kind": "namespace-method", "page": "xml-attr.html", "section": "Xml"}, "xml-attr_int": {"name": "Xml.attr_int", "sig": "Xml.attr_int(node, key: string, dflt: int) -> int", "tip": "An attribute parsed as an integer.", "kind": "namespace-method", "page": "xml-attr_int.html", "section": "Xml"}, "xml-has": {"name": "Xml.has", "sig": "Xml.has(node, key: string) -> int", "tip": "Whether an attribute is present.", "kind": "namespace-method", "page": "xml-has.html", "section": "Xml"}, "xml-attr_count": {"name": "Xml.attr_count", "sig": "Xml.attr_count(node) -> int", "tip": "How many attributes the element has.", "kind": "namespace-method", "page": "xml-attr_count.html", "section": "Xml"}, "xml-child_count": {"name": "Xml.child_count", "sig": "Xml.child_count(node) -> int", "tip": "How many child elements.", "kind": "namespace-method", "page": "xml-child_count.html", "section": "Xml"}, "xml-child": {"name": "Xml.child", "sig": "Xml.child(node, index: int) -> xml", "tip": "A child element by index.", "kind": "namespace-method", "page": "xml-child.html", "section": "Xml"}, "xml-find": {"name": "Xml.find", "sig": "Xml.find(node, tag: string) -> xml", "tip": "The first child with a given tag.", "kind": "namespace-method", "page": "xml-find.html", "section": "Xml"}, "xml-count": {"name": "Xml.count", "sig": "Xml.count(node, tag: string) -> int", "tip": "How many children have a given tag.", "kind": "namespace-method", "page": "xml-count.html", "section": "Xml"}, "base64-decode": {"name": "Base64.decode", "sig": "Base64.decode(src: string) -> string", "tip": "Decode standard base64 text to bytes.", "kind": "namespace-method", "page": "base64-decode.html", "section": "Base64"}, "base64-encode": {"name": "Base64.encode", "sig": "Base64.encode(src: string) -> string", "tip": "Encode bytes as standard base64 text.", "kind": "namespace-method", "page": "base64-encode.html", "section": "Base64"}, "tiled-read": {"name": "Tiled.read", "sig": "Tiled.read(path: string) -> value", "tip": "Read a map file into the intermediate value tree.", "kind": "namespace-method", "page": "tiled-read.html", "section": "Tiled"}, "tiled-read_tsx": {"name": "Tiled.read_tsx", "sig": "Tiled.read_tsx(path: string) -> value", "tip": "Read a tileset file into a value object.", "kind": "namespace-method", "page": "tiled-read_tsx.html", "section": "Tiled"}, "tiled-load": {"name": "Tiled.load", "sig": "Tiled.load(path: string) -> map", "tip": "Load a map file into the runtime map model.", "kind": "namespace-method", "page": "tiled-load.html", "section": "Tiled"}, "tiled-gid": {"name": "Tiled.gid", "sig": "Tiled.gid(map, layer: int, x: int, y: int) -> int", "tip": "The raw GID at a cell in a tile layer.", "kind": "namespace-method", "page": "tiled-gid.html", "section": "Tiled"}, "tiled-resolve": {"name": "Tiled.resolve", "sig": "Tiled.resolve(map, gid: int) -> gidinfo", "tip": "Decode a GID into tileset, local id and flip flags.", "kind": "namespace-method", "page": "tiled-resolve.html", "section": "Tiled"}, "tiled-width": {"name": "Tiled.width", "sig": "Tiled.width(map) -> int", "tip": "The map width in tiles.", "kind": "namespace-method", "page": "tiled-width.html", "section": "Tiled"}, "tiled-height": {"name": "Tiled.height", "sig": "Tiled.height(map) -> int", "tip": "The map height in tiles.", "kind": "namespace-method", "page": "tiled-height.html", "section": "Tiled"}, "tiled-layer_count": {"name": "Tiled.layer_count", "sig": "Tiled.layer_count(map) -> int", "tip": "How many layers the map has.", "kind": "namespace-method", "page": "tiled-layer_count.html", "section": "Tiled"}, "tiled-layer_name": {"name": "Tiled.layer_name", "sig": "Tiled.layer_name(map, index: int) -> string", "tip": "A layer's name by index.", "kind": "namespace-method", "page": "tiled-layer_name.html", "section": "Tiled"}, "tiled-draw": {"name": "Tiled.draw", "sig": "Tiled.draw(map, camx: int, camy: int) -> void", "tip": "Draw every visible tile layer.", "kind": "namespace-method", "page": "tiled-draw.html", "section": "Tiled"}, "tiled-project": {"name": "Tiled.project", "sig": "Tiled.project(map, layer: int) -> void", "tip": "Project a collision layer to the byte tilemap.", "kind": "namespace-method", "page": "tiled-project.html", "section": "Tiled"}, "tiled-tree": {"name": "Tiled.tree", "sig": "Tiled.tree(map) -> value", "tip": "The underlying intermediate value tree.", "kind": "namespace-method", "page": "tiled-tree.html", "section": "Tiled"}, "tiled-tile_prop": {"name": "Tiled.tile_prop", "sig": "Tiled.tile_prop(map, gid: int, name: string) -> int", "tip": "Whether a GID's tile carries a bool property.", "kind": "namespace-method", "page": "tiled-tile_prop.html", "section": "Tiled"}, "tiled-collide": {"name": "Tiled.collide", "sig": "Tiled.collide(map, layer: int) -> void", "tip": "Drive collision from per-tile metadata.", "kind": "namespace-method", "page": "tiled-collide.html", "section": "Tiled"}, "tiled-collision_kind": {"name": "Tiled.collision_kind", "sig": "Tiled.collision_kind(map, gid: int) -> int", "tip": "Classify a GID's collision from its tile metadata.", "kind": "namespace-method", "page": "tiled-collision_kind.html", "section": "Tiled"}, "tiled-tile_shapes": {"name": "Tiled.tile_shapes", "sig": "Tiled.tile_shapes(map, gid: int) -> int", "tip": "Whether a GID's tile has a collision shape.", "kind": "namespace-method", "page": "tiled-tile_shapes.html", "section": "Tiled"}, "tiled-draw_anim": {"name": "Tiled.draw_anim", "sig": "Tiled.draw_anim(map, camx: int, camy: int, frame: int) -> void", "tip": "Draw with animated tiles advanced to a frame.", "kind": "namespace-method", "page": "tiled-draw_anim.html", "section": "Tiled"}, "tiled-frame_gid": {"name": "Tiled.frame_gid", "sig": "Tiled.frame_gid(map, gid: int, frame: int) -> int", "tip": "The current GID of an animated tile at a frame.", "kind": "namespace-method", "page": "tiled-frame_gid.html", "section": "Tiled"}, "tiled-animated": {"name": "Tiled.animated", "sig": "Tiled.animated(map, gid: int) -> int", "tip": "Whether a GID's tile is animated.", "kind": "namespace-method", "page": "tiled-animated.html", "section": "Tiled"}, "tiled-object_count": {"name": "Tiled.object_count", "sig": "Tiled.object_count(map, layer: int) -> int", "tip": "How many objects an object layer holds.", "kind": "namespace-method", "page": "tiled-object_count.html", "section": "Tiled"}, "tiled-object": {"name": "Tiled.object", "sig": "Tiled.object(map, layer: int, index: int) -> value", "tip": "An object on an object layer by index.", "kind": "namespace-method", "page": "tiled-object.html", "section": "Tiled"}, "tiled-object_shape": {"name": "Tiled.object_shape", "sig": "Tiled.object_shape(object) -> string", "tip": "Classify an object's shape.", "kind": "namespace-method", "page": "tiled-object_shape.html", "section": "Tiled"}, "tiled-prop": {"name": "Tiled.prop", "sig": "Tiled.prop(container, name: string) -> string", "tip": "A custom property's value, with class-default fallback.", "kind": "namespace-method", "page": "tiled-prop.html", "section": "Tiled"}, "tiled-prop_int": {"name": "Tiled.prop_int", "sig": "Tiled.prop_int(container, name: string) -> int", "tip": "A custom property parsed as an integer.", "kind": "namespace-method", "page": "tiled-prop_int.html", "section": "Tiled"}, "tiled-prop_type": {"name": "Tiled.prop_type", "sig": "Tiled.prop_type(container, name: string) -> string", "tip": "A custom property's declared type.", "kind": "namespace-method", "page": "tiled-prop_type.html", "section": "Tiled"}, "tiled-load_types": {"name": "Tiled.load_types", "sig": "Tiled.load_types(path: string) -> int", "tip": "Load a project custom-type table.", "kind": "namespace-method", "page": "tiled-load_types.html", "section": "Tiled"}, "tiled-template": {"name": "Tiled.template", "sig": "Tiled.template(path: string) -> value", "tip": "Read a template file into an object value.", "kind": "namespace-method", "page": "tiled-template.html", "section": "Tiled"}, "tiled-spawn": {"name": "Tiled.spawn", "sig": "Tiled.spawn(map, object, model: string) -> int", "tip": "Spawn a Ludic entity from an object.", "kind": "namespace-method", "page": "tiled-spawn.html", "section": "Tiled"}, "tiled-spawn_layer": {"name": "Tiled.spawn_layer", "sig": "Tiled.spawn_layer(map, layer: int, model: string) -> int", "tip": "Spawn every object on a layer.", "kind": "namespace-method", "page": "tiled-spawn_layer.html", "section": "Tiled"}, "tiled-cell_x": {"name": "Tiled.cell_x", "sig": "Tiled.cell_x(map, x: int, y: int) -> int", "tip": "The screen x of a tile cell for the map orientation.", "kind": "namespace-method", "page": "tiled-cell_x.html", "section": "Tiled"}, "tiled-cell_y": {"name": "Tiled.cell_y", "sig": "Tiled.cell_y(map, x: int, y: int) -> int", "tip": "The screen y of a tile cell for the map orientation.", "kind": "namespace-method", "page": "tiled-cell_y.html", "section": "Tiled"}, "tiled-layer_kind": {"name": "Tiled.layer_kind", "sig": "Tiled.layer_kind(map, layer: int) -> int", "tip": "A layer's kind.", "kind": "namespace-method", "page": "tiled-layer_kind.html", "section": "Tiled"}, "tiled-layer_opacity": {"name": "Tiled.layer_opacity", "sig": "Tiled.layer_opacity(map, layer: int) -> string", "tip": "A layer's opacity.", "kind": "namespace-method", "page": "tiled-layer_opacity.html", "section": "Tiled"}, "tiled-layer_tint": {"name": "Tiled.layer_tint", "sig": "Tiled.layer_tint(map, layer: int) -> string", "tip": "A layer's tint colour.", "kind": "namespace-method", "page": "tiled-layer_tint.html", "section": "Tiled"}, "tiled-layer_offsetx": {"name": "Tiled.layer_offsetx", "sig": "Tiled.layer_offsetx(map, layer: int) -> int", "tip": "A layer's horizontal offset.", "kind": "namespace-method", "page": "tiled-layer_offsetx.html", "section": "Tiled"}, "tiled-layer_offsety": {"name": "Tiled.layer_offsety", "sig": "Tiled.layer_offsety(map, layer: int) -> int", "tip": "A layer's vertical offset.", "kind": "namespace-method", "page": "tiled-layer_offsety.html", "section": "Tiled"}, "tiled-world": {"name": "Tiled.world", "sig": "Tiled.world(path: string) -> value", "tip": "Read a .world file.", "kind": "namespace-method", "page": "tiled-world.html", "section": "Tiled"}, "tiled-world_count": {"name": "Tiled.world_count", "sig": "Tiled.world_count(world) -> int", "tip": "How many maps a world stitches.", "kind": "namespace-method", "page": "tiled-world_count.html", "section": "Tiled"}, "tiled-world_map": {"name": "Tiled.world_map", "sig": "Tiled.world_map(world, index: int) -> value", "tip": "A world member map by index.", "kind": "namespace-method", "page": "tiled-world_map.html", "section": "Tiled"}, "ui-build": {"name": "Ui.build", "sig": "Ui.build()", "tip": "Construct every declared ui block (loads skins, measures fonts).", "kind": "namespace-method", "page": "ui-build.html", "section": "Ui"}, "ui-open": {"name": "Ui.open", "sig": "Ui.open(id: UI_Name)", "tip": "Make one menu active and focus its first button.", "kind": "namespace-method", "page": "ui-open.html", "section": "Ui"}, "ui-close": {"name": "Ui.close", "sig": "Ui.close()", "tip": "Deactivate the menu: no menu is open.", "kind": "namespace-method", "page": "ui-close.html", "section": "Ui"}, "ui-tick": {"name": "Ui.tick", "sig": "Ui.tick(key: k)", "tip": "Advance navigation by one key (the frame loop does this for you).", "kind": "namespace-method", "page": "ui-tick.html", "section": "Ui"}, "ui-clicked": {"name": "Ui.clicked", "sig": "Ui.clicked(id: UI_Name) -> bool", "tip": "Was this control activated this frame? (polled form of UiClicked)", "kind": "namespace-method", "page": "ui-clicked.html", "section": "Ui"}, "ui-set_text": {"name": "Ui.set_text", "sig": "Ui.set_text(id: UI_Name, text: s)", "tip": "Replace a label's or button's text.", "kind": "namespace-method", "page": "ui-set_text.html", "section": "Ui"}, "ui-render": {"name": "Ui.render", "sig": "Ui.render()", "tip": "Draw the active menu.", "kind": "namespace-method", "page": "ui-render.html", "section": "Ui"}, "file-open": {"name": "File.open", "sig": "File.open(path: p, mode: m) -> pointer", "tip": "Open a file (fopen); null when it cannot be opened.", "kind": "namespace-method", "page": "file-open.html", "section": "File"}, "file-read": {"name": "File.read", "sig": "File.read(file: f, buffer: b, count: n) -> int", "tip": "Read up to n bytes into a buffer; returns the bytes read.", "kind": "namespace-method", "page": "file-read.html", "section": "File"}, "file-write": {"name": "File.write", "sig": "File.write(file: f, buffer: b, count: n) -> int", "tip": "Write n bytes from a buffer; returns the bytes written.", "kind": "namespace-method", "page": "file-write.html", "section": "File"}, "file-seek": {"name": "File.seek", "sig": "File.seek(file: f, offset: o, whence: w)", "tip": "Move the file cursor (0 start, 1 current, 2 end).", "kind": "namespace-method", "page": "file-seek.html", "section": "File"}, "file-tell": {"name": "File.tell", "sig": "File.tell(file: f) -> int", "tip": "The cursor position in bytes.", "kind": "namespace-method", "page": "file-tell.html", "section": "File"}, "file-close": {"name": "File.close", "sig": "File.close(file: f)", "tip": "Close a handle from File.open.", "kind": "namespace-method", "page": "file-close.html", "section": "File"}, "font-load": {"name": "Font.load", "sig": "Font.load(path: p) -> int", "tip": "Load a TrueType font; returns a font handle.", "kind": "namespace-method", "page": "font-load.html", "section": "Font"}, "fx-sparks": {"name": "Fx.sparks", "sig": "Fx.sparks(x: px, y: px, color: c, count: n)", "tip": "A burst of sparks flying out of a point.", "kind": "namespace-method", "page": "fx-sparks.html", "section": "Fx"}, "fx-number": {"name": "Fx.number", "sig": "Fx.number(x: px, y: px, value: n, color: c)", "tip": "A number that floats up from a point and fades.", "kind": "namespace-method", "page": "fx-number.html", "section": "Fx"}, "fx-clear": {"name": "Fx.clear", "sig": "Fx.clear()", "tip": "Drop every spark and number at once.", "kind": "namespace-method", "page": "fx-clear.html", "section": "Fx"}, "prefab-spawn": {"name": "Prefab.spawn", "sig": "Prefab.spawn(name: s) -> entity", "tip": "Spawn a prefab chosen by name at runtime; -1 if none has that name.", "kind": "namespace-method", "page": "prefab-spawn.html", "section": "Prefab"}, "prefab-spawn_at": {"name": "Prefab.spawn_at", "sig": "Prefab.spawn_at(name: s, at: IVec2) -> entity", "tip": "Spawn a prefab by name and place it.", "kind": "namespace-method", "page": "prefab-spawn_at.html", "section": "Prefab"}, "app-splash_hide": {"name": "App.splash_hide", "sig": "App.splash_hide() -> void", "tip": "Dismiss the boot splash.", "kind": "namespace-method", "page": "app-splash_hide.html", "section": "App"}, "app-set_icon": {"name": "App.set_icon", "sig": "App.set_icon(path) -> void", "tip": "Set the Dock icon from an image the game ships.", "kind": "namespace-method", "page": "app-set_icon.html", "section": "App"}, "app-window_hide": {"name": "App.window_hide", "sig": "App.window_hide() -> void", "tip": "Take the game's window off the screen.", "kind": "namespace-method", "page": "app-window_hide.html", "section": "App"}, "app-window_show": {"name": "App.window_show", "sig": "App.window_show() -> void", "tip": "Bring the game's window back.", "kind": "namespace-method", "page": "app-window_show.html", "section": "App"}, "phase-start": {"name": "Start", "sig": "phase Start", "tip": "Runs once at boot, before the game loop — the place to set up initial state.", "kind": "phase", "page": "phase-start.html", "section": "Phases"}, "phase-input": {"name": "Input", "sig": "phase Input", "tip": "The first per-frame phase — read the keyboard and record the player's intent.", "kind": "phase", "page": "phase-input.html", "section": "Phases"}, "phase-fixedupdate": {"name": "FixedUpdate", "sig": "phase FixedUpdate", "tip": "The deterministic simulation step — physics and gameplay meant to be reproducible.", "kind": "phase", "page": "phase-fixedupdate.html", "section": "Phases"}, "phase-update": {"name": "Update", "sig": "phase Update", "tip": "The ordinary per-frame game-logic step, run after Input and FixedUpdate.", "kind": "phase", "page": "phase-update.html", "section": "Phases"}, "phase-lateupdate": {"name": "LateUpdate", "sig": "phase LateUpdate", "tip": "Runs each frame after Update and before Render.", "kind": "phase", "page": "phase-lateupdate.html", "section": "Phases"}, "phase-render": {"name": "Render", "sig": "phase Render", "tip": "The last per-frame phase — draw the world, then call Screen.show() once.", "kind": "phase", "page": "phase-render.html", "section": "Phases"}, "phase-overlay": {"name": "Overlay", "sig": "phase Overlay", "tip": "The HUD pass — runs after Render and after every engine-owned drawing system.", "kind": "phase", "page": "phase-overlay.html", "section": "Phases"}, "ns-angle": {"name": "Angle", "sig": "Angle.*", "kind": "namespace", "tip": "An auto-wrapping angle in radians, so you never juggle % TAU by hand.", "page": "ns-angle.html", "section": "Namespaces"}, "ns-anim": {"name": "Anim", "sig": "Anim.*", "kind": "namespace", "tip": "Spritesheet frame animation off the fixed frame clock.", "page": "ns-anim.html", "section": "Namespaces"}, "ns-app": {"name": "App", "sig": "App.*", "kind": "namespace", "tip": "The running application, as distinct from its window.", "page": "ns-app.html", "section": "Namespaces"}, "ns-assets": {"name": "Assets", "sig": "Assets.*", "kind": "namespace", "tip": "Loading and lookup for whole-file assets, paired with the Sprite.* atlas API.", "page": "ns-assets.html", "section": "Namespaces"}, "ns-audio": {"name": "Audio", "sig": "Audio.*", "kind": "namespace", "tip": "Sound effects and music.", "page": "ns-audio.html", "section": "Namespaces"}, "ns-base64": {"name": "Base64", "sig": "Base64.*", "kind": "namespace", "tip": "Standard base64 (RFC 4648) — Base64.decode reads base64 text into the bytes it stands for, and Base64.encode is the inverse.", "page": "ns-base64.html", "section": "Namespaces"}, "ns-bigint": {"name": "BigInt", "sig": "BigInt.*", "kind": "namespace", "tip": "Arbitrary-precision integers with no upper bound, for idle/incremental counters, exact huge currencies, and score arithmetic that a 32- or 64-bit int would…", "page": "ns-bigint.html", "section": "Namespaces"}, "ns-camera": {"name": "Camera", "sig": "Camera.*", "kind": "namespace", "tip": "A world-space camera — a draw offset threaded through the render path (the same offset Screen.camera sets).", "page": "ns-camera.html", "section": "Namespaces"}, "ns-clock": {"name": "Clock", "sig": "Clock.*", "kind": "namespace", "tip": "A game-controlled simulated clock: a single seconds counter the game owns.", "page": "ns-clock.html", "section": "Namespaces"}, "ns-collision": {"name": "Collision", "sig": "Collision.*", "kind": "namespace", "tip": "2D overlap tests on integer coordinates (pixels or tiles).", "page": "ns-collision.html", "section": "Namespaces"}, "ns-color": {"name": "Color", "sig": "Color.*", "kind": "namespace", "tip": "The named color palette.", "page": "ns-color.html", "section": "Namespaces"}, "ns-crypto": {"name": "Crypto", "sig": "Crypto.*", "kind": "namespace", "tip": "Secure, test-vector-backed hashing for the handful of security-sensitive things a game actually does: signing a save or leaderboard payload so casual tampering…", "page": "ns-crypto.html", "section": "Namespaces"}, "ns-date": {"name": "Date", "sig": "Date.*", "kind": "namespace", "tip": "Calendar days on the proleptic Gregorian calendar (UTC).", "page": "ns-date.html", "section": "Namespaces"}, "ns-datetime": {"name": "DateTime", "sig": "DateTime.*", "kind": "namespace", "tip": "Instants on the wall-clock timeline (UTC), stored as a plain int: the count of seconds since 1970-01-01, matching Time.now.", "page": "ns-datetime.html", "section": "Namespaces"}, "ns-decimal": {"name": "Decimal", "sig": "Decimal.*", "kind": "namespace", "tip": "Exact base-10 fixed-point numbers for game economies — prices, balances and taxes on values like 0.10 that binary floating point cannot represent, so they…", "page": "ns-decimal.html", "section": "Namespaces"}, "ns-dict": {"name": "Dict", "sig": "Dict.*", "kind": "namespace", "tip": "A hash map from string keys to int values — the everyday lookup a game needs, like resource counts or an id registry by name.", "page": "ns-dict.html", "section": "Namespaces"}, "ns-duration": {"name": "Duration", "sig": "Duration.*", "kind": "namespace", "tip": "Spans of real time, measured in whole seconds and carried in a plain int — so a duration adds, subtracts and compares with the ordinary operators…", "page": "ns-duration.html", "section": "Namespaces"}, "ns-ease": {"name": "Ease", "sig": "Ease.*", "kind": "namespace", "tip": "Tween curves — the \"juice\" layer.", "page": "ns-ease.html", "section": "Namespaces"}, "ns-file": {"name": "File", "sig": "File.*", "kind": "namespace", "tip": "Raw file access over the C stdio calls, for programs that read and write their own formats.", "page": "ns-file.html", "section": "Namespaces"}, "ns-font": {"name": "Font", "sig": "Font.*", "kind": "namespace", "tip": "TrueType fonts for the retained UI and for Screen.draw_text-style drawing.", "page": "ns-font.html", "section": "Namespaces"}, "ns-fs": {"name": "Fs", "sig": "Fs.*", "kind": "namespace", "tip": "The filesystem, wrapped into one safe, ergonomic API — no file descriptors, no byte buffers, no half-written files.", "page": "ns-fs.html", "section": "Namespaces"}, "ns-fx": {"name": "Fx", "sig": "Fx.*", "kind": "namespace", "tip": "Engine-owned transient effects.", "page": "ns-fx.html", "section": "Namespaces"}, "ns-grid": {"name": "Grid", "sig": "Grid.*", "kind": "namespace", "tip": "Tile geometry and pathfinding over the Map tilemap.", "page": "ns-grid.html", "section": "Namespaces"}, "ns-hash": {"name": "Hash", "sig": "Hash.*", "kind": "namespace", "tip": "Fast, non-cryptographic hashing for everyday game needs: turning string IDs into integer handles, mixing a few numbers into one deterministic seed, and…", "page": "ns-hash.html", "section": "Namespaces"}, "ns-http": {"name": "Http", "sig": "Http.*", "kind": "namespace", "tip": "A poll-based HTTP / HTTPS client for out-of-band data — leaderboards, cloud saves, remote config, telemetry, downloads.", "page": "ns-http.html", "section": "Namespaces"}, "ns-huge": {"name": "Huge", "sig": "Huge.*", "kind": "namespace", "tip": "Idle/incremental big numbers — magnitudes far past what a 32- or 64-bit integer can hold, for prestige currencies and exponential growth.", "page": "ns-huge.html", "section": "Namespaces"}, "ns-ivec2": {"name": "IVec2", "sig": "IVec2.*", "kind": "namespace", "tip": "Integer 2D vector math for tile and grid coordinates, cell offsets, and integer sizes.", "page": "ns-ivec2.html", "section": "Namespaces"}, "ns-input": {"name": "Input", "sig": "Input.*", "kind": "namespace", "tip": "Reading input.", "page": "ns-input.html", "section": "Namespaces"}, "ns-job": {"name": "Job", "sig": "Job.*", "kind": "namespace", "tip": "Background work that stays out of the frame.", "page": "ns-job.html", "section": "Namespaces"}, "ns-json": {"name": "Json", "sig": "Json.*", "kind": "namespace", "tip": "The text bridge over the Value tree: Json.encode turns a value tree into compact, stable JSON text and Json.parse reads it back.", "page": "ns-json.html", "section": "Namespaces"}, "ns-light": {"name": "Light", "sig": "Light.*", "kind": "namespace", "tip": "A software 2D light-accumulation pass over the framebuffer, run in a render phase after drawing the scene and before Screen.show.", "page": "ns-light.html", "section": "Namespaces"}, "ns-list": {"name": "List", "sig": "List.*", "kind": "namespace", "tip": "Operations over []T slices — length, ends access, push/pop, swap, search, and in-place reverse.", "page": "ns-list.html", "section": "Namespaces"}, "ns-log": {"name": "Log", "sig": "Log.*", "kind": "namespace", "tip": "Levelled, structured logging — the default way to answer \"what is my game doing?\" and \"why did that break?\", and a real step up from scattering print calls…", "page": "ns-log.html", "section": "Namespaces"}, "ns-map": {"name": "Map", "sig": "Map.*", "kind": "namespace", "tip": "A character grid the game paints and reads.", "page": "ns-map.html", "section": "Namespaces"}, "ns-math": {"name": "Math", "sig": "Math.*", "kind": "namespace", "tip": "Deterministic fixed-point math.", "page": "ns-math.html", "section": "Namespaces"}, "ns-memory": {"name": "Memory", "sig": "Memory.*", "kind": "namespace", "tip": "Raw memory — allocate byte and word buffers, copy and fill regions, and peek/poke individual bytes.", "page": "ns-memory.html", "section": "Namespaces"}, "ns-mime": {"name": "Mime", "sig": "Mime.*", "kind": "namespace", "tip": "Content-type detection for assets and downloads: map a filename to its MIME type, or refine the guess by peeking at a file's leading bytes.", "page": "ns-mime.html", "section": "Namespaces"}, "ns-motion": {"name": "Motion", "sig": "Motion.*", "kind": "namespace", "tip": "Ergonomic control of the engine-owned Motion component — the value tween the engine advances each tick (see the ECS engine-systems).", "page": "ns-motion.html", "section": "Namespaces"}, "ns-network": {"name": "Network", "sig": "Network.*", "kind": "namespace", "tip": "The low-level networking seam — send and poll datagrams, serialize and apply entity state, and read ownership and role.", "page": "ns-network.html", "section": "Namespaces"}, "ns-noise": {"name": "Noise", "sig": "Noise.*", "kind": "namespace", "tip": "Procedural noise — the primitive that terrain, caves, biomes, textures, clouds, wind, and object placement are built on.", "page": "ns-noise.html", "section": "Namespaces"}, "ns-os": {"name": "Os", "sig": "Os.*", "kind": "namespace", "tip": "The operating-system interface — the environment *around* the game rather than the game itself: the command line, environment variables, the standard streams,…", "page": "ns-os.html", "section": "Namespaces"}, "ns-path": {"name": "Path", "sig": "Path.*", "kind": "namespace", "tip": "Path manipulation as pure string operations — no disk access, no allocation surprises, just the joining and splitting every save system, mod loader, and asset…", "page": "ns-path.html", "section": "Namespaces"}, "ns-percent": {"name": "Percent", "sig": "Percent.*", "kind": "namespace", "tip": "A value clamped to [0, 1] — health fractions, volumes, and the t of an interpolation, without stray values slipping below 0 or above 1.", "page": "ns-percent.html", "section": "Namespaces"}, "ns-pool": {"name": "Pool", "sig": "Pool.*", "kind": "namespace", "tip": "Entity-pool statistics.", "page": "ns-pool.html", "section": "Namespaces"}, "ns-prefab": {"name": "Prefab", "sig": "Prefab.*", "kind": "namespace", "tip": "A prefab is a model with preset component fields — prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } } — spawned with spawn Grunt { Position { x:…", "page": "ns-prefab.html", "section": "Namespaces"}, "ns-process": {"name": "Process", "sig": "Process.*", "kind": "namespace", "tip": "Child processes, started and polled — what a launcher is made of.", "page": "ns-process.html", "section": "Namespaces"}, "ns-promise": {"name": "Promise", "sig": "Promise.*", "kind": "namespace", "tip": "Combine several Job futures and resolve the group on the main thread.", "page": "ns-promise.html", "section": "Namespaces"}, "ns-query": {"name": "Query", "sig": "Query.*", "kind": "namespace", "tip": "Spatial and set queries over the live entities that carry a property, built on the World reflection ABI.", "page": "ns-query.html", "section": "Namespaces"}, "ns-random": {"name": "Random", "sig": "Random.*", "kind": "namespace", "tip": "A seeded, deterministic RNG — same seed, same sequence, every run and every platform.", "page": "ns-random.html", "section": "Namespaces"}, "ns-rect": {"name": "Rect", "sig": "Rect.*", "kind": "namespace", "tip": "Axis-aligned rectangles for HUD layout boxes, hitboxes, and camera regions.", "page": "ns-rect.html", "section": "Namespaces"}, "ns-reflect": {"name": "Reflect", "sig": "Reflect.*", "kind": "namespace", "tip": "Runtime reflection over the world schema — inspect properties, fields, and entity state by name and by index.", "page": "ns-reflect.html", "section": "Namespaces"}, "ns-regex": {"name": "Regex", "sig": "Regex.*", "kind": "namespace", "tip": "Regular expressions with PCRE/PECL-compatible syntax over a **linear-time** Thompson NFA (a Pike VM), so a bad pattern from a modder can never trigger…", "page": "ns-regex.html", "section": "Namespaces"}, "ns-save": {"name": "Save", "sig": "Save.*", "kind": "namespace", "tip": "Whole-world save and restore — write a binary snapshot of the ECS world and read it back.", "page": "ns-save.html", "section": "Namespaces"}, "ns-screen": {"name": "Screen", "sig": "Screen.*", "kind": "namespace", "tip": "The 2D drawing surface.", "page": "ns-screen.html", "section": "Namespaces"}, "ns-set": {"name": "Set", "sig": "Set.*", "kind": "namespace", "tip": "A set of string members — membership tests for tags, unlocked achievements, or visited tiles.", "page": "ns-set.html", "section": "Namespaces"}, "ns-sprite": {"name": "Sprite", "sig": "Sprite.*", "kind": "namespace", "tip": "A namespaced spritesheet / atlas API.", "page": "ns-sprite.html", "section": "Namespaces"}, "ns-sync": {"name": "Sync", "sig": "Sync.*", "kind": "namespace", "tip": "The advanced, opt-in tier — here be dragons.", "page": "ns-sync.html", "section": "Namespaces"}, "ns-system": {"name": "System", "sig": "System.*", "kind": "namespace", "tip": "Low-level file and process access — raw file handles, reading a character, and running a shell command.", "page": "ns-system.html", "section": "Namespaces"}, "ns-text": {"name": "Text", "sig": "Text.*", "kind": "namespace", "tip": "String queries and building over string values — length, character access, substrings, search, prefix/suffix tests, and int conversion.", "page": "ns-text.html", "section": "Namespaces"}, "ns-tiled": {"name": "Tiled", "sig": "Tiled.*", "kind": "namespace", "tip": "Load and draw Tiled maps.", "page": "ns-tiled.html", "section": "Namespaces"}, "ns-time": {"name": "Time", "sig": "Time.*", "kind": "namespace", "tip": "The frame clock.", "page": "ns-time.html", "section": "Namespaces"}, "ns-tween": {"name": "Tween", "sig": "Tween.*", "kind": "namespace", "tip": "Value interpolation over a timeline, off the fixed frame clock.", "page": "ns-tween.html", "section": "Namespaces"}, "ns-udp": {"name": "Udp", "sig": "Udp.*", "kind": "namespace", "tip": "Plain IPv4 datagrams, polled — the transport under a game's own netcode: peer-to-peer play, a LAN lobby, a STUN query.", "page": "ns-udp.html", "section": "Namespaces"}, "ns-ui": {"name": "Ui", "sig": "Ui.*", "kind": "namespace", "tip": "The retained-mode menu API over a ui block.", "page": "ns-ui.html", "section": "Namespaces"}, "ns-unicode": {"name": "Unicode", "sig": "Unicode.*", "kind": "namespace", "tip": "Correct-by-default text over UTF-8.", "page": "ns-unicode.html", "section": "Namespaces"}, "ns-uuid": {"name": "Uuid", "sig": "Uuid.*", "kind": "namespace", "tip": "Universally-unique identifiers — stable IDs that don't collide, generated locally with no central authority handing out numbers.", "page": "ns-uuid.html", "section": "Namespaces"}, "ns-value": {"name": "Value", "sig": "Value.*", "kind": "namespace", "tip": "A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through.", "page": "ns-value.html", "section": "Namespaces"}, "ns-vector": {"name": "Vector", "sig": "Vector.*", "kind": "namespace", "tip": "2D vector math for positions, velocities, and directions.", "page": "ns-vector.html", "section": "Namespaces"}, "ns-world": {"name": "World", "sig": "World.*", "kind": "namespace", "tip": "Runtime reflection over the ECS world — read and write component fields by id, spawn and inspect entities, and register components at runtime.", "page": "ns-world.html", "section": "Namespaces"}, "ns-xml": {"name": "Xml", "sig": "Xml.*", "kind": "namespace", "tip": "A minimal, deterministic XML reader for the element/attribute/CDATA subset the native Tiled formats (TMX/TSX/TX) use.", "page": "ns-xml.html", "section": "Namespaces"}}, "colors_page": "ns-color"}; + const KEYWORDS = S.keywords || {}, TYPES = S.types || {}, PHASES = S.phases || {}; + const BUILTINS = S.builtins || {}, NSMETHODS = S.nsmethods || {}, ANNOTS = S.annotations || {}; + const NAMESPACES = S.namespaces || {}, TIPS = S.tips || {}, CARDS = S.cards || {}; + + function esc(s){ return s.replace(/&/g,"&").replace(//g,">"); } + const isIdStart = c => /[A-Za-z_]/.test(c); + const isId = c => /[A-Za-z0-9_]/.test(c); + + function link(href, id, tip, inner){ + const t = tip ? ' title="'+esc(tip).replace(/"/g,""")+'"' : ""; + const d = id ? ' data-id="'+id+'"' : ""; + return ''+inner+''; + } + + function highlight(src){ + let out = "", i = 0; const n = src.length; + const stack = []; // call-context stack for named-parameter linking + let pending = null; // ctx to push at the next "(" + const nextNonSpace = j => { while (j < n && src[j] === " ") j++; return j; }; + + while (i < n){ + const c = src[i]; + if (c === "#"){ let j=i; while(j'; i=j; continue; } + if (c === '"' || c === "'" || c === "`"){ const q=c; let j=i+1; while(j'; i=j; continue; } + if (c === "@"){ let j=i+1; while(j'+esc(at)+''); + else out += ''+esc(at)+''; + i=j; continue; } + if (/[0-9]/.test(c)){ let j=i; while(j'; i=j; continue; } + + if (isIdStart(c)){ + let j=i; while(j'+word+''); + out += '.'; + if (word === "Color"){ + out += link("ns-color.html#"+member.toLowerCase(), "", "Named color "+word+"."+member+".", ''+esc(member)+''); + } else { + const key=word+"."+member, m=NSMETHODS[key]; + if (m){ + out += link(m.id+".html", m.id, TIPS[key]||key, ''+esc(member)+''); + if (src[nextNonSpace(k)]==="(") pending = {page:m.id, params:new Set(m.params||[])}; + } else out += ''+esc(member)+''; + } + i=k; continue; + } + + const ns = nextNonSpace(j); + const callish = src[ns] === "("; + const labelish = src[ns] === ":"; + const top = stack.length ? stack[stack.length-1] : null; + + if (KEYWORDS[word]){ + out += link(KEYWORDS[word]+".html", KEYWORDS[word], TIPS[word], ''+word+''); + } else if (BUILTINS[word] && callish){ + const b=BUILTINS[word]; + out += link(b.id+".html", b.id, TIPS[word], ''+word+''); + pending = {page:b.id, params:new Set(b.params||[])}; + } else if (labelish && top && top.params.has(word)){ + out += link(top.page+".html#param-"+word, "", "parameter: "+word, ''+word+''); + } else if (TYPES[word]){ + out += link(TYPES[word]+".html", TYPES[word], TIPS[word]||("The "+word+" type."), ''+word+''); + } else if (PHASES[word]){ + out += link(PHASES[word]+".html", PHASES[word], TIPS[word]||("The "+word+" phase."), ''+word+''); + } else if (callish){ + out += ''+esc(word)+''; + } else { + out += esc(word); + } + i=j; continue; + } + + if (c === "("){ stack.push(pending); pending=null; out+='('; i++; continue; } + if (c === ")"){ stack.pop(); out+=')'; i++; continue; } + if (/[{}\[\]=<>+\-*\/%,.:;!&|~]/.test(c)){ out+=''+esc(c)+''; i++; continue; } + out += esc(c); i++; + } + return out; + } + + function highlightAll(){ + document.querySelectorAll('pre[data-lang="ludic"]').forEach(pre => { pre.innerHTML = highlight(pre.textContent); }); + } + + /* ---- tooltip cards: click a token to open; click the card to open the --- + ---- page in a new tab; click outside (or Escape) to close ------------- */ + let cardEl = null, cardAnchor = null; + function ensureCard(){ + if (cardEl) return cardEl; + cardEl = document.createElement("div"); cardEl.className = "hovercard"; cardEl.hidden = true; + cardEl.addEventListener("click", () => { + const h = cardEl.dataset.href; if (h) window.open(h, "_blank", "noopener"); + }); + document.body.appendChild(cardEl); return cardEl; + } + function positionCard(a){ + const el = cardEl, r = a.getBoundingClientRect(); + const cw = el.offsetWidth, ch = el.offsetHeight; + let left = r.left, top = r.bottom + 8; + if (left + cw > window.innerWidth - 12) left = window.innerWidth - cw - 12; + if (left < 12) left = 12; + if (top + ch > window.innerHeight - 12) top = r.top - ch - 8; + el.style.left = Math.max(12,left) + "px"; el.style.top = Math.max(12,top) + "px"; + } + function showCard(a){ + const id = a.getAttribute("data-id"); if (!id) return; + const c = CARDS[id]; if (!c) return; + const el = ensureCard(); + el.innerHTML = '
' + + (c.kind||"").replace("namespace-method","method") + ''+esc(c.name||"")+'
' + + (c.sig ? ''+esc(c.sig)+'' : "") + + '
'+esc(c.tip||"")+'
' + + '
'+esc(c.section||"")+' · open in new tab ↗
'; + el.dataset.href = a.getAttribute("href") || ""; + el.hidden = false; cardAnchor = a; positionCard(a); + } + function hideCard(){ if (cardEl){ cardEl.hidden = true; cardAnchor = null; } } + function installCards(){ + document.addEventListener("click", e => { + const a = e.target.closest && e.target.closest("a.tok[data-id]"); + if (a){ e.preventDefault(); showCard(a); return; } // token -> open card + if (cardEl && !cardEl.hidden && !(e.target.closest && e.target.closest(".hovercard"))) hideCard(); // outside -> close + }); + document.addEventListener("keydown", e => { if (e.key === "Escape") hideCard(); }); + window.addEventListener("resize", hideCard); + window.addEventListener("scroll", () => { + if (cardEl && !cardEl.hidden && cardAnchor){ + const r = cardAnchor.getBoundingClientRect(); + if (r.bottom < 0 || r.top > window.innerHeight) hideCard(); else positionCard(cardAnchor); + } + }, {passive:true}); + } + + /* ---- flash the scrolled-to target ------------------------------------- */ + function flash(el){ if (!el) return; el.classList.remove("flash"); void el.offsetWidth; el.classList.add("flash"); } + function flashTarget(){ + const go = () => { + // Only a deep link gets a flash. The old fallback flashed the page title + // on every plain visit, which points at nothing the reader was looking for. + const h = location.hash ? decodeURIComponent(location.hash.slice(1)) : ""; + if (h) flash(document.getElementById(h)); + }; + if (document.readyState !== "loading") go(); else document.addEventListener("DOMContentLoaded", go); + window.addEventListener("hashchange", () => { const el = document.getElementById(decodeURIComponent(location.hash.slice(1))); flash(el); }); + } + + /* ---- fuzzy search on the API index ------------------------------------ */ + function subseq(q, s){ let i=0; for (let k=0;k { + const q = box.value.trim().toLowerCase(); + let shown = 0; + items.forEach(it => { + const name = (it.getAttribute("data-name")||"").toLowerCase(); + const tip = (it.getAttribute("data-tip")||"").toLowerCase(); + const hit = !q || name.includes(q) || tip.includes(q) || subseq(q, name); + it.style.display = hit ? "" : "none"; if (hit) shown++; + }); + secs.forEach(sec => { const any = [...sec.querySelectorAll(".idx-item")].some(i=>i.style.display!=="none"); sec.style.display = any ? "" : "none"; }); + if (none) none.hidden = shown !== 0; + }; + box.addEventListener("input", run); + box.addEventListener("keydown", e => { if (e.key === "Enter"){ const first = items.find(i=>i.style.display!=="none"); if (first) location.href = first.getAttribute("href"); } }); + } + + /* ---- mobile nav toggle (auto-wired on every page) ------------------- */ + function initNav(){ + document.querySelectorAll(".nav-toggle").forEach(btn => { + btn.addEventListener("click", () => { + const h = btn.closest("header.nav"); if (!h) return; + const open = h.classList.toggle("open"); + btn.setAttribute("aria-expanded", open ? "true" : "false"); + }); + }); + document.querySelectorAll("header.nav .nav-links a").forEach(a => { + a.addEventListener("click", () => { + const h = a.closest("header.nav"); + if (h){ h.classList.remove("open"); const b = h.querySelector(".nav-toggle"); if (b) b.setAttribute("aria-expanded","false"); } + }); + }); + } + if (document.readyState !== "loading") initNav(); else document.addEventListener("DOMContentLoaded", initNav); + + global.Ludic = { highlight, highlightAll, installCards, flashTarget, installSearch, initNav, SYMBOLS: S }; +})(window); diff --git a/map-border.html b/map-border.html new file mode 100644 index 00000000..80323039 --- /dev/null +++ b/map-border.html @@ -0,0 +1,30 @@ + + + + + +Map.border — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.border
+
+ method +

Map.border

+
+ Map.border(glyph: g) +

Writes glyph along the map's edge — the arena wall around a generated room.

+ +

Example

# doc-check: skip — illustrative
+Map.fill(glyph: '.')
+Map.border(glyph: '#')
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-fill.html b/map-fill.html new file mode 100644 index 00000000..0e585886 --- /dev/null +++ b/map-fill.html @@ -0,0 +1,30 @@ + + + + + +Map.fill — Ludic + + + + + + +
+ +
+ method +

Map.fill

+
+ Map.fill(glyph: g) +

Fills the whole map with one glyph — the first stroke of a generated room.

+ +

Example

# doc-check: skip — illustrative
+Map.size(20, 15)
+Map.fill(glyph: '.')
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-get.html b/map-get.html new file mode 100644 index 00000000..0bf2740f --- /dev/null +++ b/map-get.html @@ -0,0 +1,29 @@ + + + + + +Map.get — Ludic + + + + + + +
+ +
+ method +

Map.get

+
+ Map.get(x: tx, y: ty) -> int +

Reads one cell of the tilemap by tile coordinates; outside the map it answers '#', so the edge of the world reads as wall.

+ +

Example

# doc-check: skip — illustrative
+if Map.get(x: 3, y: 4) == '.' { … }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-height.html b/map-height.html new file mode 100644 index 00000000..56b00de6 --- /dev/null +++ b/map-height.html @@ -0,0 +1,29 @@ + + + + + +Map.height — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.height
+
+ method +

Map.height

+
+ Map.height() -> int +

The height set by Map.size.

+ +

Example

# doc-check: skip — illustrative
+for y in 0 .. Map.height() { … }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-is_solid.html b/map-is_solid.html new file mode 100644 index 00000000..0526d0cb --- /dev/null +++ b/map-is_solid.html @@ -0,0 +1,29 @@ + + + + + +Map.is_solid — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.is_solid
+
+ method +

Map.is_solid

+
+ Map.is_solid(x: tx, y: ty) -> bool +

True when the cell holds one of the solid glyphs the Solids configuration names (wall, solid2), or lies outside the map. With no Solids entity nothing is solid. The move system and the projectiles use the same answer.

+ +

Example

# doc-check: skip — illustrative
+if Map.is_solid(x: tile.x, y: tile.y) { continue }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-is_solid_at.html b/map-is_solid_at.html new file mode 100644 index 00000000..e0ee791c --- /dev/null +++ b/map-is_solid_at.html @@ -0,0 +1,29 @@ + + + + + +Map.is_solid_at — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.is_solid_at
+
+ method +

Map.is_solid_at

+
+ Map.is_solid_at(x: px, y: py) -> bool +

Map.is_solid for a pixel position, using the tile size from the Solids config.

+ +

Example

# doc-check: skip — illustrative
+if not Map.is_solid_at(x: pushed.x, y: pushed.y) { Position.x = pushed.x }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-random_cell.html b/map-random_cell.html new file mode 100644 index 00000000..c4b7c634 --- /dev/null +++ b/map-random_cell.html @@ -0,0 +1,30 @@ + + + + + +Map.random_cell — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.random_cell
+
+ method +

Map.random_cell

+
+ Map.random_cell(glyph: g) -> IVec2 +

Picks a random cell whose glyph matches: random tries first, then a sweep, so a map with any such cell always yields one; (-1, -1) when none exists. Deterministic, from the seeded RNG.

+ +

Example

# doc-check: skip — illustrative
+let tile = Map.random_cell(glyph: '.')
+Spawn.enemy(kind, IVec2.scale(tile, 16))
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-random_cell_far.html b/map-random_cell_far.html new file mode 100644 index 00000000..2903cf41 --- /dev/null +++ b/map-random_cell_far.html @@ -0,0 +1,29 @@ + + + + + +Map.random_cell_far — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.random_cell_far
+
+ method +

Map.random_cell_far

+
+ Map.random_cell_far(glyph:, from:, min_tiles:) -> IVec2 +

Like Map.random_cell, but at least min_tiles from from when it can find one — a spawn point away from the hero.

+ +

Example

# doc-check: skip — illustrative
+let tile = Map.random_cell_far(glyph: '.', from: hero_tile, min_tiles: 6)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-rect.html b/map-rect.html new file mode 100644 index 00000000..0ee9e670 --- /dev/null +++ b/map-rect.html @@ -0,0 +1,29 @@ + + + + + +Map.rect — Ludic + + + + + + +
+ +
+ method +

Map.rect

+
+ Map.rect(x: tx, y: ty, width: w, height: h, glyph: g) +

Writes glyph into every cell of the rectangle; cells outside the map are skipped. A wall slab, a carved corridor, a cleared lane.

+ +

Example

# doc-check: skip — illustrative
+Map.rect(x: 9, y: 1, width: 2, height: 13, glyph: '.')   # a lane the cover never blocks
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-row.html b/map-row.html new file mode 100644 index 00000000..8ea15c2e --- /dev/null +++ b/map-row.html @@ -0,0 +1,58 @@ + + + + + +Map.row — Ludic + + + + + + +
+ +
+ method +

Map.row

+
+ Map.row(y, cells) +

Fills a single map row from a string, one character per cell — an easy, readable way to author a level directly in source. Character y selects the row (from the top, starting at 0), and each character of cells becomes the tile at that column. Call Map.size first, then one Map.row per row; the string length should match the map width. By convention '#' is a wall and '.' is open floor, but you may use any characters as your own tile codes and interpret them however you like with Map.tile.

+

Parameters

ythe row index to fill, from the top (0-based)
cellsa string of tile characters, one per column
+

Example

program HandDrawnLevel {
+  const GRID_WIDTH: int = 10
+  const GRID_HEIGHT: int = 5
+  const TILE_SIZE: int = 16
+
+  handler BuildLevel phase Start {
+    Map.size(width: GRID_WIDTH, height: GRID_HEIGHT)
+    Map.row(y: 0, cells: "##########")
+    Map.row(y: 1, cells: "#........#")
+    Map.row(y: 2, cells: "#..####..#")
+    Map.row(y: 3, cells: "#........#")
+    Map.row(y: 4, cells: "##########")
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for row in 0 .. GRID_HEIGHT {
+      for column in 0 .. GRID_WIDTH {
+        if Map.tile(x: column, y: row) == '#' {
+          Screen.fill_rectangle(
+            x: column * TILE_SIZE,
+            y: row * TILE_SIZE,
+            width: TILE_SIZE,
+            height: TILE_SIZE,
+            color: Color.White)
+        }
+      }
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-set.html b/map-set.html new file mode 100644 index 00000000..b370d6fd --- /dev/null +++ b/map-set.html @@ -0,0 +1,29 @@ + + + + + +Map.set — Ludic + + + + + + +
+ +
+ method +

Map.set

+
+ Map.set(x: tx, y: ty, glyph: g) +

Writes one cell of the tilemap. A game edits the engine's grid directly — carving a door, placing a pillar — instead of keeping its own copy and re-stamping rows.

+ +

Example

# doc-check: skip — illustrative
+Map.set(x: 9, y: 0, glyph: '.')      # open the north door
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-size.html b/map-size.html new file mode 100644 index 00000000..ce82ecaa --- /dev/null +++ b/map-size.html @@ -0,0 +1,57 @@ + + + + + +Map.size — Ludic + + + + + + +
+ +
+ method +

Map.size

+
+ Map.size(width, height) +

Declares the dimensions of the built-in tilemap, in cells (not pixels). Call it once during a Start-phase handler before you fill the map with Map.row, so the runtime knows how many columns and rows to allocate. The tilemap is a lightweight, character-based grid the whole program can read with Map.tile, which is handy for static level geometry such as walls and floors. Reads outside these bounds are treated as a wall character, so the edge of the world is solid for free.

+

Parameters

widththe map width in cells (columns)
heightthe map height in cells (rows)
+

Example

program LevelBounds {
+  const GRID_WIDTH: int = 12
+  const GRID_HEIGHT: int = 4
+  const TILE_SIZE: int = 16
+
+  handler BuildLevel phase Start {
+    Map.size(width: GRID_WIDTH, height: GRID_HEIGHT)
+    Map.row(y: 0, cells: "############")
+    Map.row(y: 1, cells: "#..........#")
+    Map.row(y: 2, cells: "#..........#")
+    Map.row(y: 3, cells: "############")
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for row in 0 .. GRID_HEIGHT {
+      for column in 0 .. GRID_WIDTH {
+        if Map.tile(x: column, y: row) == '#' {
+          Screen.fill_rectangle(
+            x: column * TILE_SIZE,
+            y: row * TILE_SIZE,
+            width: TILE_SIZE,
+            height: TILE_SIZE,
+            color: Color.White)
+        }
+      }
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-tile.html b/map-tile.html new file mode 100644 index 00000000..c8973b95 --- /dev/null +++ b/map-tile.html @@ -0,0 +1,64 @@ + + + + + +Map.tile — Ludic + + + + + + +
+ +
+ method +

Map.tile

+
+ Map.tile(x, y) -> int +

Reads the tile character at cell (x, y) and returns it as a character code, so you can compare it against literals like '#' or '.'. Use it for collision checks (is the cell ahead a wall?), rendering the map, or querying your own custom tile codes. Cells outside the map you set with Map.size answer '#', so the border of the world acts as a wall automatically and you never need special edge-of-map handling. Coordinates are cell indices, not pixels.

+

Parameters

xthe cell column to read (0-based)
ythe cell row to read (0-based)
+

Example

program WallCollision {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler BuildLevel phase Start {
+    Map.size(width: 8, height: 4)
+    Map.row(y: 0, cells: "########")
+    Map.row(y: 1, cells: "#..##..#")
+    Map.row(y: 2, cells: "#......#")
+    Map.row(y: 3, cells: "########")
+    spawn Player { Position { column: 1, row: 2 } }
+  }
+
+  handler MovePlayer phase Update {
+    for (Position) in query [Position, {Player}] {
+      let next_column = Position.column + 1
+      if Map.tile(x: next_column, y: Position.row) != '#' {
+        Position.column = next_column
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-to_tile.html b/map-to_tile.html new file mode 100644 index 00000000..1cd976d0 --- /dev/null +++ b/map-to_tile.html @@ -0,0 +1,29 @@ + + + + + +Map.to_tile — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.to_tile
+
+ method +

Map.to_tile

+
+ Map.to_tile(pixel: IVec2) -> IVec2 +

Divides a pixel position by the tile size of the Solids config.

+ +

Example

# doc-check: skip — illustrative
+let tile = Map.to_tile(pixel: Collider.center(player))
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/map-width.html b/map-width.html new file mode 100644 index 00000000..cfbebada --- /dev/null +++ b/map-width.html @@ -0,0 +1,29 @@ + + + + + +Map.width — Ludic + + + + + + +
+
API Reference › Map — tilemap › Map.width
+
+ method +

Map.width

+
+ Map.width() -> int +

The width set by Map.size.

+ +

Example

# doc-check: skip — illustrative
+for x in 0 .. Map.width() { … }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-abs.html b/math-abs.html new file mode 100644 index 00000000..5cd20429 --- /dev/null +++ b/math-abs.html @@ -0,0 +1,35 @@ + + + + + +Math.abs — Ludic + + + + + + +
+
API Reference › Math › Math.abs
+
+ method +

Math.abs

+
+ Math.abs(x) -> int +

Returns the absolute value of x — its distance from zero, always non-negative. It preserves the type of its argument, so Math.abs of a fixed stays fixed. Reach for it when you care about how far apart two things are regardless of direction, such as the gap between a target and current position, or the speed behind a signed velocity. The bare form abs(x) is kept as a convenience alias.

+

Parameters

xthe value whose magnitude you want
+

Example

program ChaseDistance {
+  var target: int = 40
+  var here: int = 12
+
+  handler Report phase Update {
+    let gap = Math.abs(target - here)   # 28, regardless of which is larger
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-acos.html b/math-acos.html new file mode 100644 index 00000000..43250ee0 --- /dev/null +++ b/math-acos.html @@ -0,0 +1,32 @@ + + + + + +Math.acos — Ludic + + + + + + +
+
API Reference › Math › Math.acos
+
+ method +

Math.acos

+
+ Math.acos(x) -> fixed +

Returns the angle in radians whose cosine is x, in 0..pi. The input should be in -1.0..1.0. Handy for recovering the angle between two normalized directions from their dot product.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let between = Math.acos(dot)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-asin.html b/math-asin.html new file mode 100644 index 00000000..8a7843c1 --- /dev/null +++ b/math-asin.html @@ -0,0 +1,32 @@ + + + + + +Math.asin — Ludic + + + + + + +
+
API Reference › Math › Math.asin
+
+ method +

Math.asin

+
+ Math.asin(x) -> fixed +

Returns the angle in radians whose sine is x, in -pi/2..pi/2. The input should be in -1.0..1.0. Built from Math.atan2 and Math.sqrt, so it is deterministic.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let angle = Math.asin(height / radius)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-atan2.html b/math-atan2.html new file mode 100644 index 00000000..5293712c --- /dev/null +++ b/math-atan2.html @@ -0,0 +1,32 @@ + + + + + +Math.atan2 — Ludic + + + + + + +
+
API Reference › Math › Math.atan2
+
+ method +

Math.atan2

+
+ Math.atan2(y, x) -> fixed +

Returns the angle in radians from the positive x-axis to the point (x, y), in the full range -pi..pi — the standard way to get a heading from a direction vector. Note the argument order is y then x. Deterministic fixed-point, accurate to within about a degree.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let heading = Math.atan2(target_y - y, target_x - x)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-ceil.html b/math-ceil.html new file mode 100644 index 00000000..a8e1e44a --- /dev/null +++ b/math-ceil.html @@ -0,0 +1,36 @@ + + + + + +Math.ceil — Ludic + + + + + + +
+
API Reference › Math › Math.ceil
+
+ method +

Math.ceil

+
+ Math.ceil(x) -> int +

Takes a fixed value and returns the smallest int not less than it — rounding toward positive infinity, so Math.ceil(2.1) is 3 and Math.ceil(4.0) stays 4. Use it when you need to cover a fractional amount with whole units: how many full tiles a span touches, or how many rows a variable-height list needs.

+

Parameters

xthe fixed value to round up
+

Example

program RowsNeeded {
+  const ROW_HEIGHT: fixed = 12.0
+
+  var content_height: fixed = 30.0
+
+  handler Layout phase Update {
+    let rows = Math.ceil(content_height / ROW_HEIGHT)   # 3
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-clamp.html b/math-clamp.html new file mode 100644 index 00000000..ad61cf19 --- /dev/null +++ b/math-clamp.html @@ -0,0 +1,36 @@ + + + + + +Math.clamp — Ludic + + + + + + +
+
API Reference › Math › Math.clamp
+
+ method +

Math.clamp

+
+ Math.clamp(v, lo, hi) -> int +

Returns v held inside the inclusive range lo..hi: below lo it returns lo, above hi it returns hi, otherwise v unchanged. It is the single-call form of Math.max(lo, Math.min(v, hi)) and works for int and fixed alike. Use it to keep a camera inside the world, a slider within its track, or a stat within its legal bounds. The bare form clamp(v, lo, hi) is kept as a convenience alias.

+

Parameters

vthe value to constrain
lothe lowest allowed result (inclusive)
hithe highest allowed result (inclusive)
+

Example

program CameraBounds {
+  const WORLD_WIDTH: int = 320
+
+  var camera_x: int = 0
+
+  handler Follow phase Update {
+    camera_x = Math.clamp(camera_x, 0, WORLD_WIDTH - 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-cos.html b/math-cos.html new file mode 100644 index 00000000..79cd956b --- /dev/null +++ b/math-cos.html @@ -0,0 +1,32 @@ + + + + + +Math.cos — Ludic + + + + + + +
+
API Reference › Math › Math.cos
+
+ method +

Math.cos

+
+ Math.cos(radians) -> fixed +

Returns the cosine of radians as a fixed in -1.0..1.0, computed as Math.sin(radians + pi/2) off the same deterministic table. Use Math.cos and Math.sin together to convert an angle into an (x, y) direction — for orbits, aiming, or steering.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let x = center_x + Math.cos(angle) * radius
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-deg_to_rad.html b/math-deg_to_rad.html new file mode 100644 index 00000000..1f9217d7 --- /dev/null +++ b/math-deg_to_rad.html @@ -0,0 +1,32 @@ + + + + + +Math.deg_to_rad — Ludic + + + + + + +
+
API Reference › Math › Math.deg_to_rad
+
+ method +

Math.deg_to_rad

+
+ Math.deg_to_rad(degrees) -> fixed +

Converts an angle from degrees to radians (multiplying by pi/180), since Math.sin/Math.cos/Math.tan all take radians. Use it when your data or design speaks in degrees — a 90-degree turn, a 45-degree cone — and you need to feed it to the trig functions.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let r = Math.sin(Math.deg_to_rad(90.0))   # ~1.0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-dist.html b/math-dist.html new file mode 100644 index 00000000..71d09354 --- /dev/null +++ b/math-dist.html @@ -0,0 +1,32 @@ + + + + + +Math.dist — Ludic + + + + + + +
+
API Reference › Math › Math.dist
+
+ method +

Math.dist

+
+ Math.dist(x0, y0, x1, y1) -> fixed +

Returns the straight-line distance between (x0, y0) and (x1, y1) — sqrt(dx*dx + dy*dy) in fixed-point. Use it for proximity and range checks. When you only need to compare distances (which is nearer? within radius?), prefer Math.dist2, which skips the square root.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Math.dist(px, py, ex, ey) < attack_range { strike() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-dist2.html b/math-dist2.html new file mode 100644 index 00000000..f0a9e52e --- /dev/null +++ b/math-dist2.html @@ -0,0 +1,33 @@ + + + + + +Math.dist2 — Ludic + + + + + + +
+
API Reference › Math › Math.dist2
+
+ method +

Math.dist2

+
+ Math.dist2(x0, y0, x1, y1) -> fixed +

Returns the squared distance between (x0, y0) and (x1, y1) — dx*dx + dy*dy, with no square root. Comparing squared distances gives the same ordering as comparing real distances, so this is the cheaper choice for "nearest" and "within radius" tests: compare against radius * radius instead of calling Math.dist.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let r2 = radius * radius
+    if Math.dist2(px, py, ex, ey) < r2 { in_range() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-exp.html b/math-exp.html new file mode 100644 index 00000000..1e518980 --- /dev/null +++ b/math-exp.html @@ -0,0 +1,32 @@ + + + + + +Math.exp — Ludic + + + + + + +
+
API Reference › Math › Math.exp
+
+ method +

Math.exp

+
+ Math.exp(x) -> fixed +

Returns ex (the natural exponential), computed in Q16.16 as 2x·log₂e through a range-reduced integer polynomial — so it is bit-identical on every platform, the same guarantee the rest of the runtime gives. Pair it with Math.log for exponential decay and growth: cooldowns that ease off, difficulty ramps, or a value that relaxes toward a target. The result must land inside the fixed-point range (magnitudes below 32768), so keep x under about 10; larger exponents saturate.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let remaining = Math.exp(0.0 - decay * elapsed)   # smooth decay 1 -> 0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-floor.html b/math-floor.html new file mode 100644 index 00000000..b2ae5bb6 --- /dev/null +++ b/math-floor.html @@ -0,0 +1,36 @@ + + + + + +Math.floor — Ludic + + + + + + +
+
API Reference › Math › Math.floor
+
+ method +

Math.floor

+
+ Math.floor(x) -> int +

Takes a fixed value and returns the largest int not greater than it — rounding toward negative infinity, so Math.floor(2.7) is 2 and Math.floor(-0.2) is -1. This is the fixed-to-int conversion you reach for when turning a smooth position into a whole tile or pixel index. It is the same operation as the bare floor(x).

+

Parameters

xthe fixed value to round down
+

Example

program TileIndex {
+  const TILE_SIZE: fixed = 16.0
+
+  var world_x: fixed = 40.5
+
+  handler Locate phase Update {
+    let column = Math.floor(world_x / TILE_SIZE)   # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-hypot.html b/math-hypot.html new file mode 100644 index 00000000..4b95bf58 --- /dev/null +++ b/math-hypot.html @@ -0,0 +1,32 @@ + + + + + +Math.hypot — Ludic + + + + + + +
+
API Reference › Math › Math.hypot
+
+ method +

Math.hypot

+
+ Math.hypot(x, y) -> fixed +

Returns sqrt(x*x + y*y) — the length of the 2D vector (x, y), or equivalently the hypotenuse of a right triangle with those legs. It is the one-call form for a magnitude and reads more clearly than spelling out the squares and root. To normalize a vector, divide each component by its Math.hypot.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let length = Math.hypot(velocity_x, velocity_y)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-inverse_lerp.html b/math-inverse_lerp.html new file mode 100644 index 00000000..37393cb1 --- /dev/null +++ b/math-inverse_lerp.html @@ -0,0 +1,36 @@ + + + + + +Math.inverse_lerp — Ludic + + + + + + +
+
API Reference › Math › Math.inverse_lerp
+
+ method +

Math.inverse_lerp

+
+ Math.inverse_lerp(a, b, v) -> fixed +

The inverse of Math.lerp: given endpoints a and b and a value v, it returns the fraction (v - a) / (b - a) — 0.0 when v equals a, 1.0 when it equals b. Use it to turn a raw quantity into a normalized amount: how full a health bar is, how far a timer has run, or the t to feed into another Math.lerp. The endpoints must differ, since equal ones would divide by zero.

+

Parameters

athe endpoint that maps to 0.0
bthe endpoint that maps to 1.0
vthe value to locate between them
+

Example

program HealthFraction {
+  const MAX_HP: fixed = 200.0
+
+  var hp: fixed = 150.0
+
+  handler Draw phase Render {
+    let fraction = Math.inverse_lerp(0.0, MAX_HP, hp)   # 0.75
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-lerp.html b/math-lerp.html new file mode 100644 index 00000000..44a230f8 --- /dev/null +++ b/math-lerp.html @@ -0,0 +1,35 @@ + + + + + +Math.lerp — Ludic + + + + + + +
+
API Reference › Math › Math.lerp
+
+ method +

Math.lerp

+
+ Math.lerp(a, b, t) -> fixed +

Linear interpolation: returns a when t is 0.0, b when t is 1.0, and a proportional blend in between — the exact value a + (b - a) * t. All three arguments are fixed and the result is fixed. It is the workhorse behind smooth motion: ease a camera toward a target, fade a value over time, or sample a gradient. Values of t outside 0..1 extrapolate past the endpoints.

+

Parameters

athe value returned at t = 0.0
bthe value returned at t = 1.0
tthe blend amount, normally 0.0..1.0
+

Example

program EaseCamera {
+  var camera_x: fixed = 0.0
+  var target_x: fixed = 100.0
+
+  handler Follow phase Update {
+    camera_x = Math.lerp(camera_x, target_x, 0.1)   # glides a tenth of the way each frame
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-log.html b/math-log.html new file mode 100644 index 00000000..445d8a48 --- /dev/null +++ b/math-log.html @@ -0,0 +1,32 @@ + + + + + +Math.log — Ludic + + + + + + +
+
API Reference › Math › Math.log
+
+ method +

Math.log

+
+ Math.log(x) -> fixed +

Returns the natural logarithm ln(x) for x > 0, computed in Q16.16 from a deterministic integer log₂ (bit-scan for the exponent, a short series for the mantissa) scaled by ln 2 — bit-identical on every platform. It is the inverse of Math.exp: reach for it to turn multiplicative growth into a straight line — score-to-level curves, logarithmic difficulty, decibel-style volume. Non-positive inputs are out of domain and saturate to a large negative value rather than returning a meaningful number.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let level = Math.floor(Math.log(score))   # diminishing returns on score
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-max.html b/math-max.html new file mode 100644 index 00000000..46b9a9c8 --- /dev/null +++ b/math-max.html @@ -0,0 +1,34 @@ + + + + + +Math.max — Ludic + + + + + + +
+
API Reference › Math › Math.max
+
+ method +

Math.max

+
+ Math.max(a, b) -> int +

Returns whichever of a and b is larger. Like Math.min, it works for both int and fixed operands. Use it to hold a value at a floor — for example keeping a countdown at zero instead of going negative, or a health bar from dropping below the minimum. The bare form max(a, b) is kept as a convenience alias.

+

Parameters

athe first value
bthe second value
+

Example

program Countdown {
+  var timer: int = 60
+
+  handler Tick phase Update {
+    timer = Math.max(timer - 1, 0)   # stops at 0, never negative
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-min.html b/math-min.html new file mode 100644 index 00000000..ee15a5e4 --- /dev/null +++ b/math-min.html @@ -0,0 +1,34 @@ + + + + + +Math.min — Ludic + + + + + + +
+
API Reference › Math › Math.min
+
+ method +

Math.min

+
+ Math.min(a, b) -> int +

Returns whichever of a and b is smaller. It works on plain int values and on fixed values alike — both are stored as signed 32-bit words, so the comparison keeps their order either way. Use it to cap a value at a ceiling, keep a cursor inside a list, or take the nearer of two distances. The bare form min(a, b) is kept as a convenience alias.

+

Parameters

athe first value
bthe second value
+

Example

program ClampScore {
+  var score: int = 0
+
+  handler AddPoint phase Update {
+    score = Math.min(score + 1, 100)   # never climbs past 100
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-move_toward.html b/math-move_toward.html new file mode 100644 index 00000000..c6159265 --- /dev/null +++ b/math-move_toward.html @@ -0,0 +1,32 @@ + + + + + +Math.move_toward — Ludic + + + + + + +
+
API Reference › Math › Math.move_toward
+
+ method +

Math.move_toward

+
+ Math.move_toward(from, to, delta) -> fixed +

Moves from toward to by at most delta, never overshooting — when the gap is smaller than delta it returns exactly to. Unlike Math.lerp, which eases by a fraction of the remaining distance, this moves at a constant rate, which is what you want for turrets, meters, and UI values that should approach a target steadily and then stop.

+ +

Example

program Demo {
+  handler Step phase Update {
+    health_shown = Math.move_toward(health_shown, health, 2.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-ping_pong.html b/math-ping_pong.html new file mode 100644 index 00000000..5f9788cd --- /dev/null +++ b/math-ping_pong.html @@ -0,0 +1,32 @@ + + + + + +Math.ping_pong — Ludic + + + + + + +
+
API Reference › Math › Math.ping_pong
+
+ method +

Math.ping_pong

+
+ Math.ping_pong(t, len) -> int +

Maps an ever-increasing counter t to a value that rises from 0 to len and back, bouncing forever — the triangle wave to Math.wrap's sawtooth. Feed it a frame counter to drive a patrol, a pulsing highlight, or a back-and-forth sweep without tracking direction yourself.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let phase = Math.ping_pong(frame, 30)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-posmod.html b/math-posmod.html new file mode 100644 index 00000000..a4fe99d3 --- /dev/null +++ b/math-posmod.html @@ -0,0 +1,32 @@ + + + + + +Math.posmod — Ludic + + + + + + +
+
API Reference › Math › Math.posmod
+
+ method +

Math.posmod

+
+ Math.posmod(a, m) -> int +

Returns a modulo m, but always in the range 0..m-1 — unlike the % operator, which keeps the sign of a (so -1 % 5 is -1, while Math.posmod(-1, 5) is 4). It is what you want for wrapping indices and grid coordinates that can go negative.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let col = Math.posmod(x, grid_width)   # never negative
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-pow.html b/math-pow.html new file mode 100644 index 00000000..9ea4ad1a --- /dev/null +++ b/math-pow.html @@ -0,0 +1,32 @@ + + + + + +Math.pow — Ludic + + + + + + +
+
API Reference › Math › Math.pow
+
+ method +

Math.pow

+
+ Math.pow(base, exp) -> fixed +

Returns baseexp for base > 0 and any fixed exp, computed in Q16.16 as 2exp·log₂base — so non-integer exponents work (Math.pow(x, 0.5) is a square root, though Math.sqrt is cheaper and exact) and it stays bit-identical on every platform. Use it for gameplay curves that need a tunable exponent: damage falloff, easing shaped by a designer-set power, or compound-growth economies. Keep the result inside the fixed range (magnitudes below 32768); combinations that exceed it saturate.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let falloff = Math.pow(0.9, distance)   # each unit keeps 90% of the effect
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-rad_to_deg.html b/math-rad_to_deg.html new file mode 100644 index 00000000..f33f46e0 --- /dev/null +++ b/math-rad_to_deg.html @@ -0,0 +1,32 @@ + + + + + +Math.rad_to_deg — Ludic + + + + + + +
+
API Reference › Math › Math.rad_to_deg
+
+ method +

Math.rad_to_deg

+
+ Math.rad_to_deg(radians) -> fixed +

Converts an angle from radians to degrees (multiplying by 180/pi) — the inverse of Math.deg_to_rad. Use it to present an internally-radians angle in the degrees a player or designer expects, for a HUD readout or a debug overlay.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let heading_deg = Math.rad_to_deg(heading)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-remap.html b/math-remap.html new file mode 100644 index 00000000..409232d4 --- /dev/null +++ b/math-remap.html @@ -0,0 +1,37 @@ + + + + + +Math.remap — Ludic + + + + + + +
+
API Reference › Math › Math.remap
+
+ method +

Math.remap

+
+ Math.remap(v, in0, in1, out0, out1) -> fixed +

Maps v from the input range in0..in1 onto the output range out0..out1, keeping its relative position — equivalent to Math.lerp(out0, out1, Math.inverse_lerp(in0, in1, v)). All arguments are fixed. It is the one call that converts between units: a health value into a bar width, a noise sample in -1..1 into a 0..255 shade, or a mouse position into a world coordinate. Swap the output ends to invert the mapping.

+

Parameters

vthe value to rescale
in0the start of the input range
in1the end of the input range
out0the start of the output range
out1the end of the output range
+

Example

program HealthBar {
+  const MAX_HP: fixed = 200.0
+  const BAR_WIDTH: fixed = 64.0
+
+  var hp: fixed = 150.0
+
+  handler Draw phase Render {
+    let width = Math.remap(hp, 0.0, MAX_HP, 0.0, BAR_WIDTH)   # 48.0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-round.html b/math-round.html new file mode 100644 index 00000000..38b66b40 --- /dev/null +++ b/math-round.html @@ -0,0 +1,34 @@ + + + + + +Math.round — Ludic + + + + + + +
+
API Reference › Math › Math.round
+
+ method +

Math.round

+
+ Math.round(x) -> int +

Takes a fixed value and returns the nearest int, with halves rounding up: Math.round(2.5) is 3 and Math.round(2.4) is 2. Choose it over Math.floor when you want the closest whole value rather than always the lower one — snapping a smoothly-moving sprite to a pixel, or reporting a fractional total as a clean number.

+

Parameters

xthe fixed value to round to nearest
+

Example

program SnapToPixel {
+  var smooth_x: fixed = 10.5
+
+  handler Draw phase Render {
+    let px = Math.round(smooth_x)   # 11
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-sign.html b/math-sign.html new file mode 100644 index 00000000..e70990e1 --- /dev/null +++ b/math-sign.html @@ -0,0 +1,35 @@ + + + + + +Math.sign — Ludic + + + + + + +
+
API Reference › Math › Math.sign
+
+ method +

Math.sign

+
+ Math.sign(x) -> int +

Returns -1 when x is negative, 1 when it is positive, and 0 when it is exactly zero. It works on both int and fixed values, since zero and sign are the same in Q16.16. Multiply a step by Math.sign(dx) to move one unit toward a target, or read the sign of a velocity to decide which way a sprite faces.

+

Parameters

xthe value to test
+

Example

program FaceTarget {
+  var x: int = 8
+  var target: int = 20
+
+  handler StepToward phase Update {
+    x = x + Math.sign(target - x)   # +1 while target is to the right
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-sin.html b/math-sin.html new file mode 100644 index 00000000..4b4cd38b --- /dev/null +++ b/math-sin.html @@ -0,0 +1,32 @@ + + + + + +Math.sin — Ludic + + + + + + +
+
API Reference › Math › Math.sin
+
+ method +

Math.sin

+
+ Math.sin(radians) -> fixed +

Returns the sine of radians as a fixed in -1.0..1.0. It is table-driven (a 256-entry Q16.16 sine table with linear interpolation) so it is fast and fully deterministic. Angles wrap, so any value works — no need to reduce into a range first. Pair it with Math.cos to turn an angle into a direction.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let y = center_y + Math.sin(angle) * radius
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-smoothstep.html b/math-smoothstep.html new file mode 100644 index 00000000..62f2b5db --- /dev/null +++ b/math-smoothstep.html @@ -0,0 +1,32 @@ + + + + + +Math.smoothstep — Ludic + + + + + + +
+
API Reference › Math › Math.smoothstep
+
+ method +

Math.smoothstep

+
+ Math.smoothstep(e0, e1, x) -> fixed +

Returns 0.0 when x is at or below e0, 1.0 at or above e1, and a smooth S-curve (ease-in and ease-out) in between — the classic t*t*(3 - 2t) Hermite ramp. Use it for fades, dissolves, and easing a normalized amount before feeding it to Math.lerp, when a straight linear ramp looks too abrupt.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let a = Math.smoothstep(0.0, 1.0, fade_t)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-snapped.html b/math-snapped.html new file mode 100644 index 00000000..f318c74b --- /dev/null +++ b/math-snapped.html @@ -0,0 +1,32 @@ + + + + + +Math.snapped — Ludic + + + + + + +
+
API Reference › Math › Math.snapped
+
+ method +

Math.snapped

+
+ Math.snapped(v, step) -> fixed +

Rounds v to the nearest multiple of step (both fixed) — for snapping a position to a grid, quantizing an angle to 8 directions, or rounding a value to a tidy increment. Math.snapped(x, 16.0) snaps x to a 16-unit grid.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let gx = Math.snapped(world_x, 16.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-sqrt.html b/math-sqrt.html new file mode 100644 index 00000000..f73cb1c9 --- /dev/null +++ b/math-sqrt.html @@ -0,0 +1,32 @@ + + + + + +Math.sqrt — Ludic + + + + + + +
+
API Reference › Math › Math.sqrt
+
+ method +

Math.sqrt

+
+ Math.sqrt(x) -> fixed +

Returns the non-negative square root of x, computed in Q16.16 by a deterministic integer algorithm — so it is bit-identical on every platform, the same guarantee the rest of the runtime gives. Negative inputs return 0. Use it for lengths and distances; when you have two legs of a right triangle reach for Math.hypot, and when you only need to compare magnitudes prefer the cheaper squared distance Math.dist2.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let speed = Math.sqrt(vx * vx + vy * vy)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-tan.html b/math-tan.html new file mode 100644 index 00000000..cb67a142 --- /dev/null +++ b/math-tan.html @@ -0,0 +1,32 @@ + + + + + +Math.tan — Ludic + + + + + + +
+
API Reference › Math › Math.tan
+
+ method +

Math.tan

+
+ Math.tan(radians) -> fixed +

Returns the tangent of radians, computed as Math.sin(radians) / Math.cos(radians) in fixed-point. Near odd multiples of pi/2 the cosine approaches zero and the result grows without bound, so guard those angles. For most gameplay, Math.sin/Math.cos are what you want; tan is here for slopes and field-of-view math.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let slope = Math.tan(pitch)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/math-wrap.html b/math-wrap.html new file mode 100644 index 00000000..db965360 --- /dev/null +++ b/math-wrap.html @@ -0,0 +1,32 @@ + + + + + +Math.wrap — Ludic + + + + + + +
+
API Reference › Math › Math.wrap
+
+ method +

Math.wrap

+
+ Math.wrap(v, lo, hi) -> int +

Wraps v into the half-open range lo..hi, so values that run off one end reappear at the other — the toroidal counterpart to Math.clamp. Use it for wrap-around movement, cycling through a menu, or keeping a scrolling offset in bounds.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let x = Math.wrap(player_x, 0, world_width)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-bytes.html b/memory-bytes.html new file mode 100644 index 00000000..6b498dd0 --- /dev/null +++ b/memory-bytes.html @@ -0,0 +1,28 @@ + + + + + +Memory.bytes — Ludic + + + + + + +
+
API Reference › Memory › Memory.bytes
+
+ method +

Memory.bytes

+
+ Memory.bytes(n) -> pointer +

Allocates an n-byte buffer and returns a pointer to it.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-copy.html b/memory-copy.html new file mode 100644 index 00000000..8e681e29 --- /dev/null +++ b/memory-copy.html @@ -0,0 +1,28 @@ + + + + + +Memory.copy — Ludic + + + + + + +
+
API Reference › Memory › Memory.copy
+
+ method +

Memory.copy

+
+ Memory.copy(dst, src, n) -> void +

Copies n bytes from src to dst (regions must not overlap).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-fill.html b/memory-fill.html new file mode 100644 index 00000000..8ad8b31a --- /dev/null +++ b/memory-fill.html @@ -0,0 +1,28 @@ + + + + + +Memory.fill — Ludic + + + + + + +
+
API Reference › Memory › Memory.fill
+
+ method +

Memory.fill

+
+ Memory.fill(buf, value, n) -> void +

Sets the first n bytes of buf to the byte value.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-peek.html b/memory-peek.html new file mode 100644 index 00000000..035774dc --- /dev/null +++ b/memory-peek.html @@ -0,0 +1,28 @@ + + + + + +Memory.peek — Ludic + + + + + + +
+
API Reference › Memory › Memory.peek
+
+ method +

Memory.peek

+
+ Memory.peek(buf, i) -> int +

Returns the byte at buf[i] as an int in 0..255.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-poke.html b/memory-poke.html new file mode 100644 index 00000000..cf94a2d1 --- /dev/null +++ b/memory-poke.html @@ -0,0 +1,28 @@ + + + + + +Memory.poke — Ludic + + + + + + +
+
API Reference › Memory › Memory.poke
+
+ method +

Memory.poke

+
+ Memory.poke(buf, i, value) -> void +

Stores the low byte of value at buf[i].

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/memory-words.html b/memory-words.html new file mode 100644 index 00000000..68fb8e0f --- /dev/null +++ b/memory-words.html @@ -0,0 +1,28 @@ + + + + + +Memory.words — Ludic + + + + + + +
+
API Reference › Memory › Memory.words
+
+ method +

Memory.words

+
+ Memory.words(n) -> words +

Allocates a buffer of n 32-bit integer words, indexable as w[i].

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/mime-of.html b/mime-of.html new file mode 100644 index 00000000..fd88f39f --- /dev/null +++ b/mime-of.html @@ -0,0 +1,32 @@ + + + + + +Mime.of — Ludic + + + + + + +
+
API Reference › Mime › Mime.of
+
+ method +

Mime.of

+
+ Mime.of(path) -> string +

Returns the MIME type inferred from the file extension (e.g. hero.png → image/png), or application/octet-stream for an unknown extension. A pure lookup — it does not open the file.

+

Parameters

paththe path or filename
+

Example

program MimeOf {
+  entry {
+    if Mime.of("hero.png") == "image/png" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/mime-sniff.html b/mime-sniff.html new file mode 100644 index 00000000..81e6e45f --- /dev/null +++ b/mime-sniff.html @@ -0,0 +1,33 @@ + + + + + +Mime.sniff — Ludic + + + + + + +
+
API Reference › Mime › Mime.sniff
+
+ method +

Mime.sniff

+
+ Mime.sniff(path) -> string +

Reads the file's leading bytes and recognises well-known signatures (PNG, JPEG, GIF, PDF), so a mislabelled or extension-less file is still identified. Falls back to Mime.of when no signature matches or the file cannot be read.

+

Parameters

paththe path to inspect
+

Example

program MimeSniff {
+  entry {
+    let kind = Mime.sniff("download.bin")
+    print(len(kind))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/motion-to.html b/motion-to.html new file mode 100644 index 00000000..7ea51ad5 --- /dev/null +++ b/motion-to.html @@ -0,0 +1,37 @@ + + + + + +Motion.to — Ludic + + + + + + +
+
API Reference › Motion › Motion.to
+
+ method +

Motion.to

+
+ Motion.to(entity, from, to, dur, ease) +

Starts a value tween on an entity's Motion component: it sets from, to, dur and ease, resets the timer, and seeds value at from, so the engine interpolates value from from to to over dur ticks and latches done at the end. One call replaces setting five fields by hand — a health bar sliding, a door opening, an alpha fade. A no-op if the entity has no Motion. Integer and deterministic.

+

Parameters

entitythe entity carrying Motion
durthe duration in engine ticks
ease0 linear, 1 in, 2 out, 3 in-out
+

Example

program Demo {
+  property Motion { ticks: int = 0, dur: int = 0, from: int = 0, to: int = 0, ease: int = 0, value: int = 0, done: int = 0 }
+  model Door { Motion }
+  entry {
+    spawn Door { Motion { } }
+    let e = World.query_next(World.prop_id("Motion"), 0)
+    Motion.to(e, 0, 64, 30, 2)          # slide 0 -> 64 over 30 ticks, ease-out
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-apply.html b/network-apply.html new file mode 100644 index 00000000..1c3b4c57 --- /dev/null +++ b/network-apply.html @@ -0,0 +1,28 @@ + + + + + +Network.apply — Ludic + + + + + + +
+
API Reference › Network › Network.apply
+
+ method +

Network.apply

+
+ Network.apply(entity, buf) -> int +

Reads synced fields from buf into entity — the inverse of Network.serialize.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-is_owner.html b/network-is_owner.html new file mode 100644 index 00000000..0dca98b6 --- /dev/null +++ b/network-is_owner.html @@ -0,0 +1,28 @@ + + + + + +Network.is_owner — Ludic + + + + + + +
+
API Reference › Network › Network.is_owner
+
+ method +

Network.is_owner

+
+ Network.is_owner(entity) -> bool +

Returns true when this peer owns entity — owner(entity) == local_id().

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-is_server.html b/network-is_server.html new file mode 100644 index 00000000..887e4000 --- /dev/null +++ b/network-is_server.html @@ -0,0 +1,28 @@ + + + + + +Network.is_server — Ludic + + + + + + +
+
API Reference › Network › Network.is_server
+
+ method +

Network.is_server

+
+ Network.is_server() -> bool +

Returns true on the authoritative peer. Offline it is true, so server-only guards run in single-player.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-local_id.html b/network-local_id.html new file mode 100644 index 00000000..51e67267 --- /dev/null +++ b/network-local_id.html @@ -0,0 +1,28 @@ + + + + + +Network.local_id — Ludic + + + + + + +
+
API Reference › Network › Network.local_id
+
+ method +

Network.local_id

+
+ Network.local_id() -> int +

Returns the network id of the local peer.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-owner.html b/network-owner.html new file mode 100644 index 00000000..c302b4f0 --- /dev/null +++ b/network-owner.html @@ -0,0 +1,28 @@ + + + + + +Network.owner — Ludic + + + + + + +
+
API Reference › Network › Network.owner
+
+ method +

Network.owner

+
+ Network.owner(entity) -> int +

Returns the peer id that owns entity's @Owned state.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-poll.html b/network-poll.html new file mode 100644 index 00000000..2d2cf2b0 --- /dev/null +++ b/network-poll.html @@ -0,0 +1,28 @@ + + + + + +Network.poll — Ludic + + + + + + +
+
API Reference › Network › Network.poll
+
+ method +

Network.poll

+
+ Network.poll(buf, cap) -> int +

Reads up to cap bytes of the next inbound datagram into buf and returns the byte count, or zero when none is waiting.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-send.html b/network-send.html new file mode 100644 index 00000000..664ba906 --- /dev/null +++ b/network-send.html @@ -0,0 +1,28 @@ + + + + + +Network.send — Ludic + + + + + + +
+
API Reference › Network › Network.send
+
+ method +

Network.send

+
+ Network.send(peer, buf, len) -> void +

Sends the first len bytes of buf to peer. Absent a real socket it uses the built-in loopback, so offline code still runs.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-serialize.html b/network-serialize.html new file mode 100644 index 00000000..4f09243d --- /dev/null +++ b/network-serialize.html @@ -0,0 +1,28 @@ + + + + + +Network.serialize — Ludic + + + + + + +
+
API Reference › Network › Network.serialize
+
+ method +

Network.serialize

+
+ Network.serialize(entity, buf) -> int +

Writes the @Sync fields of entity into buf and returns the byte count, for sending over the wire.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/network-set_owner.html b/network-set_owner.html new file mode 100644 index 00000000..ccc8239b --- /dev/null +++ b/network-set_owner.html @@ -0,0 +1,28 @@ + + + + + +Network.set_owner — Ludic + + + + + + +
+
API Reference › Network › Network.set_owner
+
+ method +

Network.set_owner

+
+ Network.set_owner(entity, peer) -> void +

Sets the owning peer of entity to peer.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-cellular2.html b/noise-cellular2.html new file mode 100644 index 00000000..24c972a9 --- /dev/null +++ b/noise-cellular2.html @@ -0,0 +1,34 @@ + + + + + +Noise.cellular2 — Ludic + + + + + + +
+
API Reference › Noise › Noise.cellular2
+
+ method +

Noise.cellular2

+
+ Noise.cellular2(x, y, seed) -> fixed +

Samples **cellular (Worley) noise** at (x, y) and returns the F1 distance — the distance to the nearest feature point — as a fixed (roughly [0, 1.5]). Each lattice cell holds one feature point placed by its hash; scanning the 3×3 neighbourhood finds the closest one. The distance field forms Voronoi cells, which are exactly the structure you want for stone and cracked textures, biome or region boundaries, and scattered-feature layouts. Small distances mark cell centres; ridges appear where two cells meet.

Pair it with Noise.cellular2_id to also know *which* cell you are in. Deterministic in fixed point across platforms and runs.

+

Parameters

seedthe field selector
+

Example

program Cellular {
+  entry {
+    let seed = 7
+    let d = Noise.cellular2(fixed(5) / fixed(4), fixed(3) / fixed(4), seed)
+    print(Math.floor(d * fixed(1000)))   # distance to nearest cell point
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-cellular2_id.html b/noise-cellular2_id.html new file mode 100644 index 00000000..bfc41626 --- /dev/null +++ b/noise-cellular2_id.html @@ -0,0 +1,35 @@ + + + + + +Noise.cellular2_id — Ludic + + + + + + +
+
API Reference › Noise › Noise.cellular2_id
+
+ method +

Noise.cellular2_id

+
+ Noise.cellular2_id(x, y, seed) -> int +

Returns the integer **id of the nearest cell** in the same Worley diagram that Noise.cellular2 measures distance in — a stable hash that is identical for every sample point inside a given cell. Use it to assign something discrete per region: the biome or material of a cell, the variant of a scattered prop, a per-region colour. Combine it with the F1 distance to shade toward cell edges.

The id is a hash, so treat it as an opaque label (mod it into your table of choices); it is deterministic across platforms and runs.

+

Parameters

seedthe field selector (must match the cellular2 call it pairs with)
+

Example

program CellId {
+  entry {
+    let seed = 7
+    let id = Noise.cellular2_id(fixed(5) / fixed(4), fixed(3) / fixed(4), seed)
+    let biome = Math.posmod(id, 4)   # one of 4 biomes for this cell
+    print(biome)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-fbm2.html b/noise-fbm2.html new file mode 100644 index 00000000..84742769 --- /dev/null +++ b/noise-fbm2.html @@ -0,0 +1,34 @@ + + + + + +Noise.fbm2 — Ludic + + + + + + +
+
API Reference › Noise › Noise.fbm2
+
+ method +

Noise.fbm2

+
+ Noise.fbm2(x, y, seed, octaves) -> fixed +

Samples **fractal Brownian motion** at (x, y): it stacks octaves layers of simplex2, each at double the frequency and half the amplitude of the last, and normalises by the total amplitude so the result stays a fixed in [-1, 1]. Layering this way adds fine detail on top of broad shapes — the standard recipe for natural-looking terrain, clouds, and marble. More octaves means more detail (and more cost); 4–6 is typical.

Each octave uses a different derived seed, and the whole thing is deterministic in fixed point across platforms and runs.

+

Parameters

seedthe base field selector (each octave derives from it)
octaveshow many layers to sum (higher = more detail)
+

Example

program Fbm {
+  entry {
+    let seed = 1337
+    let h = Noise.fbm2(fixed(2) / fixed(7), fixed(9) / fixed(7), seed, 5)
+    print(Math.floor(Noise.unit(h) * fixed(100)))   # detailed 0..100 height
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-perlin2.html b/noise-perlin2.html new file mode 100644 index 00000000..df793445 --- /dev/null +++ b/noise-perlin2.html @@ -0,0 +1,35 @@ + + + + + +Noise.perlin2 — Ludic + + + + + + +
+
API Reference › Noise › Noise.perlin2
+
+ method +

Noise.perlin2

+
+ Noise.perlin2(x, y, seed) -> fixed +

Samples 2D **Perlin gradient noise** at (x, y) for the given seed and returns a fixed in [-1, 1]. Instead of a random value per lattice point, Perlin places a random gradient at each corner and interpolates their dot products with the offset vectors — which gives smoother, more natural gradients than value noise, the familiar look of classic terrain and cloud fields. By construction the value is exactly 0 at every integer lattice point.

Deterministic in fixed point across platforms and runs. Scale the coordinates to set feature size, and stack octaves with Noise.fbm2 for richer detail.

+

Parameters

seedthe field selector
+

Example

program Perlin {
+  entry {
+    let seed = 1337
+    if Noise.perlin2(fixed(0), fixed(0), seed) == 0 { print(1) }   # zero at the lattice
+    let n = Noise.perlin2(fixed(10) / fixed(3), fixed(7) / fixed(3), seed)
+    print(Math.floor(n * fixed(1000)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-simplex2.html b/noise-simplex2.html new file mode 100644 index 00000000..4d6b1373 --- /dev/null +++ b/noise-simplex2.html @@ -0,0 +1,34 @@ + + + + + +Noise.simplex2 — Ludic + + + + + + +
+
API Reference › Noise › Noise.simplex2
+
+ method +

Noise.simplex2

+
+ Noise.simplex2(x, y, seed) -> fixed +

Samples 2D **simplex noise** at (x, y) for the given seed and returns a fixed in [-1, 1]. Simplex noise sums contributions from the three corners of a skewed triangular cell, which gives it fewer of the axis-aligned directional artifacts that gradient noise can show — making it the recommended default for organic fields like terrain height and biome masks.

Deterministic in fixed point across platforms and runs, and the generator that Noise.fbm2 layers by default. Scale the coordinates to set feature size.

+

Parameters

seedthe field selector
+

Example

program Simplex {
+  entry {
+    let seed = 1337
+    let n = Noise.simplex2(fixed(4) / fixed(9), fixed(6) / fixed(9), seed)
+    print(Math.floor(Noise.unit(n) * fixed(100)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-unit.html b/noise-unit.html new file mode 100644 index 00000000..30481d64 --- /dev/null +++ b/noise-unit.html @@ -0,0 +1,34 @@ + + + + + +Noise.unit — Ludic + + + + + + +
+
API Reference › Noise › Noise.unit
+
+ method +

Noise.unit

+
+ Noise.unit(n) -> fixed +

Remaps a noise sample from [-1, 1] to [0, 1] — literally n / 2 + 0.5 — returning a fixed. The signed range is the natural output of the samplers, but heights, densities, probabilities, and colour ramps usually want the unsigned [0, 1] range; unit is the one-step conversion. It is a plain affine remap, so unit(-1) == 0, unit(0) == 0.5, and unit(1) == 1.

+

Parameters

na sample in [-1, 1] (e.g. from perlin2/simplex2/fbm2)
+

Example

program Unit {
+  entry {
+    let n = Noise.simplex2(fixed(1) / fixed(2), fixed(1) / fixed(2), 1337)
+    let height = Math.floor(Noise.unit(n) * fixed(64))   # 0..64 tiles
+    print(height)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/noise-value2.html b/noise-value2.html new file mode 100644 index 00000000..459b204f --- /dev/null +++ b/noise-value2.html @@ -0,0 +1,34 @@ + + + + + +Noise.value2 — Ludic + + + + + + +
+
API Reference › Noise › Noise.value2
+
+ method +

Noise.value2

+
+ Noise.value2(x, y, seed) -> fixed +

Samples 2D **value noise** at (x, y) for the given seed and returns a fixed in [-1, 1]. Value noise assigns a pseudo-random value to each integer lattice point and smoothly interpolates between them with a quintic fade — the cheapest generator here, with a slightly blocky, retro character that suits low-detail height fields, dithering, and per-cell variation.

Because it is a pure function of (x, y, seed) in fixed point, the same arguments always produce the same value on every platform. Sample at a fractional frequency (scale the coordinates) to change feature size.

+

Parameters

seedthe field selector; different seeds give independent worlds
+

Example

program Value {
+  entry {
+    let seed = 1337
+    let n = Noise.value2(fixed(3) / fixed(8), fixed(5) / fixed(8), seed)
+    print(Math.floor(Noise.unit(n) * fixed(100)))   # a 0..100 height
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-angle.html b/ns-angle.html new file mode 100644 index 00000000..b8944be4 --- /dev/null +++ b/ns-angle.html @@ -0,0 +1,22 @@ + + + + + +Angle — Ludic + + + + + + +
+
API Reference › Angle
+
namespace

Angle

+

An auto-wrapping angle in radians, so you never juggle % TAU by hand. Every Angle.* result is normalized into [-pi, pi), which makes diff the shortest signed rotation between two headings and lerp turn the short way around. Build from degrees with Angle.from_degrees (read back with to_degrees), take sin / cos, combine with add, and normalize any raw value with wrap. It builds on the deterministic fixed-point Math.* trig, so results are bit-identical on every platform. Arguments are positional; angles are fixed radians. Spliced in only when a program mentions Angle.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-anim.html b/ns-anim.html new file mode 100644 index 00000000..d51a39eb --- /dev/null +++ b/ns-anim.html @@ -0,0 +1,22 @@ + + + + + +Anim — Ludic + + + + + + +
+
API Reference › Anim
+
namespace

Anim

+

Spritesheet frame animation off the fixed frame clock. Store an elapsed timer (seconds, a fixed) on a component and each frame ask Anim.frame / Anim.once / Anim.pingpong which cell to draw; Anim.cell_x/Anim.cell_y turn a frame index into a source rectangle on the sheet. Everything is integer/fixed and deterministic — the same timer reproduces the same frame every run, so replays and lockstep netcode match exactly. For the ECS engine-owned SpriteAnim component there is also an ergonomic layer: register named clips with Anim.clip and play them by name with Anim.play, and arm frame events with Anim.on_frame / Anim.fired.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-app.html b/ns-app.html new file mode 100644 index 00000000..0aea60ee --- /dev/null +++ b/ns-app.html @@ -0,0 +1,22 @@ + + + + + +App — Ludic + + + + + + +
+ +
namespace

App

+

The running application, as distinct from its window. Today that is the boot splash: the runtime raises it before main from the game's asset pack, and App.splash_hide takes it down when the game has something to show instead. App.window_hide and App.window_show take the game's own window off the screen and bring it back without closing it — a launcher stepping aside while the game it started runs.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-assets.html b/ns-assets.html new file mode 100644 index 00000000..dbead1ea --- /dev/null +++ b/ns-assets.html @@ -0,0 +1,22 @@ + + + + + +Assets — Ludic + + + + + + +
+
API Reference › Assets
+
namespace

Assets

+

Loading and lookup for whole-file assets, paired with the Sprite.* atlas API. Assets.image (and its alias Assets.load) loads a whole image file as one sprite; Assets.get looks a named sprite up.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-audio.html b/ns-audio.html new file mode 100644 index 00000000..a1c46130 --- /dev/null +++ b/ns-audio.html @@ -0,0 +1,22 @@ + + + + + +Audio — Ludic + + + + + + +
+
API Reference › Audio
+
namespace

Audio

+

Sound effects and music. A sound is loaded once with Audio.load into a handle; Audio.play fires it one-shot and Audio.play_music loops it on a single music channel. Playback is out-of-band — the audio device is real-time, not part of the deterministic simulation — but every trigger here is an ordinary frame-driven call, so a recorded run replays the same sounds at the same frames. Headless builds carry the whole API as no-ops (no audio device needed), so the same game code runs under the test harness.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-base64.html b/ns-base64.html new file mode 100644 index 00000000..2cbd1df1 --- /dev/null +++ b/ns-base64.html @@ -0,0 +1,22 @@ + + + + + +Base64 — Ludic + + + + + + +
+
API Reference › Base64
+
namespace

Base64

+

Standard base64 (RFC 4648) — Base64.decode reads base64 text into the bytes it stands for, and Base64.encode is the inverse. The decoder ignores ASCII whitespace, so it reads the newline-wrapped base64 that Tiled writes inside a <data> element; that output then feeds the DEFLATE inflater for zlib/gzip-compressed layer data. Deterministic; spliced on demand when a program mentions Base64.*. (The security-sensitive encoder Crypto.base64 is a separate, hardened path.)

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-bigint.html b/ns-bigint.html new file mode 100644 index 00000000..ef2f4577 --- /dev/null +++ b/ns-bigint.html @@ -0,0 +1,22 @@ + + + + + +BigInt — Ludic + + + + + + +
+
API Reference › BigInt
+
namespace

BigInt

+

Arbitrary-precision integers with no upper bound, for idle/incremental counters, exact huge currencies, and score arithmetic that a 32- or 64-bit int would overflow. A BigInt is a sign-magnitude number stored as base-1e9 limbs, so every operation is exact and therefore deterministic — bit-identical on every platform, with no binary floating point. Build one with BigInt.from (an int) or BigInt.parse (decimal text), combine with add / sub / mul / pow / div / mod, compare with cmp / eq / is_zero, and render with str. Arguments are positional. The runtime is spliced in only when a program mentions BigInt.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-camera.html b/ns-camera.html new file mode 100644 index 00000000..4f3e4e56 --- /dev/null +++ b/ns-camera.html @@ -0,0 +1,22 @@ + + + + + +Camera — Ludic + + + + + + +
+
API Reference › Camera
+
namespace

Camera

+

A world-space camera — a draw offset threaded through the render path (the same offset Screen.camera sets). Camera.set places it, Camera.follow eases it toward a target, and Camera.shake jitters it from the seeded RNG for impact and explosions. Everything is integer and deterministic — driven off the same seed and inputs, a replay reproduces the exact camera path, shake included. The camera moves everything drawn; reset it to (0, 0) to draw a fixed HUD.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-clock.html b/ns-clock.html new file mode 100644 index 00000000..5267414c --- /dev/null +++ b/ns-clock.html @@ -0,0 +1,22 @@ + + + + + +Clock — Ludic + + + + + + +
+
API Reference › Clock
+
namespace

Clock

+

A game-controlled simulated clock: a single seconds counter the game owns. Unlike Time.now / Time.since, it never reads the wall clock, so any gameplay that reads Clock.now() is deterministic and replay-safe. Set it outright, or advance it by a Duration each tick to run time at whatever rate the simulation wants. The clock is a plain instant (seconds since 1970), so all the DateTime.* readers work on it directly.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-collision.html b/ns-collision.html new file mode 100644 index 00000000..3c2f0e59 --- /dev/null +++ b/ns-collision.html @@ -0,0 +1,30 @@ + + + + + +Collision — Ludic + + + + + + +
+
API Reference › Collision
+
namespace

Collision

+

2D overlap tests on integer coordinates (pixels or tiles). Rectangles are (x, y, w, h) from the top-left; circles are (x, y, r). Each returns a bool. Squared distances are computed in 64-bit so large coordinates never overflow. + +These are static boolean tests. For a *moving* body with real collision resolution, the engine ships a physics-lite movement system built on the same integer math — declare the well-known components and the engine advances them for you each frame, no handler wired: + +- property Body { vx, vy, gravity, max_fall, rx, ry, policy, on_ground, hit_wall, hit_ceiling } — velocity/accel state in Q16.16 fixed-point (vx/vy/gravity/max_fall), engine-owned sub-pixel accumulators (rx/ry), an integration policy (0 platformer with gravity, 1 top-down), and the derived contact flags the engine sets each frame. +- property Collider { w, h, offx, offy, is_trigger, one_way, layer, mask, hit, entered, exited } — an AABB shape offset from the entity's Position { x, y }, with layer/mask filtering, one-way-platform and trigger flags, and per-frame trigger outputs. +- property Solids { tile, wall, oneway } — an optional single config entity that turns on the tile-grid broadphase over the Map tilemap (tile px size, the solid wall glyph, and an optional one-way oneway glyph). + +The engine-owned esys_move system (Update phase) integrates velocity and gravity into a tentative move, then resolves it with a per-axis **swept AABB** — against both solid Collider entities and the tile grid — so a fast body never tunnels through a wall. It handles one-way platforms (which block only a downward landing), reports trigger/sensor overlaps without resolving them, and sets on_ground / hit_wall / hit_ceiling for a controller to read. Everything is integer and deterministic, so movement reproduces exactly under replay, lockstep and world_save snapshots. Contact is surfaced as polled flags (the SpriteAnim.event_fired shape) rather than engine-emitted events, so a game raises its own CollisionResolved / TriggerEntered events from its handler with no coupling. This is the shared foundation the built-in gameplay controllers build on. Related: Grid, Map, Motion, @EngineSystem.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-color.html b/ns-color.html new file mode 100644 index 00000000..14a424f6 --- /dev/null +++ b/ns-color.html @@ -0,0 +1,22 @@ + + + + + +Color — Ludic + + + + + + +
+
API Reference › Color
+
namespace

Color

+

The named color palette. Every Color.Name lowers to a plain 0xRRGGBB integer at compile time — no runtime cost. 221 names are built in.

+

Whites

Color.White#FFFFFF
Color.Snow#FFFAFA
Color.Ivory#FFFFF0
Color.EggShellWhite#F0EAD6
Color.FloralWhite#FFFAF0
Color.SeaShell#FFF5EE
Color.Linen#FAF0E6
Color.AntiqueWhite#FAEBD7
Color.OldLace#FDF5E6
Color.Beige#F5F5DC
Color.Cream#FFFDD0
Color.Honeydew#F0FFF0
Color.MintCream#F5FFFA
Color.Azure#F0FFFF
Color.AliceBlue#F0F8FF
Color.GhostWhite#F8F8FF
Color.WhiteSmoke#F5F5F5
Color.Lavender#E6E6FA
Color.Bone#E3DAC9
Color.Parchment#F1E9D2

Grays

Color.Gainsboro#DCDCDC
Color.LightGray#D3D3D3
Color.Silver#C0C0C0
Color.Ash#B2BEB5
Color.DarkGray#A9A9A9
Color.Gray#808080
Color.DimGray#696969
Color.Nickel#727472
Color.Slate#708090
Color.SlateGray#708090
Color.LightSlateGray#778899
Color.Gunmetal#2A3439
Color.Charcoal#36454F
Color.Graphite#1C1C1C
Color.Onyx#353839
Color.Jet#343434
Color.Black#000000
Color.EerieBlack#1B1B1B
Color.RaisinBlack#242124
Color.Ebony#555D50

Reds

Color.Red#FF0000
Color.Crimson#DC143C
Color.Scarlet#FF2400
Color.Vermilion#E34234
Color.FireBrick#B22222
Color.Cinnabar#E44D2E
Color.DarkRed#8B0000
Color.Maroon#800000
Color.Ruby#E0115F
Color.Cardinal#C41E3A
Color.IndianRed#CD5C5C
Color.Rust#B7410E
Color.Sangria#92000A
Color.Redwood#A45A52
Color.Cerise#DE3163
Color.Amaranth#E52B50
Color.Carmine#960018
Color.Chestnut#954535
Color.Brick#CB4154
Color.TerraCotta#E2725B

Pinks

Color.Pink#FFC0CB
Color.LightPink#FFB6C1
Color.HotPink#FF69B4
Color.DeepPink#FF1493
Color.PaleVioletRed#DB7093
Color.Rose#FF007F
Color.Blush#DE5D83
Color.Salmon#FA8072
Color.LightSalmon#FFA07A
Color.DarkSalmon#E9967A
Color.Coral#FF7F50
Color.Watermelon#FC6C85
Color.Flamingo#FC8EAC
Color.Bubblegum#FFC1CC
Color.Fuchsia#FF00FF
Color.Magenta#FF00FF
Color.Mauve#E0B0FF
Color.Puce#CC8899
Color.Thistle#D8BFD8
Color.Orchid#DA70D6

Oranges

Color.Orange#FFA500
Color.DarkOrange#FF8C00
Color.Tangerine#F28500
Color.Pumpkin#FF7518
Color.Apricot#FBCEB1
Color.Peach#FFE5B4
Color.Cantaloupe#FFA62B
Color.Amber#FFBF00
Color.Bronze#CD7F32
Color.Copper#B87333
Color.Marigold#EAA221
Color.Carrot#ED9121
Color.Persimmon#EC5800
Color.Papaya#FF9E2C
Color.Sunset#FAD6A5

Yellows

Color.Yellow#FFFF00
Color.LightYellow#FFFFE0
Color.Gold#FFD700
Color.Goldenrod#DAA520
Color.Lemon#FFF700
Color.Canary#FFEF00
Color.Mustard#FFDB58
Color.Flax#EEDC82
Color.Wheat#F5DEB3
Color.Corn#FBEC5D
Color.Dandelion#F0E130
Color.Saffron#F4C430
Color.Khaki#F0E68C
Color.DarkKhaki#BDB76B
Color.Straw#E4D96F

Browns

Color.Brown#8B4513
Color.SaddleBrown#8B4513
Color.Sienna#A0522D
Color.Chocolate#D2691E
Color.Peru#CD853F
Color.Tan#D2B48C
Color.BurlyWood#DEB887
Color.Sand#C2B280
Color.Coffee#6F4E37
Color.Espresso#4B3621
Color.Mahogany#C04000
Color.Walnut#773F1A
Color.Umber#635147
Color.Sepia#704214
Color.Taupe#483C32
Color.Fawn#E5AA70
Color.Caramel#C68E17
Color.Cocoa#D2691E
Color.Hazel#8E7618
Color.Wenge#645452

Greens

Color.Green#008000
Color.Lime#00FF00
Color.LimeGreen#32CD32
Color.LawnGreen#7CFC00
Color.Chartreuse#7FFF00
Color.GreenYellow#ADFF2F
Color.SpringGreen#00FF7F
Color.MintGreen#98FF98
Color.SeaGreen#2E8B57
Color.MediumSeaGreen#3CB371
Color.ForestGreen#228B22
Color.DarkGreen#006400
Color.OliveDrab#6B8E23
Color.Olive#808000
Color.Moss#8A9A5B
Color.Fern#4F7942
Color.Emerald#50C878
Color.Jade#00A86B
Color.Malachite#0BDA51
Color.Shamrock#009E60
Color.Pistachio#93C572
Color.Avocado#568203
Color.Pine#01796F
Color.Sage#9CAF88
Color.Kelly#4CBB17
Color.Hunter#355E3B
Color.Basil#579229
Color.Clover#2E8B57
Color.Juniper#6D9A79
Color.Neon#39FF14

Cyans

Color.Cyan#00FFFF
Color.Aqua#00FFFF
Color.LightCyan#E0FFFF
Color.PaleTurquoise#AFEEEE
Color.Aquamarine#7FFFD4
Color.Turquoise#40E0D0
Color.MediumTurquoise#48D1CC
Color.DarkTurquoise#00CED1
Color.Teal#008080
Color.DarkCyan#008B8B
Color.CadetBlue#5F9EA0
Color.Lagoon#018E8E
Color.Seafoam#93E9BE
Color.Cerulean#007BA7
Color.SkyBlueLight#80DAEB
Color.Robin#00CCCC
Color.Verdigris#43B3AE
Color.Celadon#ACE1AF

Blues

Color.Blue#0000FF
Color.LightBlue#ADD8E6
Color.PowderBlue#B0E0E6
Color.SkyBlue#87CEEB
Color.LightSkyBlue#87CEFA
Color.DeepSkyBlue#00BFFF
Color.DodgerBlue#1E90FF
Color.CornflowerBlue#6495ED
Color.SteelBlue#4682B4
Color.RoyalBlue#4169E1
Color.MediumBlue#0000CD
Color.DarkBlue#00008B
Color.MidnightBlue#191970
Color.Cobalt#0047AB
Color.Sapphire#0F52BA
Color.Denim#1560BD
Color.Indigo#4B0082
Color.Prussian#003153
Color.Ultramarine#3F00FF
Color.Periwinkle#CCCCFF
Color.Iris#5A4FCF
Color.Glaucous#6082B6
Color.Zaffre#0014A8
Color.Berry#2E2D88

Purples

Color.Purple#800080
Color.Violet#EE82EE
Color.DarkViolet#9400D3
Color.BlueViolet#8A2BE2
Color.MediumPurple#9370DB
Color.Amethyst#9966CC
Color.Plum#8E4585
Color.Eggplant#614051
Color.Grape#6F2DA8
Color.Wine#722F37
Color.Mulberry#C54B8C
Color.Lilac#C8A2C8
Color.Wisteria#C9A0DC
Color.Heliotrope#DF73FF
Color.Byzantium#702963
Color.Tyrian#66023C
Color.RebeccaPurple#663399
Color.Orchid2#AF69EF
+ ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-crypto.html b/ns-crypto.html new file mode 100644 index 00000000..70c4296b --- /dev/null +++ b/ns-crypto.html @@ -0,0 +1,28 @@ + + + + + +Crypto — Ludic + + + + + + +
+
API Reference › Crypto
+
namespace

Crypto

+

Secure, test-vector-backed hashing for the handful of security-sensitive things a game actually does: signing a save or leaderboard payload so casual tampering is detectable, and verifying that a network message or token was not forged by someone who does not hold the key. This is the deliberate counterpart to the fast Hash library — same idea, opposite trade-off. Hash is fast and reversible and must never guard anything; Crypto is SHA-256 and HMAC-SHA256 implemented to the standard, so the algorithms are the ones with published known-answer tests rather than anything home-grown. + +Digests are returned as lowercase hex strings, not raw bytes — a str is null-terminated and a raw digest can contain a zero byte, so hex is the form you can print, store, and compare directly. + +Alongside hashing, this library exposes the OS cryptographically-secure random generator — random_bytes, random_hex, and random_u32 — for tokens, nonces, and Uuid generation, plus base64 for moving bytes through text-only channels. The secure-random helpers are deliberately non-deterministic and must never seed the lockstep simulation RNG (Random). + +What this is not: it is not DRM and it is not unbeatable anti-cheat. A client-side game cannot keep a secret from the machine it runs on — a determined owner can always read the key out of the binary. Use it to make *casual* tampering detectable and to authenticate messages between parties who share a key. To verify a MAC always use Crypto.verify_hmac (a constant-time check), never ==, which leaks how much of a guessed MAC was correct.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-date.html b/ns-date.html new file mode 100644 index 00000000..c2ad17c6 --- /dev/null +++ b/ns-date.html @@ -0,0 +1,22 @@ + + + + + +Date — Ludic + + + + + + +
+
API Reference › Date
+
namespace

Date

+

Calendar days on the proleptic Gregorian calendar (UTC). A Date is stored as a plain int: the count of days since 1970-01-01, so shifting a date by whole days is ordinary integer arithmetic and two dates subtract to a day count. Build one with Date.new, read its parts with year/month/day/weekday. Every operation is deterministic integer math — no leap seconds, no timezone, no floating point.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-datetime.html b/ns-datetime.html new file mode 100644 index 00000000..5e45ccbe --- /dev/null +++ b/ns-datetime.html @@ -0,0 +1,22 @@ + + + + + +DateTime — Ludic + + + + + + +
+
API Reference › DateTime
+
namespace

DateTime

+

Instants on the wall-clock timeline (UTC), stored as a plain int: the count of seconds since 1970-01-01, matching Time.now. Build one with DateTime.from, shift it by a Duration with DateTime.add, and pull it apart with the year/month/day/hour/minute/second readers. Deterministic integer math throughout; v1 is UTC-only with no leap seconds, and assumes instants at or after 1970.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-decimal.html b/ns-decimal.html new file mode 100644 index 00000000..57f362dd --- /dev/null +++ b/ns-decimal.html @@ -0,0 +1,22 @@ + + + + + +Decimal — Ludic + + + + + + +
+
API Reference › Decimal
+
namespace

Decimal

+

Exact base-10 fixed-point numbers for game economies — prices, balances and taxes on values like 0.10 that binary floating point cannot represent, so they always add up exactly. A Decimal is a BigInt mantissa with a decimal scale (the number of digits after the point), giving unbounded range and exact add / sub / mul. Build one with Decimal.from (an int) or Decimal.parse (text like "19.99"), compare with cmp / eq, change precision with rescale (truncates toward zero), read the current precision with scale, and render with str. Every operation is exact and deterministic. The runtime is spliced in only when a program mentions Decimal.* (or BigInt.*).

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-dict.html b/ns-dict.html new file mode 100644 index 00000000..d881c9b3 --- /dev/null +++ b/ns-dict.html @@ -0,0 +1,22 @@ + + + + + +Dict — Ludic + + + + + + +
+
API Reference › Dict
+
namespace

Dict

+

A hash map from string keys to int values — the everyday lookup a game needs, like resource counts or an id registry by name. A Dict is backed by an open-addressing hash table (FNV-1a, linear probing, tombstone deletes, growing at load factor 0.7), so get/set/has are O(1) on average rather than the linear scan a list would give. Create one with Dict.new, then set / get / get_or / has / remove / size / clear / keys. Values are int (which also holds an entity or any small id); for richer values use the Value.* tree. Arguments are positional. The runtime is spliced in only when a program mentions Dict.* (or Set.*).

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-duration.html b/ns-duration.html new file mode 100644 index 00000000..cb1bf643 --- /dev/null +++ b/ns-duration.html @@ -0,0 +1,22 @@ + + + + + +Duration — Ludic + + + + + + +
+
API Reference › Duration
+
namespace

Duration

+

Spans of real time, measured in whole seconds and carried in a plain int — so a duration adds, subtracts and compares with the ordinary operators (Duration.minutes(5) + Duration.seconds(30), away > Duration.hours(3)). The constructors build a span from a unit; the as_* readers convert a span back to whole units (truncating toward zero). Integer-only, so no floating-point drift.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-ease.html b/ns-ease.html new file mode 100644 index 00000000..3aa78ca8 --- /dev/null +++ b/ns-ease.html @@ -0,0 +1,22 @@ + + + + + +Ease — Ludic + + + + + + +
+
API Reference › Ease
+
namespace

Ease

+

Tween curves — the "juice" layer. Each takes a normalized amount t in 0.0..1.0 and returns an eased fixed, ready to feed to Math.lerp. All deterministic fixed-point.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-file.html b/ns-file.html new file mode 100644 index 00000000..f422011e --- /dev/null +++ b/ns-file.html @@ -0,0 +1,22 @@ + + + + + +File — Ludic + + + + + + +
+
API Reference › File
+
namespace

File

+

Raw file access over the C stdio calls, for programs that read and write their own formats. File.open returns a handle (or null), File.read / File.write move bytes through a bytes buffer, File.seek / File.tell position the cursor, and File.close releases the handle. For text and structured data prefer the higher-level Fs, Json and Prefs APIs.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-font.html b/ns-font.html new file mode 100644 index 00000000..cae04381 --- /dev/null +++ b/ns-font.html @@ -0,0 +1,22 @@ + + + + + +Font — Ludic + + + + + + +
+
API Reference › Font
+
namespace

Font

+

TrueType fonts for the retained UI and for Screen.draw_text-style drawing. Font.load reads a .ttf / .ttc file and returns a handle that a ui block's font: property or a text call takes.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-fs.html b/ns-fs.html new file mode 100644 index 00000000..bc1c6819 --- /dev/null +++ b/ns-fs.html @@ -0,0 +1,26 @@ + + + + + +Fs — Ludic + + + + + + +
+ +
namespace

Fs

+

The filesystem, wrapped into one safe, ergonomic API — no file descriptors, no byte buffers, no half-written files. The foundation for saves, config, mods, and asset loading: read and write whole files as text, check existence, make directories, copy, and list a folder. The bare file_* builtins remain underneath for streaming; Fs.* is the everyday layer. + +Fallible calls surface failure as a value — null, false, or -1 — that you branch on, never a crash (a first-class try/else lands with the error-handling work). Writes are atomic (written to a temporary file and renamed into place), so a crash mid-write never corrupts the previous file. list returns entries sorted, so a directory walk is reproducible across runs and platforms. + +Paths are /-separated over the native (macOS/BSD) filesystem — the fully supported target today; a sandboxed virtual filesystem for wasm and recursive directory copy are follow-ups. Text helpers are UTF-8 (they pair with Unicode); use Path to build the paths and Os for per-user locations.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-fx.html b/ns-fx.html new file mode 100644 index 00000000..24a8453e --- /dev/null +++ b/ns-fx.html @@ -0,0 +1,22 @@ + + + + + +Fx — Ludic + + + + + + +
+ +
namespace

Fx

+

Engine-owned transient effects. A game asks for a burst of sparks or a floating number and the engine owns the rest: it moves and ages them every Update, draws them every Render after the sprites (through the camera, shake and clip), and drops them when they expire. Nothing is an entity, so a hit effect needs no component, model, handler or draw call. Velocities and lifetimes come from the seeded RNG, so a replay produces the same sparks.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-grid.html b/ns-grid.html new file mode 100644 index 00000000..f218af6f --- /dev/null +++ b/ns-grid.html @@ -0,0 +1,22 @@ + + + + + +Grid — Ludic + + + + + + +
+
API Reference › Grid
+
namespace

Grid

+

Tile geometry and pathfinding over the Map tilemap. A cell is passable unless it is out of bounds or holds the caller's wall tile (a char code, e.g. '#'), so any impassable glyph works. Everything is integer and deterministic — same map and query reproduce the same path every run. Grid.line/Grid.flood/Grid.a_star return Cell slices (index them with len / [i]; each cell has .x and .y).

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-hash.html b/ns-hash.html new file mode 100644 index 00000000..802f8633 --- /dev/null +++ b/ns-hash.html @@ -0,0 +1,24 @@ + + + + + +Hash — Ludic + + + + + + +
+
API Reference › Hash
+
namespace

Hash

+

Fast, non-cryptographic hashing for everyday game needs: turning string IDs into integer handles, mixing a few numbers into one deterministic seed, and checksumming data to catch corruption. Every function is plain 32-bit integer arithmetic with a defined byte order and fixed constants, so a given input hashes to exactly the same value on every platform and every run — the guarantee that makes it safe for procedural generation and lockstep networking. Results are 32-bit and print as signed integers. Arguments are positional. + +Not for security. These hashes are fast and reversible — never use them for passwords, tokens, or tamper-proofing. For secure hashing reach for the cryptography library instead.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-http.html b/ns-http.html new file mode 100644 index 00000000..f3f0cd30 --- /dev/null +++ b/ns-http.html @@ -0,0 +1,24 @@ + + + + + +Http — Ludic + + + + + + +
+
API Reference › Http
+
namespace

Http

+

A poll-based HTTP / HTTPS client for out-of-band data — leaderboards, cloud saves, remote config, telemetry, downloads. Open a request with Http.get / Http.post (or Http.open to add headers first), then each frame call Http.poll — it returns -1 while pending, 0 on error, or the status code — so the frame never blocks. Read the reply with Http.status / ok / text / header, and pair it with Json.* for (de)serialization. TLS is the system's, on by default. A large download goes to a file instead of memory with Http.save_to, and Http.received / expected drive a progress bar while it is pending. + +HTTP depends on the network and the wall clock, so — like Net.* and Time.now — it is out-of-band and must never feed the deterministic lockstep/replay simulation. The transport is NSURLConnection / NSURLSession on macOS and WinHTTP on Windows; the raw-response parser (Http.parse) is pure and portable.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-huge.html b/ns-huge.html new file mode 100644 index 00000000..6369d7ce --- /dev/null +++ b/ns-huge.html @@ -0,0 +1,22 @@ + + + + + +Huge — Ludic + + + + + + +
+
API Reference › Huge
+
namespace

Huge

+

Idle/incremental big numbers — magnitudes far past what a 32- or 64-bit integer can hold, for prestige currencies and exponential growth. A Huge is a normalized mantissa (a Q16.16 fixed in [1, 10)) times 10^exponent, so it can represent 10^100 or 10^1000 while staying one small value. It is a display-scale number (about four significant digits), not a lockstep-exact one — keep it out of the deterministic simulation and reach for BigInt / Decimal when exactness matters. Build one with Huge.from, combine with add / sub / mul / neg, compare with cmp / sign, read mantissa / exp, and render as 1.23e45 with str. Arguments are positional. Spliced in only when a program mentions Huge.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-input.html b/ns-input.html new file mode 100644 index 00000000..d2525ae2 --- /dev/null +++ b/ns-input.html @@ -0,0 +1,24 @@ + + + + + +Input — Ludic + + + + + + +
+
API Reference › Input
+
namespace

Input

+

Reading input. At the base, Input.key gives this frame's key and Input.poll is the single per-frame read that also drives deterministic record/replay. Over that sit named actions — Input.bind / Input.down / Input.pressed / Input.rebind — so gameplay reads rebindable actions, not physical keys. + +The device layer adds everything past one key per frame: multiple simultaneous held keys (Input.key_down / key_pressed / key_released), analog Input.axis and normalized Input.vector, the mouse (position, delta, buttons, wheel), gamepads and touch. The held set is fed by the platform when windowed and by the Input.press / Input.set_* injection on every target — the same idea as Godot's action_press, and what a replay, an AI, or the network feeds. Everything is integer and deterministic, and record/replay snapshots the whole per-frame state.

+
Input.key() -> intThe key pressed this frame as a character code (0 when nothing is pressed).Input.bind(action: str, key: int) -> voidBind a physical key to a named action, so gameplay reads the action, not the key.Input.rebind(action: str, from: int, to: int) -> voidRemap an action from one key to another at runtime (rebinding menus).Input.poll() -> intAdvance one frame of input; the single per-frame read behind actions and replay.Input.down(action: str) -> boolIs a named action held on the frame last polled?Input.pressed(action: str) -> boolDid a named action go down this frame (a one-shot edge)?Input.record() -> voidStart recording polled input each frame (for deterministic replay).Input.replay() -> voidReplay recorded input; poll then reads the tape, not the device.Input.key_down(key) -> boolIs a physical key held this frame?Input.key_pressed(key) -> boolDid a key go down this frame (edge)?Input.key_released(key) -> boolDid a key go up this frame (edge)?Input.key_label(key) -> stringThe name to show a player for a key - in their own keyboard layout.Input.press(key)Inject a held key (AI, tutorial, testing, network).Input.release(key)Release an injected key.Input.axis(neg, pos) -> fixedA -1..+1 axis from two keys.Input.axis_i(neg, pos) -> intA -1/0/1 directional intent as a plain int, no bool->int glue.Input.vector(left, right, up, down) -> VectorA normalized 2D vector from four keys.Input.strength(action) -> fixed0..1 strength of a named action.Input.mouse_x() -> intThe mouse x position this frame.Input.mouse_y() -> intThe mouse y position this frame.Input.mouse_dx() -> intMouse x movement since the last poll.Input.mouse_dy() -> intMouse y movement since the last poll.Input.mouse_down(button) -> boolIs a mouse button held?Input.wheel() -> intScroll-wheel delta this frame.Input.set_mouse(x, y, buttons, wheel)Inject the mouse state (headless / AI / testing).Input.pad_connected(pad) -> boolIs a gamepad connected?Input.pad_button(pad, button) -> boolIs a gamepad button held?Input.pad_axis(pad, axis) -> fixedA gamepad analog axis, -1..+1.Input.set_pad(pad, connected, buttons, lx, ly, rx, ry)Inject a gamepad's whole state.Input.touch_count() -> intHow many touch points are active?Input.touch_x(index) -> intThe x of a touch point.Input.touch_y(index) -> intThe y of a touch point.Input.set_touch(index, x, y, active)Inject a touch point.Input.action(action: str, key: int) -> voidRegister a default key binding for an action (kept if already bound).Input.bind_pad(action: str, button: int) -> voidAlso fire a named action from a gamepad button (device-agnostic).Input.active(action: str) -> boolIs a named action active right now (any bound key held, or pad button down)?Input.just_pressed(action: str) -> boolDid a named action go active this frame (the deterministic on-press)?Input.just_released(action: str) -> boolDid a named action go inactive this frame (the on-release edge)?Input.cursor_mode(mode)Hide / lock / confine the OS mouse cursor to the window (windowed).Input.move_i() -> IVec2The standard top-down movement intent, -1/0/1 per axis.
+ ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-ivec2.html b/ns-ivec2.html new file mode 100644 index 00000000..55342ccf --- /dev/null +++ b/ns-ivec2.html @@ -0,0 +1,22 @@ + + + + + +IVec2 — Ludic + + + + + + +
+
API Reference › IVec2
+
namespace

IVec2

+

Integer 2D vector math for tile and grid coordinates, cell offsets, and integer sizes. An IVec2 is a pair of whole-number int components (x, y) packed into one value, so it is copied by value and never allocates. Every operation is exact integer arithmetic — no rounding, and bit-identical on every platform. Use it wherever a fractional part would be meaningless; reach for Vector when you need sub-pixel precision. Arguments are positional.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-job.html b/ns-job.html new file mode 100644 index 00000000..6810f3b4 --- /dev/null +++ b/ns-job.html @@ -0,0 +1,22 @@ + + + + + +Job — Ludic + + + + + + +
+ +
namespace

Job

+

Background work that stays out of the frame. A Job is a future — a handle to a result that lands later. Kick one off with Job.run (a background compute that advances a little each Job.pump and finishes after enough frames, so heavy work never hitches) or Job.defer (a future you resolve yourself with Job.fulfill / Job.fail). Poll it with done / ok / failed / cancelled, read result / error, and always collect on the main thread — a Job must never touch the ECS world directly. The scheduler is deterministic and cooperative, so the same jobs and the same budget reproduce byte-for-byte, every run and every target. For work that should use every core now, Job.parallel_for(count, fn work, ctx) runs work(i, ctx) across a pool of real OS threads and returns when all of it is done; a worker computes on what it was handed and never changes the world. Arguments are positional. Spliced in only when a program mentions Job.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-json.html b/ns-json.html new file mode 100644 index 00000000..30104b52 --- /dev/null +++ b/ns-json.html @@ -0,0 +1,22 @@ + + + + + +Json — Ludic + + + + + + +
+
API Reference › Json
+
namespace

Json

+

The text bridge over the Value tree: Json.encode turns a value tree into compact, stable JSON text and Json.parse reads it back. Together with Reflect.serialize/Reflect.apply this is a one-call, bit-exact save/load for entities, and a diffable on-disk format for tooling. Determinism holds: the same tree always encodes to the same bytes.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-light.html b/ns-light.html new file mode 100644 index 00000000..b2bb295d --- /dev/null +++ b/ns-light.html @@ -0,0 +1,22 @@ + + + + + +Light — Ludic + + + + + + +
+
API Reference › Light
+
namespace

Light

+

A software 2D light-accumulation pass over the framebuffer, run in a render phase after drawing the scene and before Screen.show. Light.ambient multiplies the whole scene toward a tint — the night/cave modulate that darkens everything so lights add mood back on top. Light.point accumulates a radial glow that falls off with distance and clamps per channel, and Light.occlude registers rectangles that block a light's rays to cast hard shadows (cleared each frame with Light.clear_occluders). Lighting is a rendering concern only — it never touches game state or replays — and it is fully deterministic (integer and Q16.16 fixed), so the same scene lights identically every run and in a headless render, keeping screenshots diffable. Colours are 0x00RRGGBB. Beyond the radial core, the pass carries the render-quality tiers: Light.spot cones, a Light.falloff exponent, Light.soft shadows (penumbra), Light.gel colour cookies, normal-mapped surfaces (Light.normal + Light.height) that shade by facing, and a Light.time_of_day day/night ramp — every one deterministic. This is the imperative surface; the engine also consumes Light2D/Occluder components automatically (a Light2D may carry optional direction/spread/falloff/softness/gel fields). Related: Screen, Color, Camera.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-list.html b/ns-list.html new file mode 100644 index 00000000..c3ec513a --- /dev/null +++ b/ns-list.html @@ -0,0 +1,22 @@ + + + + + +List — Ludic + + + + + + +
+
API Reference › List
+
namespace

List

+

Operations over []T slices — length, ends access, push/pop, swap, search, and in-place reverse. A slice is a shared growable buffer, so these mutate it in place and every holder sees the change. Arguments are positional.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-log.html b/ns-log.html new file mode 100644 index 00000000..0b5d07ed --- /dev/null +++ b/ns-log.html @@ -0,0 +1,26 @@ + + + + + +Log — Ludic + + + + + + +
+ +
namespace

Log

+

Levelled, structured logging — the default way to answer "what is my game doing?" and "why did that break?", and a real step up from scattering print calls through your code. Each message carries a level, from trace (most verbose) through debug, info, and warn to error, and a runtime threshold set with Log.set_level decides which ones actually appear — so a development build can be chatty and a release build quiet, without touching the call sites. + +Messages go to standard error, kept separate from a program's real stdout, tagged with their level. Beyond the message you can pass **structured fields** as trailing key/value pairs — Log.warn("missing texture", "path", p, "id", n) prints [WARN] missing texture path=... id=... — cheap to write and easy to grep. Numeric values (int, long) are formatted for you; the level tag is chosen at compile time, so a filtered-out level costs only a threshold comparison at runtime. + +Logging never touches the simulation — it writes to stderr and returns — so it has no effect on gameplay determinism or replays. This first version ships the console (stderr) sink; a rotating-file sink and an in-engine overlay sink are planned follow-ups.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-map.html b/ns-map.html new file mode 100644 index 00000000..6ef2da23 --- /dev/null +++ b/ns-map.html @@ -0,0 +1,22 @@ + + + + + +Map — Ludic + + + + + + +
+ +
namespace

Map

+

A character grid the game paints and reads.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-math.html b/ns-math.html new file mode 100644 index 00000000..94c95aa9 --- /dev/null +++ b/ns-math.html @@ -0,0 +1,24 @@ + + + + + +Math — Ludic + + + + + + +
+
API Reference › Math
+
namespace

Math

+

Deterministic fixed-point math. Every function is computed in Q16.16 with plain integer arithmetic, so results are bit-identical on every platform and every run — the same guarantee the rest of the runtime gives. Arguments are positional. + +Given a float or double argument, the same functions compute in that type instead (using the platform's math library) and return it — floor, ceil and round included, which return an int only for fixed. sign returns an int either way. trunc and atan exist only for floats.

+
Math.min(a, b) -> intThe smaller of two values.Math.max(a, b) -> intThe larger of two values.Math.abs(x) -> intThe magnitude of a value, dropping its sign.Math.clamp(v, lo, hi) -> intConstrain a value to the range [lo, hi].Math.sign(x) -> intThe sign of a value as -1, 0, or 1.Math.floor(x) -> intRound a fixed value down to the nearest whole int.Math.ceil(x) -> intRound a fixed value up to the nearest whole int.Math.round(x) -> intRound a fixed value to the nearest whole int.Math.lerp(a, b, t) -> fixedBlend between two values by a 0..1 amount.Math.inverse_lerp(a, b, v) -> fixedFind where a value sits between two endpoints as a 0..1 fraction.Math.remap(v, in0, in1, out0, out1) -> fixedRescale a value from one range into another.Math.sqrt(x) -> fixedThe square root of a fixed value.Math.sin(radians) -> fixedSine of an angle in radians.Math.cos(radians) -> fixedCosine of an angle in radians.Math.tan(radians) -> fixedTangent of an angle in radians.Math.hypot(x, y) -> fixedLength of the vector (x, y).Math.dist(x0, y0, x1, y1) -> fixedDistance between two points.Math.dist2(x0, y0, x1, y1) -> fixedSquared distance between two points.Math.deg_to_rad(degrees) -> fixedConvert degrees to radians.Math.rad_to_deg(radians) -> fixedConvert radians to degrees.Math.posmod(a, m) -> intModulo that is always non-negative.Math.wrap(v, lo, hi) -> intWrap a value into the range [lo, hi).Math.ping_pong(t, len) -> intBounce a counter back and forth in [0, len].Math.snapped(v, step) -> fixedRound a value to the nearest multiple of step.Math.move_toward(from, to, delta) -> fixedStep from one value toward another by at most delta.Math.smoothstep(e0, e1, x) -> fixedA smooth 0..1 ramp between two edges.Math.atan2(y, x) -> fixedThe angle of the vector (x, y), in radians.Math.asin(x) -> fixedThe arcsine of a value, in radians.Math.acos(x) -> fixedThe arccosine of a value, in radians.Math.exp(x) -> fixede raised to the power x.Math.log(x) -> fixedThe natural logarithm of x.Math.pow(base, exp) -> fixedbase raised to a fixed exponent.
+ ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-memory.html b/ns-memory.html new file mode 100644 index 00000000..66a5c807 --- /dev/null +++ b/ns-memory.html @@ -0,0 +1,22 @@ + + + + + +Memory — Ludic + + + + + + +
+
API Reference › Memory
+
namespace

Memory

+

Raw memory — allocate byte and word buffers, copy and fill regions, and peek/poke individual bytes. The low-level escape hatch.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-mime.html b/ns-mime.html new file mode 100644 index 00000000..6b19a8cd --- /dev/null +++ b/ns-mime.html @@ -0,0 +1,24 @@ + + + + + +Mime — Ludic + + + + + + +
+
API Reference › Mime
+
namespace

Mime

+

Content-type detection for assets and downloads: map a filename to its MIME type, or refine the guess by peeking at a file's leading bytes. Useful for deciding how to load a dropped-in mod file, labelling an export, or picking a decoder. + +of is a pure lookup by extension; sniff reads the first bytes and recognises a few well-known signatures (PNG, JPEG, GIF, PDF), falling back to the extension when nothing matches. The extension table is deliberately compact and documented; richer magic-byte coverage is a follow-up.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-motion.html b/ns-motion.html new file mode 100644 index 00000000..0b71fe20 --- /dev/null +++ b/ns-motion.html @@ -0,0 +1,22 @@ + + + + + +Motion — Ludic + + + + + + +
+
API Reference › Motion
+
namespace

Motion

+

Ergonomic control of the engine-owned Motion component — the value tween the engine advances each tick (see the ECS engine-systems). Motion.to starts a tween on an entity from one value to another over a number of ticks with an easing curve, rewinding it so it plays from the start, instead of setting the component's from/to/dur/ease fields by hand. The tween is integer and deterministic, so motion reproduces exactly under replay and lockstep. For a standalone, sequenced tween not tied to a component, see the fluent Tween handles. Related: Anim, Ease, Tween.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-network.html b/ns-network.html new file mode 100644 index 00000000..3ab3113e --- /dev/null +++ b/ns-network.html @@ -0,0 +1,22 @@ + + + + + +Network — Ludic + + + + + + +
+
API Reference › Network
+
namespace

Network

+

The low-level networking seam — send and poll datagrams, serialize and apply entity state, and read ownership and role. Multiplayer is normally a compile-time property of the ECS, not glue you thread by hand: mark fields @Sync and models @Owned and the compiler generates the replication. These Network.* primitives are what that sugar lowers to, for when you drive the transport yourself. Offline they collapse to single-player defaults.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-noise.html b/ns-noise.html new file mode 100644 index 00000000..934e4902 --- /dev/null +++ b/ns-noise.html @@ -0,0 +1,28 @@ + + + + + +Noise — Ludic + + + + + + +
+
API Reference › Noise
+
namespace

Noise

+

Procedural noise — the primitive that terrain, caves, biomes, textures, clouds, wind, and object placement are built on. Every generator is implemented in Q16.16 **fixed point** over an integer permutation hash seeded from an explicit seed, so a given seed reproduces the *exact* same field on every platform and every run: native, headless, and (later) wasm all agree bit-for-bit. That is a real edge over float-based engines, whose worlds can drift subtly across CPUs and break shared-seed multiplayer or replays. + +Coordinates are fixed values. The integer part of a coordinate selects a lattice cell and the fraction interpolates within it, so you scale feature size by sampling at a fractional *frequency* (e.g. multiply coordinates by 1/64). Outputs are fixed normalised to [-1, 1]; Noise.unit remaps that to [0, 1] when you want a height or a probability. + +Pick a generator by feel: value2 is cheap and blocky; perlin2 is the classic gradient noise; simplex2 is the organic default with fewer directional artifacts; fbm2 stacks octaves of simplex for natural, detailed fields; and cellular2 (Worley) gives Voronoi-cell structure for stone, cracks, and biome boundaries. Every sampler is a pure function of (x, y, seed) — no global state, no allocation — so it is safe to call across a whole worldgen pass or a per-pixel fill. + +Seed worldgen from its own seed (or a dedicated Random stream), kept separate from gameplay RNG, so generating the world never desyncs the simulation.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-os.html b/ns-os.html new file mode 100644 index 00000000..01ef4d07 --- /dev/null +++ b/ns-os.html @@ -0,0 +1,28 @@ + + + + + +Os — Ludic + + + + + + +
+ +
namespace

Os

+

The operating-system interface — the environment *around* the game rather than the game itself: the command line, environment variables, the standard streams, the process exit code, the host platform, and the per-user folders a game writes its saves, config, and cache into. It rounds the bare System builtins (arg, getenv, exit) into one coherent, Go-flavored namespace. + +It is deliberately small and game-scoped: there is no process spawning, no signals, and no user/permission APIs. Reach for it in launchers, asset pipelines, and dev tools — parsing launch flags like --level 3, reading config from the environment, or resolving where a save file belongs — far more than in the simulation itself. + +**Determinism:** args, the env family, and platform/arch are non-deterministic host input. Read them once at startup to configure the game, and keep them out of the replayable simulation so a shared seed still reproduces. + +**Platform coverage:** this first version targets the native macOS/BSD host — the fully supported target today. platform is portable (the uname system name is available on every Unix); arch and the known-folder layout (save_dir/config_dir/cache_dir) follow the macOS conventions. Linux/Windows/wasm folder resolution and a target-aware arch are documented follow-ups.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-path.html b/ns-path.html new file mode 100644 index 00000000..165de79a --- /dev/null +++ b/ns-path.html @@ -0,0 +1,24 @@ + + + + + +Path — Ludic + + + + + + +
+
API Reference › Path
+
namespace

Path

+

Path manipulation as pure string operations — no disk access, no allocation surprises, just the joining and splitting every save system, mod loader, and asset pipeline needs. Separate a filename from its folder, get an extension, join segments without worrying about doubled or missing separators, and collapse ./../duplicate slashes into a canonical form. + +Paths use / as the separator, matching the native (macOS/BSD) filesystem — the fully supported target today; Windows-style separators are a follow-up. Pair these with Os (which supplies per-user directory *locations*) and Fs (which does the actual reading and writing).

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-percent.html b/ns-percent.html new file mode 100644 index 00000000..dd2e1465 --- /dev/null +++ b/ns-percent.html @@ -0,0 +1,22 @@ + + + + + +Percent — Ludic + + + + + + +
+
API Reference › Percent
+
namespace

Percent

+

A value clamped to [0, 1] — health fractions, volumes, and the t of an interpolation, without stray values slipping below 0 or above 1. Percent.clamp pins any fixed into range; Percent.of forms a clamped ratio num / den; Percent.lerp interpolates a..b by a clamped t; and Percent.apply scales a value by a clamped fraction. Every operation is deterministic fixed-point. Arguments are positional. Spliced in only when a program mentions Percent.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-pool.html b/ns-pool.html new file mode 100644 index 00000000..59be528b --- /dev/null +++ b/ns-pool.html @@ -0,0 +1,22 @@ + + + + + +Pool — Ludic + + + + + + +
+
API Reference › Pool
+
namespace

Pool

+

Entity-pool statistics. Ludic's ECS is already pool-based: the allocator recycles freed entity slots through a freelist (a despawned slot is reused by the next spawn before any new slot is taken), and component storage is fixed per-entity arrays — so spawning and despawning many entities per frame does no per-spawn heap allocation and cannot fragment. Pool.live / Pool.free / Pool.reserved / Pool.capacity read those counters so a bullet-hell or horde game can watch reuse and budget against the cap.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-prefab.html b/ns-prefab.html new file mode 100644 index 00000000..e66dd289 --- /dev/null +++ b/ns-prefab.html @@ -0,0 +1,22 @@ + + + + + +Prefab — Ludic + + + + + + +
+
API Reference › Prefab
+
namespace

Prefab

+

A prefab is a model with preset component fields — prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } } — spawned with spawn Grunt { Position { x: 40 } }, where the spawn's own fields override the presets. Prefabs chain (prefab Grunt: Foe, where Foe is itself a prefab) so shared presets live once. spawn is also an expression yielding the new entity (let e = spawn Grunt { … }), and Prefab.spawn spawns a prefab chosen at runtime by name.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-process.html b/ns-process.html new file mode 100644 index 00000000..a396f9ec --- /dev/null +++ b/ns-process.html @@ -0,0 +1,24 @@ + + + + + +Process — Ludic + + + + + + +
+
API Reference › Process
+
namespace

Process

+

Child processes, started and polled — what a launcher is made of. Process.spawn starts a program directly, with no shell, and returns at once; each frame Process.poll answers -1 while the child runs and its exit code once it has ended, so a crash is a non-zero code the game can act on. Process.kill ends a child and Process.free lets a handle go. Pair it with App.window_hide to step aside while the child runs. + +The child inherits the environment and the working directory. posix_spawn on macOS; CreateProcessW on Windows, with each argument quoted by the MSVC rules and no console window. Like Http.* it is out-of-band and must never feed the deterministic lockstep/replay simulation.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-promise.html b/ns-promise.html new file mode 100644 index 00000000..f0beccd4 --- /dev/null +++ b/ns-promise.html @@ -0,0 +1,22 @@ + + + + + +Promise — Ludic + + + + + + +
+
API Reference › Promise
+
namespace

Promise

+

Combine several Job futures and resolve the group on the main thread. Promise.all succeeds once every member has, Promise.race once the first does; both return an ordinary job handle you poll like any other. For a loading screen, Promise.count_done over the same handles is the bar's numerator and len the denominator, and Promise.all_done is the ready check. Ludic has no closures, so progress is polled rather than chained through a then callback. Build the handle list with new []int + push. Spliced in only when a program mentions Promise.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-query.html b/ns-query.html new file mode 100644 index 00000000..a9a1d653 --- /dev/null +++ b/ns-query.html @@ -0,0 +1,22 @@ + + + + + +Query — Ludic + + + + + + +
+
API Reference › Query
+
namespace

Query

+

Spatial and set queries over the live entities that carry a property, built on the World reflection ABI. Query.count and Query.first ask how many bearers there are and which is first; Query.nearest and Query.within add a position — read from a coordinate property's two int fields — to find the closest entity to a point or every entity inside a radius. A property id comes from World.prop_id and a field id from World.field_id. Everything is integer and deterministic: the same world reproduces the same answers, entity order included, every run. The scan is linear over the entity table — ample for the entity counts Ludic targets — and a game that uses Query.* gets the reflection ABI emitted automatically, no @event required.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-random.html b/ns-random.html new file mode 100644 index 00000000..5ab7c1c7 --- /dev/null +++ b/ns-random.html @@ -0,0 +1,22 @@ + + + + + +Random — Ludic + + + + + + +
+
API Reference › Random
+
namespace

Random

+

A seeded, deterministic RNG — same seed, same sequence, every run and every platform.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-rect.html b/ns-rect.html new file mode 100644 index 00000000..840144c0 --- /dev/null +++ b/ns-rect.html @@ -0,0 +1,22 @@ + + + + + +Rect — Ludic + + + + + + +
+
API Reference › Rect
+
namespace

Rect

+

Axis-aligned rectangles for HUD layout boxes, hitboxes, and camera regions. A Rect is four Q16.16 fixed components — position (x, y) (its top-left corner) and size (w, h) — packed into a single value that is copied by value and never allocates. It offers fast point-in-rect and rectangle-overlap tests. Every operation is deterministic fixed-point, bit-identical on every platform. Arguments are positional.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-reflect.html b/ns-reflect.html new file mode 100644 index 00000000..73e51c92 --- /dev/null +++ b/ns-reflect.html @@ -0,0 +1,22 @@ + + + + + +Reflect — Ludic + + + + + + +
+
API Reference › Reflect
+
namespace

Reflect

+

Runtime reflection over the world schema — inspect properties, fields, and entity state by name and by index. It powers the conveniences game devs consume without writing reflection code: automatic save/load, data-driven tools, and debug/inspector overlays. Enumerate with Reflect.prop_count/Reflect.prop_name and Reflect.field_count/Reflect.field_name/Reflect.field_type; resolve ids with Reflect.prop/Reflect.field; read and write an entity's fields with Reflect.get/Reflect.set/Reflect.has; and identify its model with Reflect.kind/Reflect.model. The metadata tables are generated at compile time (the same reflection ABI a foreign mod binds), so introspection is a table walk, not heavy runtime machinery. This is an advanced/tooling surface — most developers get its benefits through built-in features. Related: World, Query.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-regex.html b/ns-regex.html new file mode 100644 index 00000000..2b81babc --- /dev/null +++ b/ns-regex.html @@ -0,0 +1,22 @@ + + + + + +Regex — Ludic + + + + + + +
+
API Reference › Regex
+
namespace

Regex

+

Regular expressions with PCRE/PECL-compatible syntax over a **linear-time** Thompson NFA (a Pike VM), so a bad pattern from a modder can never trigger catastrophic backtracking — matching is O(n·m), never exponential. Compile a pattern once and reuse it; an invalid pattern is an error value, never a crash. Supported: literals, ., classes [...] with ranges/negation and \d \w \s (and their negations), anchors ^ $, alternation |, capturing and (?:…) groups, and the quantifiers * + ? and {n,m} in greedy or lazy form. Backreferences and look-around are out of scope for a linear engine.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-save.html b/ns-save.html new file mode 100644 index 00000000..2c7a7533 --- /dev/null +++ b/ns-save.html @@ -0,0 +1,22 @@ + + + + + +Save — Ludic + + + + + + +
+
API Reference › Save
+
namespace

Save

+

Whole-world save and restore — write a binary snapshot of the ECS world and read it back.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-screen.html b/ns-screen.html new file mode 100644 index 00000000..2ccf8a71 --- /dev/null +++ b/ns-screen.html @@ -0,0 +1,22 @@ + + + + + +Screen — Ludic + + + + + + +
+
API Reference › Screen
+
namespace

Screen

+

The 2D drawing surface. Every call takes named arguments; draw during the Render phase, then Screen.show().

+
Screen.clear(color)Fill the whole framebuffer with one color to start a fresh frame.Screen.fill_rectangle(x, y, width, height, color)Draw a solid, filled rectangle at a pixel position.Screen.draw_rectangle(x, y, width, height, color)Draw a one-pixel-thick rectangle outline (not filled).Screen.put_pixel(x, y, color)Set a single pixel at a pixel coordinate to one color.Screen.draw_text(x, y, text, color, scale)Draw a string with the built-in font at an integer scale.Screen.draw_number(x, y, value, color, scale)Draw an integer directly, with no string allocation or conversion.Screen.show()Present the finished frame — copy everything drawn this frame to the window.Screen.width() -> intThe framebuffer width in pixels.Screen.height() -> intThe framebuffer height in pixels.Screen.status(text)Set the persistent one-line status/HUD string shown by the runtime.Screen.line(x1, y1, x2, y2, color)Draw a straight line between two points.Screen.circle(x, y, radius, color)Draw a circle outline.Screen.fill_circle(x, y, radius, color)Draw a filled disc.Screen.triangle(x1, y1, x2, y2, x3, y3, color)Draw a triangle outline.Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color)Draw a filled triangle.Screen.sprite(id, x, y)Blit a sprite at its natural size.Screen.sprite_scaled(id, x, y, scale)Blit a sprite at an integer scale.Screen.oval(x, y, rx, ry, color)Draw an axis-aligned ellipse outline.Screen.camera(x, y)Set the world-space camera offset for the draw path.Screen.clip(x, y, width, height)Restrict drawing to a screen-space rectangle.Screen.clip_reset()Lift the clip rectangle (draw to the whole screen again).Screen.blend_mode(mode)Choose replace or additive pixel blending.Screen.measure_text(text) -> intThe pixel advance width of text in the built-in font.Screen.pixel(x, y) -> intRead a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:)A filled meter: value of max over a track.
+ ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-set.html b/ns-set.html new file mode 100644 index 00000000..625c8457 --- /dev/null +++ b/ns-set.html @@ -0,0 +1,22 @@ + + + + + +Set — Ludic + + + + + + +
+ +
namespace

Set

+

A set of string members — membership tests for tags, unlocked achievements, or visited tiles. A Set shares the same open-addressing hash table as Dict, so add / has / remove are O(1) on average and duplicates are ignored. Create one with Set.new, then add / has / remove / size / clear / members. Arguments are positional. The runtime is spliced in only when a program mentions Set.* (or Dict.*).

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-sprite.html b/ns-sprite.html new file mode 100644 index 00000000..e67085a5 --- /dev/null +++ b/ns-sprite.html @@ -0,0 +1,22 @@ + + + + + +Sprite — Ludic + + + + + + +
+
API Reference › Sprite
+
namespace

Sprite

+

A namespaced spritesheet / atlas API. Sprite.sheet loads one image and remembers its cell grid; Sprite.cell addresses a cell by grid coords and Sprite.cell_span a sprite that spans more than one cell; Sprite.define / Sprite.named name and look up a cell; Sprite.draw / Sprite.draw_scaled blit it (through the camera / zoom / clip). It stands on the variable-size image loader, so a cell can be any size — not just 16x16. (Distinct from the Sprite engine component of the sprite-render system.)

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-sync.html b/ns-sync.html new file mode 100644 index 00000000..179a294f --- /dev/null +++ b/ns-sync.html @@ -0,0 +1,22 @@ + + + + + +Sync — Ludic + + + + + + +
+
API Reference › Sync
+
namespace

Sync

+

The advanced, opt-in tier — here be dragons. Raw building blocks for engine-level systems that pass data around: a mutex (cooperative lock), an atomic counter (get / set / add / cas) and a bounded channel (send / recv / can_recv / len). On today's single-threaded deterministic runtime these are cooperative — correct, ordered, replayable and impossible to deadlock — and exist so message-passing code reads the same now as it will when a preemptive OS-thread backend lands behind this same API. Beginners never need this; reach for Job.* / Promise.* instead. Spliced in only when a program mentions Sync.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-system.html b/ns-system.html new file mode 100644 index 00000000..4b32453b --- /dev/null +++ b/ns-system.html @@ -0,0 +1,22 @@ + + + + + +System — Ludic + + + + + + +
+
API Reference › System
+
namespace

System

+

Low-level file and process access — raw file handles, reading a character, and running a shell command. Outside the deterministic simulation. For command-line arguments, environment variables, the exit code, and the standard streams, use the Os namespace.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-text.html b/ns-text.html new file mode 100644 index 00000000..e8d9a79b --- /dev/null +++ b/ns-text.html @@ -0,0 +1,22 @@ + + + + + +Text — Ludic + + + + + + +
+
API Reference › Text
+
namespace

Text

+

String queries and building over string values — length, character access, substrings, search, prefix/suffix tests, and int conversion. Arguments are positional. Strings are byte sequences, so indices and lengths count bytes.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-tiled.html b/ns-tiled.html new file mode 100644 index 00000000..60dd3d06 --- /dev/null +++ b/ns-tiled.html @@ -0,0 +1,22 @@ + + + + + +Tiled — Ludic + + + + + + +
+
API Reference › Tiled
+
namespace

Tiled

+

Load and draw Tiled maps. Both native format families — TMX/TSX/TX (XML) and TMJ/TSJ/TJ (JSON) — read onto one intermediate, a Value tree in Tiled's JSON schema, with layer data decoded to a dense GID list; so a CSV .tmx and a base64+zlib .tmj of the same map read identically. Tiled.read returns that intermediate tree for a map file and Tiled.read_tsx for a tileset file. Spliced on demand when a program mentions Tiled.*, alongside the Xml, Base64 and Value runtimes it builds on.

+
Tiled.read(path: string) -> valueRead a map file into the intermediate value tree.Tiled.read_tsx(path: string) -> valueRead a tileset file into a value object.Tiled.load(path: string) -> mapLoad a map file into the runtime map model.Tiled.gid(map, layer: int, x: int, y: int) -> intThe raw GID at a cell in a tile layer.Tiled.resolve(map, gid: int) -> gidinfoDecode a GID into tileset, local id and flip flags.Tiled.width(map) -> intThe map width in tiles.Tiled.height(map) -> intThe map height in tiles.Tiled.layer_count(map) -> intHow many layers the map has.Tiled.layer_name(map, index: int) -> stringA layer's name by index.Tiled.draw(map, camx: int, camy: int) -> voidDraw every visible tile layer.Tiled.project(map, layer: int) -> voidProject a collision layer to the byte tilemap.Tiled.tree(map) -> valueThe underlying intermediate value tree.Tiled.tile_prop(map, gid: int, name: string) -> intWhether a GID's tile carries a bool property.Tiled.collide(map, layer: int) -> voidDrive collision from per-tile metadata.Tiled.collision_kind(map, gid: int) -> intClassify a GID's collision from its tile metadata.Tiled.tile_shapes(map, gid: int) -> intWhether a GID's tile has a collision shape.Tiled.draw_anim(map, camx: int, camy: int, frame: int) -> voidDraw with animated tiles advanced to a frame.Tiled.frame_gid(map, gid: int, frame: int) -> intThe current GID of an animated tile at a frame.Tiled.animated(map, gid: int) -> intWhether a GID's tile is animated.Tiled.object_count(map, layer: int) -> intHow many objects an object layer holds.Tiled.object(map, layer: int, index: int) -> valueAn object on an object layer by index.Tiled.object_shape(object) -> stringClassify an object's shape.Tiled.prop(container, name: string) -> stringA custom property's value, with class-default fallback.Tiled.prop_int(container, name: string) -> intA custom property parsed as an integer.Tiled.prop_type(container, name: string) -> stringA custom property's declared type.Tiled.load_types(path: string) -> intLoad a project custom-type table.Tiled.template(path: string) -> valueRead a template file into an object value.Tiled.spawn(map, object, model: string) -> intSpawn a Ludic entity from an object.Tiled.spawn_layer(map, layer: int, model: string) -> intSpawn every object on a layer.Tiled.cell_x(map, x: int, y: int) -> intThe screen x of a tile cell for the map orientation.Tiled.cell_y(map, x: int, y: int) -> intThe screen y of a tile cell for the map orientation.Tiled.layer_kind(map, layer: int) -> intA layer's kind.Tiled.layer_opacity(map, layer: int) -> stringA layer's opacity.Tiled.layer_tint(map, layer: int) -> stringA layer's tint colour.Tiled.layer_offsetx(map, layer: int) -> intA layer's horizontal offset.Tiled.layer_offsety(map, layer: int) -> intA layer's vertical offset.Tiled.world(path: string) -> valueRead a .world file.Tiled.world_count(world) -> intHow many maps a world stitches.Tiled.world_map(world, index: int) -> valueA world member map by index.
+ ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-time.html b/ns-time.html new file mode 100644 index 00000000..8085ea3f --- /dev/null +++ b/ns-time.html @@ -0,0 +1,22 @@ + + + + + +Time — Ludic + + + + + + +
+
API Reference › Time
+
namespace

Time

+

The frame clock. frame/elapsed/delta are deterministic (driven by a per-frame counter at a fixed 60 fps), so a replay reproduces every value; now is the wall clock and is explicitly non-deterministic.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-tween.html b/ns-tween.html new file mode 100644 index 00000000..1ee986b5 --- /dev/null +++ b/ns-tween.html @@ -0,0 +1,22 @@ + + + + + +Tween — Ludic + + + + + + +
+
API Reference › Tween
+
namespace

Tween

+

Value interpolation over a timeline, off the fixed frame clock. The timeline helpers Tween.progress/Tween.loop/Tween.yoyo turn an elapsed timer and a duration into a normalized amount; Tween.ease shapes that amount through an easing curve (shared with Ease); and the typed blends Tween.number/Tween.round/Tween.point/Tween.tint interpolate a fixed, int, Vector, or color. All deterministic fixed-point, so a replay reproduces every eased value exactly. Beyond these pure interpolators there are fluent handles the engine advances for you: Tween.to starts one and returns a handle, Tween.chain and Tween.delay sequence more segments, and Tween.value / Tween.done / Tween.parallel / Tween.stop read and control them.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-udp.html b/ns-udp.html new file mode 100644 index 00000000..f75d6925 --- /dev/null +++ b/ns-udp.html @@ -0,0 +1,24 @@ + + + + + +Udp — Ludic + + + + + + +
+ +
namespace

Udp

+

Plain IPv4 datagrams, polled — the transport under a game's own netcode: peer-to-peer play, a LAN lobby, a STUN query. Open a socket with Udp.open, send with Udp.send, and each frame drain what arrived with Udp.recv, which returns 0 when nothing is waiting and never blocks. The sender of the last datagram read is Udp.from_ip / from_port. + +An address is an int: a.b.c.d is (a << 24) | (b << 16) | (c << 8) | d, converted by Udp.ip and Udp.ip_text. Datagrams can be lost, repeated and reordered; the protocol a game builds on top says what to do about that. Like Http.* it is out-of-band and must never feed the deterministic lockstep/replay simulation directly. BSD sockets on macOS, Winsock on Windows.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-ui.html b/ns-ui.html new file mode 100644 index 00000000..d0c7839f --- /dev/null +++ b/ns-ui.html @@ -0,0 +1,22 @@ + + + + + +Ui — Ludic + + + + + + +
+ +
namespace

Ui

+

The retained-mode menu API over a ui block. Ui.build constructs every declared widget tree once (after fonts and skins are loaded); Ui.open makes one menu active and Ui.close deactivates it; the frame loop ticks navigation on its own, an activation fires the UiClicked event, and Ui.clicked is the polled form; Ui.set_text updates a label or button, and Ui.render draws the active menu (call it from an Overlay handler so it paints over the world). Every id: Name in a ui block mints a UI_Name handle.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-unicode.html b/ns-unicode.html new file mode 100644 index 00000000..8ac581ef --- /dev/null +++ b/ns-unicode.html @@ -0,0 +1,28 @@ + + + + + +Unicode — Ludic + + + + + + +
+
API Reference › Unicode
+
namespace

Unicode

+

Correct-by-default text over UTF-8. Player names, translated menus, and chat arrive as UTF-8 bytes, and counting *bytes* gets non-ASCII text wrong — the wrong length, and truncation that slices a character in half into mojibake. The Unicode namespace works in code points and (approximately) grapheme clusters instead, so measuring, indexing, truncating, and case-mapping behave for every language. + +This is the correctness layer, not a replacement: the byte-oriented Text operations stay for speed on ASCII and for raw byte work. Reach for Unicode.* whenever the text came from a human — a name, a message, a localized string. + +Three notions of "length" matter, and the API keeps them distinct: byte_len (storage), len (code points — Unicode scalar values), and grapheme_len (user-perceived characters, where a base letter plus its combining accent, or a ZWJ emoji sequence, count as one). + +**Coverage (v1):** decoding and validation cover the full UTF-8 range. Case mapping covers ASCII and the Latin-1 letters — correct for Western-European text; wider scripts (Latin-Extended, Greek, Cyrillic), locale rules (Turkish i, German ß), and NFC normalization are follow-ups. grapheme_len approximates UAX#29 for the cases real player text hits — combining marks, variation selectors, ZWJ sequences, and flag pairs.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-uuid.html b/ns-uuid.html new file mode 100644 index 00000000..47b57286 --- /dev/null +++ b/ns-uuid.html @@ -0,0 +1,28 @@ + + + + + +Uuid — Ludic + + + + + + +
+
API Reference › Uuid
+
namespace

Uuid

+

Universally-unique identifiers — stable IDs that don't collide, generated locally with no central authority handing out numbers. Reach for a UUID whenever something needs an identity that survives being saved, shared, or sent over a network: players and sessions in multiplayer, user-created content (levels, items, mods) that has to merge cleanly across installs, or a per-install / per-run ID for analytics and bug reports. + +Two versions are provided. Uuid.new makes a **v4** (random) UUID — 122 bits of entropy, effectively never colliding. Uuid.new_v7 makes a **v7** (time-ordered) UUID whose leading bits are a millisecond timestamp, so a batch of v7 IDs sorts by creation time — friendly to database indexes and append logs. Both set the RFC 4122 version and variant bits correctly. + +A UUID is represented as its canonical lowercase 36-character text form (8-4-4-4-12), the same shape you store, print, send, and compare — so there is no conversion at each boundary. Validate untrusted input with Uuid.is_valid or normalise it with Uuid.parse, and compare with Uuid.equals, which ignores case. + +**Determinism caveat.** v4 and the random tail of v7 come from the OS cryptographically-secure RNG (Crypto), which is non-deterministic by design. Minting a UUID inside the lockstep simulation will desync replays and networked peers — generate IDs at the edges (on connect, on save, on spawn-from-input), never per tick in reproducible gameplay code.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-value.html b/ns-value.html new file mode 100644 index 00000000..4f1e6b0c --- /dev/null +++ b/ns-value.html @@ -0,0 +1,22 @@ + + + + + +Value — Ludic + + + + + + +
+
API Reference › Value
+
namespace

Value

+

A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through. A node is one of null, int, fixed, bool, str, list, or object (see Value.kind). Build one with the constructors (Value.int, Value.object, …) and the builders Value.add/Value.put; read it with Value.get/Value.at/Value.count and the as_* accessors. Related: Json, Reflect.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-vector.html b/ns-vector.html new file mode 100644 index 00000000..a4168c40 --- /dev/null +++ b/ns-vector.html @@ -0,0 +1,22 @@ + + + + + +Vector — Ludic + + + + + + +
+
API Reference › Vector
+
namespace

Vector

+

2D vector math for positions, velocities, and directions. A Vector is a pair of Q16.16 fixed components packed into one value, so it is copied by value and never allocates. Every operation is deterministic integer fixed-point — bit-identical on every platform, the same guarantee the rest of the runtime gives. Arguments are positional; angles are in radians.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-world.html b/ns-world.html new file mode 100644 index 00000000..008754f4 --- /dev/null +++ b/ns-world.html @@ -0,0 +1,22 @@ + + + + + +World — Ludic + + + + + + +
+
API Reference › World
+
namespace

World

+

Runtime reflection over the ECS world — read and write component fields by id, spawn and inspect entities, and register components at runtime. The introspection layer a mod uses.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ns-xml.html b/ns-xml.html new file mode 100644 index 00000000..fd5e32f6 --- /dev/null +++ b/ns-xml.html @@ -0,0 +1,22 @@ + + + + + +Xml — Ludic + + + + + + +
+ +
namespace

Xml

+

A minimal, deterministic XML reader for the element/attribute/CDATA subset the native Tiled formats (TMX/TSX/TX) use. Xml.parse turns a document into an element tree; the accessors read a node's tag, text, attributes (Xml.attr/Xml.attr_int/Xml.has) and children (Xml.child/Xml.find/Xml.count). It expands the five predefined entities and numeric character references, skips the <?xml?> prolog, comments and <!DOCTYPE>, and is best-effort rather than validating — the same contract as Json. Spliced on demand when a program mentions Xml.*.

+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-access.html b/op-access.html new file mode 100644 index 00000000..91ebacd9 --- /dev/null +++ b/op-access.html @@ -0,0 +1,30 @@ + + + + + +Member & index — Ludic + + + + + + +
+
API Reference › Operators & tokens › Member & index
+
+ operator +

Member & index

+
+ x.field buffer[i] text[a..b] +

The access operators reach into a compound value. value.field reads or writes a property field and chains freely (route.next.column); buffer[index] reads or writes one element of a slice or raw buffer; and text[start..end] produces a fresh substring of the bytes in the half-open range, while text[index] reads a single byte as an int code point. Each form works both as a value and as an assignment target, and they compose — route[index].column = 0 is one address computation. Field access also binds method-style calls like Screen.fill_rectangle.

+ +

Example

let head_column: int = segments[0].column
+segments[0].column = head_column + 1
+let extension: string = file_name[len(file_name) - 4..len(file_name)]
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-arith.html b/op-arith.html new file mode 100644 index 00000000..06e4afed --- /dev/null +++ b/op-arith.html @@ -0,0 +1,30 @@ + + + + + +Arithmetic — Ludic + + + + + + +
+ +
+ operator +

Arithmetic

+
+ + - * / % +

The arithmetic operators are + (add), - (subtract), * (multiply), / (divide), and % (remainder). On int values / is integer division that truncates and % gives the remainder — handy for wrapping a value or testing a period, as in elapsed_frames % 30 == 0. On fixed values the same symbols do fixed-point math (multiply and divide are scaled), and mixing an int with a fixed promotes the int. *, /, and % bind tighter than + and -, so column * TILE_SIZE + 1 groups as expected.

+ +

Example

let pixel_x: int = column * TILE_SIZE + 1
+let is_even_row: bool = row % 2 == 0
+let average: int = (current_health + maximum_health) / 2
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-assign.html b/op-assign.html new file mode 100644 index 00000000..f45cc9fa --- /dev/null +++ b/op-assign.html @@ -0,0 +1,32 @@ + + + + + +Assignment — Ludic + + + + + + +
+ +
+ operator +

Assignment

+
+ name = value +

Assignment stores a value into a target — a var binding, a property field, or a buffer/slice element — with target = value. The compound forms +=, -=, *=, and /= update in place, so score += 1 means score = score + 1. Assignment is a statement, not an expression: it produces no value, so you cannot write if x = 0 (use == for the test). Only a mutable target accepts it — assigning to a let or const binding is a compile error — though you may still mutate through an immutable binding that holds a record or slice.

+ +

Example

var score: int = 0
+score = 10
+score += 5
+let current_position = new_position()
+current_position.column = current_position.column + 1
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-bitwise.html b/op-bitwise.html new file mode 100644 index 00000000..c6b6fcbe --- /dev/null +++ b/op-bitwise.html @@ -0,0 +1,31 @@ + + + + + +Bitwise — Ludic + + + + + + +
+ +
+ operator +

Bitwise

+
+ & | ^ ~ << >> +

The bitwise operators work on the bits of an int: & (and), | (or), ^ (xor), ~ (not), << (shift left), and >> (a logical/unsigned shift right). They are the tools for flag sets, packing several small values into one integer, and fast multiply/divide by powers of two. Precedence is Go-style: <<, >>, and & bind like * (tightly), while | and ^ bind like +, and all of them bind tighter than comparison — so flags & MASK == 0 means (flags & MASK) == 0 with no parentheses.

+ +

Example

const FLAG_POISONED: int = 1
+const FLAG_SHIELDED: int = 2
+var status_flags: int = FLAG_POISONED | FLAG_SHIELDED
+var is_shielded: bool = status_flags & FLAG_SHIELDED != 0
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-comment.html b/op-comment.html new file mode 100644 index 00000000..dc86b6d6 --- /dev/null +++ b/op-comment.html @@ -0,0 +1,30 @@ + + + + + +Comment — Ludic + + + + + + +
+ +
+ operator +

Comment

+
+ # to end of line +

A comment begins with # and runs to the end of the line; the compiler ignores everything after it. Ludic has only this one line-comment form — there is no block-comment syntax — so to comment out several lines put a # on each. Use comments to explain intent next to a const, to label a section of a handler, or to note a gotcha. The source formatter (ludic-fmt) works on tokens, so your comments and blank lines survive a reformat.

+ +

Example

const TILE_SIZE: int = 16          # pixels per grid cell
+var score: int = 0                 # reset in the Start phase
+# the head is drawn brighter than the body
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-compare.html b/op-compare.html new file mode 100644 index 00000000..2238e604 --- /dev/null +++ b/op-compare.html @@ -0,0 +1,29 @@ + + + + + +Comparison — Ludic + + + + + + +
+ +
+ operator +

Comparison

+
+ == != < <= > >= +

The comparison operators — == (equal), != (not equal), <, <=, >, >= — take two values and yield a bool, the kind of test an if or where clause wants. On string values == and != compare by content, so direction == "left" checks the characters, not the pointer (a comparison against null stays a pointer test). Comparison binds looser than arithmetic and looser than the bitwise operators, so flags & MASK == 0 reads as (flags & MASK) == 0 with no parentheses needed. Chain several conditions together with and / or.

+ +

Example

if current_health <= 0 { become GameOver }
+if pressed_key == 'w' and elapsed_frames > 0 { row = row - 1 }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-interp.html b/op-interp.html new file mode 100644 index 00000000..417cc02a --- /dev/null +++ b/op-interp.html @@ -0,0 +1,30 @@ + + + + + +String interpolation — Ludic + + + + + + +
+
API Reference › Operators & tokens › String interpolation
+
+ operator +

String interpolation

+
+ `text {expr} more` +

A backtick string ` … is an interpolated string: any {expr} hole inside it is evaluated, converted to text, and concatenated with the surrounding literal parts. Numbers, bools, and fixed values are stringified automatically and string values pass through, so score: {score} desugars to "score: " + string(score). It is the readable way to build a message from mixed pieces without hand-writing a + chain. Write a literal brace with {{ or }}`.

+ +

Example

let status_line: string = `score {score} — health {current_health}/{maximum_health}`
+Screen.status(status_line)
+print(`wave {wave_number} incoming`)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-literals.html b/op-literals.html new file mode 100644 index 00000000..eff522ad --- /dev/null +++ b/op-literals.html @@ -0,0 +1,31 @@ + + + + + +Literals — Ludic + + + + + + +
+ +
+ operator +

Literals

+
+ 42 0x1E90FF 'w' "text" true null +

Literals are the fixed values you write directly in source. 42 is a decimal int and 0x1E90FF is a hex int — hex is how colors are written, so a raw color is just an integer. A number with a decimal point (1.5) is a fixed. 'w' is a character literal, an int code point handy for comparing against Input.key(). "text" is a string, true / false are bools, and null is the null-pointer literal used to test an unset record, slice, or pointer field.

+ +

Example

let sky_color: int = 0x1E90FF
+let banner: string = "GAME OVER"
+if Input.key() == 'w' { row = row - 1 }
+if next_segment == null { is_tail = true }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-logical.html b/op-logical.html new file mode 100644 index 00000000..142c5b73 --- /dev/null +++ b/op-logical.html @@ -0,0 +1,29 @@ + + + + + +Logical — Ludic + + + + + + +
+ +
+ operator +

Logical

+
+ and or not +

The boolean combinators are and, or, and not. They combine bool values — usually the results of comparisons — into the compound conditions an if, while, or where clause tests. Ludic spells them as words, not symbols: && and || are not operators, and a bare ! is rejected with a diagnostic pointing you to not (!= is unaffected). and binds tighter than or, so a and b or c groups as (a and b) or c; add parentheses when you mean otherwise.

+ +

Example

if pressed_key != 0 and not is_paused { apply_input() }
+if current_health <= 0 or elapsed_frames > time_limit { become GameOver }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/op-range.html b/op-range.html new file mode 100644 index 00000000..879d1611 --- /dev/null +++ b/op-range.html @@ -0,0 +1,32 @@ + + + + + +Range — Ludic + + + + + + +
+ +
+ operator +

Range

+
+ a .. b +

a .. b is a half-open numeric range used by the counting for loop: for index in a .. b walks index from a up to but not including b. This "up to but not including" convention makes lengths line up naturally — 0 .. len(route) visits every element index of a slice, and 0 .. GRID_WIDTH visits every column with no off-by-one. Both endpoints are int expressions, so a bound can be a const, a var, or any computed value. It is distinct from the string slice text[start..end], which uses the same half-open idea for bytes.

+ +

Example

for row in 0 .. GRID_HEIGHT {
+  for column in 0 .. GRID_WIDTH {
+    Screen.put_pixel(x: column, y: row, color: Color.MidnightBlue)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-arch.html b/os-arch.html new file mode 100644 index 00000000..5f11084e --- /dev/null +++ b/os-arch.html @@ -0,0 +1,33 @@ + + + + + +Os.arch — Ludic + + + + + + +
+
API Reference › Os › Os.arch
+
+ method +

Os.arch

+
+ Os.arch() -> string +

The host CPU architecture as reported by the system, e.g. "arm64" or "x86_64". This reads the uname machine field using the macOS/BSD layout; a target-aware value for other platforms is a documented follow-up.

+ +

Example

program Arch {
+  entry {
+    let a = Os.arch()
+    print(len(a))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-arg.html b/os-arg.html new file mode 100644 index 00000000..b4a3ba54 --- /dev/null +++ b/os-arg.html @@ -0,0 +1,33 @@ + + + + + +Os.arg — Ludic + + + + + + +
+
API Reference › Os › Os.arg
+
+ method +

Os.arg

+
+ Os.arg(i) -> string +

The argument at index i. Index 0 is the program path; 1 onward are the launch arguments. Reading past arg_count() is out of bounds.

+

Parameters

ithe argument index (0 = the program path)
+

Example

program Arg {
+  entry {
+    let prog = Os.arg(0)   # the program path
+    print(len(prog))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-arg_count.html b/os-arg_count.html new file mode 100644 index 00000000..f0e84810 --- /dev/null +++ b/os-arg_count.html @@ -0,0 +1,32 @@ + + + + + +Os.arg_count — Ludic + + + + + + +
+
API Reference › Os › Os.arg_count
+
+ method +

Os.arg_count

+
+ Os.arg_count() -> int +

The number of command-line arguments, always at least 1 (the program path itself). Pair it with arg for index-based access, or just use args.

+ +

Example

program Argc {
+  entry {
+    if Os.arg_count() >= 1 { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-args.html b/os-args.html new file mode 100644 index 00000000..b93bf5c7 --- /dev/null +++ b/os-args.html @@ -0,0 +1,34 @@ + + + + + +Os.args — Ludic + + + + + + +
+
API Reference › Os › Os.args
+
+ method +

Os.args

+
+ Os.args() -> []string +

Returns the whole argument vector as a []string: element 0 is the program path, and the rest are the arguments the process was launched with. The list has exactly arg_count() elements, so you can iterate it directly to parse launch flags.

+ +

Example

program Args {
+  entry {
+    let all = Os.args()
+    var i = 0
+    while i < len(all) { print(len(all[i])); i = i + 1 }   # one length per argument
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-cache_dir.html b/os-cache_dir.html new file mode 100644 index 00000000..aa36868b --- /dev/null +++ b/os-cache_dir.html @@ -0,0 +1,36 @@ + + + + + +Os.cache_dir — Ludic + + + + + + +
+
API Reference › Os › Os.cache_dir
+
+ method +

Os.cache_dir

+
+ Os.cache_dir(app) -> string +

The per-user directory for an application's disposable cache — regenerable data the system may clear under disk pressure. Never store anything here you cannot recreate.

| | | +| --- | --- | +| macOS | ~/Library/Caches/<app> | +| Linux | $XDG_CACHE_HOME/<app>, or ~/.cache/<app> |

An XDG variable that is set but empty falls back to the default, as the spec requires.

+

Parameters

appthe application name (used as the folder name)
+

Example

program CacheDir {
+  entry {
+    let dir = Os.cache_dir("MyGame")
+    print(len(dir))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-config_dir.html b/os-config_dir.html new file mode 100644 index 00000000..b9c1ff1e --- /dev/null +++ b/os-config_dir.html @@ -0,0 +1,36 @@ + + + + + +Os.config_dir — Ludic + + + + + + +
+
API Reference › Os › Os.config_dir
+
+ method +

Os.config_dir

+
+ Os.config_dir(app) -> string +

The per-user directory where an application should store configuration, named for the app.

| | | +| --- | --- | +| macOS | ~/Library/Application Support/<app> | +| Linux | $XDG_CONFIG_HOME/<app>, or ~/.config/<app> |

On macOS this is the same directory as save_dir — the platform keeps config and saves together, and Apple's home for a config file that is not an NSUserDefaults plist is Application Support. On Linux XDG separates the two. An XDG variable that is set but empty falls back to the default, as the spec requires.

+

Parameters

appthe application name (used as the folder name)
+

Example

program ConfigDir {
+  entry {
+    let dir = Os.config_dir("MyGame")
+    print(len(dir))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-env.html b/os-env.html new file mode 100644 index 00000000..0e5b6dfe --- /dev/null +++ b/os-env.html @@ -0,0 +1,35 @@ + + + + + +Os.env — Ludic + + + + + + +
+
API Reference › Os › Os.env
+
+ method +

Os.env

+
+ Os.env(name) -> string +

Reads an environment variable and returns its value, or null when the variable is not set. Use env_or when you want a fallback, or has_env to test presence without risking a null.

+

Parameters

namethe variable name
+

Example

program Env {
+  entry {
+    if Os.has_env("HOME") {
+      let home = Os.env("HOME")
+      print(len(home))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-env_or.html b/os-env_or.html new file mode 100644 index 00000000..0d8bc847 --- /dev/null +++ b/os-env_or.html @@ -0,0 +1,33 @@ + + + + + +Os.env_or — Ludic + + + + + + +
+
API Reference › Os › Os.env_or
+
+ method +

Os.env_or

+
+ Os.env_or(name, fallback) -> string +

Reads an environment variable, returning fallback when it is not set — the null-safe way to read optional configuration. The result is always non-null, so it is safe to use directly.

+

Parameters

namethe variable name
fallbackthe value to use when the variable is unset
+

Example

program EnvOr {
+  entry {
+    let assets = Os.env_or("LUDIC_ASSETS", "assets/")
+    print(len(assets))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-exit.html b/os-exit.html new file mode 100644 index 00000000..999d3629 --- /dev/null +++ b/os-exit.html @@ -0,0 +1,36 @@ + + + + + +Os.exit — Ludic + + + + + + +
+
API Reference › Os › Os.exit
+
+ method +

Os.exit

+
+ Os.exit(code) +

Terminates the process immediately with the given status code — 0 for success, non-zero for failure, the convention every shell and CI system reads. Nothing after the call runs. Intended for tools and launchers; a game normally ends through its own lifecycle instead.

+

Parameters

codethe process exit status (0 = success)
+

Example

program Exit {
+  entry {
+    if Os.arg_count() < 2 {
+      Os.stderr_write("usage: tool <arg>\n")
+      Os.exit(1)
+    }
+    print(0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-has_env.html b/os-has_env.html new file mode 100644 index 00000000..0554a017 --- /dev/null +++ b/os-has_env.html @@ -0,0 +1,32 @@ + + + + + +Os.has_env — Ludic + + + + + + +
+
API Reference › Os › Os.has_env
+
+ method +

Os.has_env

+
+ Os.has_env(name) -> bool +

Tests whether an environment variable is set, without reading its value. Useful as a feature/debug toggle, or to guard an env read.

+

Parameters

namethe variable name
+

Example

program HasEnv {
+  entry {
+    if Os.has_env("LUDIC_DEBUG") { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-pid.html b/os-pid.html new file mode 100644 index 00000000..a7926c44 --- /dev/null +++ b/os-pid.html @@ -0,0 +1,33 @@ + + + + + +Os.pid — Ludic + + + + + + +
+
API Reference › Os › Os.pid
+
+ method +

Os.pid

+
+ Os.pid() -> int +

The operating system's id for this process — unique among the processes alive right now, so it is the natural suffix for scratch files that concurrent runs of the same tool must not share.

+ +

Example

program Pid {
+  entry {
+    let scratch = `{Os.temp_dir()}/mytool_{Os.pid()}.log`
+    print(len(scratch) > 0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-platform.html b/os-platform.html new file mode 100644 index 00000000..dde609c5 --- /dev/null +++ b/os-platform.html @@ -0,0 +1,32 @@ + + + + + +Os.platform — Ludic + + + + + + +
+
API Reference › Os › Os.platform
+
+ method +

Os.platform

+
+ Os.platform() -> string +

The host platform as a short id: "macos", "linux", or the raw uname system name on other Unixes. Derived from the operating-system name, which is portable across every Unix host. Non-deterministic host input — read it to branch on platform, not inside the simulation.

+ +

Example

program Platform {
+  entry {
+    if Os.platform() == "macos" { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-save_dir.html b/os-save_dir.html new file mode 100644 index 00000000..ddf3ed82 --- /dev/null +++ b/os-save_dir.html @@ -0,0 +1,36 @@ + + + + + +Os.save_dir — Ludic + + + + + + +
+
API Reference › Os › Os.save_dir
+
+ method +

Os.save_dir

+
+ Os.save_dir(app) -> string +

The per-user directory where an application should store save files, named for the app. Which directory that is, is the platform's decision, not the program's:

| | | +| --- | --- | +| macOS | ~/Library/Application Support/<app> | +| Linux | $XDG_DATA_HOME/<app>, or ~/.local/share/<app> |

An XDG variable that is set but empty falls back to the default, as the spec requires. The path is returned as a string; creating the directory and reading/writing files is the filesystem library's job (see the config_dir/cache_dir companions).

On macOS save_dir and config_dir are the same directory — Apple's home for a config file that is not an NSUserDefaults plist is Application Support too. On Linux XDG separates them, and so does this.

+

Parameters

appthe application name (used as the folder name)
+

Example

program SaveDir {
+  entry {
+    let dir = Os.save_dir("MyGame")
+    print(len(dir))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-set_env.html b/os-set_env.html new file mode 100644 index 00000000..28cef3b3 --- /dev/null +++ b/os-set_env.html @@ -0,0 +1,32 @@ + + + + + +Os.set_env — Ludic + + + + + + +
+
API Reference › Os › Os.set_env
+
+ method +

Os.set_env

+
+ Os.set_env(name, value) -> bool +

Sets an environment variable for this process (and any children it launches), overwriting any existing value. Returns true on success. Environment changes are process-local and non-deterministic host state — keep them out of the simulation.

+

Parameters

namethe variable name
valuethe value to set
+

Example

program SetEnv {
+  entry {
+    if Os.set_env("LUDIC_MODE", "fast") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-stderr_write.html b/os-stderr_write.html new file mode 100644 index 00000000..93ddde47 --- /dev/null +++ b/os-stderr_write.html @@ -0,0 +1,33 @@ + + + + + +Os.stderr_write — Ludic + + + + + + +
+
API Reference › Os › Os.stderr_write
+
+ method +

Os.stderr_write

+
+ Os.stderr_write(s) +

Writes a string to standard error verbatim. Standard error is the right stream for diagnostics, warnings, and usage messages: it stays separate from a tool's real output on standard out, so a caller can capture one without the other.

+

Parameters

sthe string to write
+

Example

program StderrWrite {
+  entry {
+    Os.stderr_write("warning: using defaults\n")
+    print(0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-stdout_write.html b/os-stdout_write.html new file mode 100644 index 00000000..3639c901 --- /dev/null +++ b/os-stdout_write.html @@ -0,0 +1,33 @@ + + + + + +Os.stdout_write — Ludic + + + + + + +
+
API Reference › Os › Os.stdout_write
+
+ method +

Os.stdout_write

+
+ Os.stdout_write(s) +

Writes a string to standard output verbatim — no trailing newline is added, unlike print. Use it for tool output where you control the exact bytes (progress, generated text, machine-readable lines).

+

Parameters

sthe string to write
+

Example

program StdoutWrite {
+  entry {
+    Os.stdout_write("hello")
+    Os.stdout_write("\n")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-temp_dir.html b/os-temp_dir.html new file mode 100644 index 00000000..340dff11 --- /dev/null +++ b/os-temp_dir.html @@ -0,0 +1,33 @@ + + + + + +Os.temp_dir — Ludic + + + + + + +
+
API Reference › Os › Os.temp_dir
+
+ method +

Os.temp_dir

+
+ Os.temp_dir() -> string +

The system temporary directory (from TMPDIR, falling back to /tmp) — for short-lived scratch files that need not survive a restart.

+ +

Example

program TempDir {
+  entry {
+    let dir = Os.temp_dir()
+    print(len(dir))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/os-unset_env.html b/os-unset_env.html new file mode 100644 index 00000000..42edbc50 --- /dev/null +++ b/os-unset_env.html @@ -0,0 +1,33 @@ + + + + + +Os.unset_env — Ludic + + + + + + +
+
API Reference › Os › Os.unset_env
+
+ method +

Os.unset_env

+
+ Os.unset_env(name) -> bool +

Removes an environment variable from this process. Returns true on success (including when the variable was already absent).

+

Parameters

namethe variable name
+

Example

program UnsetEnv {
+  entry {
+    Os.set_env("LUDIC_TMP", "1")
+    if Os.unset_env("LUDIC_TMP") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-base.html b/path-base.html new file mode 100644 index 00000000..5873f190 --- /dev/null +++ b/path-base.html @@ -0,0 +1,32 @@ + + + + + +Path.base — Ludic + + + + + + +
+
API Reference › Path › Path.base
+
+ method +

Path.base

+
+ Path.base(p) -> string +

Returns the final component — the file or last directory name — after the last /. A path with no separator is returned unchanged. The complement of dir.

+

Parameters

pthe path
+

Example

program Base {
+  entry {
+    if Path.base("mods/foo/data.json") == "data.json" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-dir.html b/path-dir.html new file mode 100644 index 00000000..d49dcc16 --- /dev/null +++ b/path-dir.html @@ -0,0 +1,32 @@ + + + + + +Path.dir — Ludic + + + + + + +
+
API Reference › Path › Path.dir
+
+ method +

Path.dir

+
+ Path.dir(p) -> string +

Returns everything before the last / — the containing directory. A path with no separator yields "." (the current directory); the root / stays /.

+

Parameters

pthe path
+

Example

program Dir {
+  entry {
+    if Path.dir("mods/foo/data.json") == "mods/foo" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-ext.html b/path-ext.html new file mode 100644 index 00000000..0758f4bf --- /dev/null +++ b/path-ext.html @@ -0,0 +1,32 @@ + + + + + +Path.ext — Ludic + + + + + + +
+
API Reference › Path › Path.ext
+
+ method +

Path.ext

+
+ Path.ext(p) -> string +

Returns the extension of the final component, including the leading dot (e.g. ".json"), or the empty string when there is none. A leading-dot name (a dotfile like .gitignore) has no extension.

+

Parameters

pthe path
+

Example

program Ext {
+  entry {
+    if Path.ext("hero.png") == ".png" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-join.html b/path-join.html new file mode 100644 index 00000000..d4cde4fd --- /dev/null +++ b/path-join.html @@ -0,0 +1,32 @@ + + + + + +Path.join — Ludic + + + + + + +
+
API Reference › Path › Path.join
+
+ method +

Path.join

+
+ Path.join(a, b) -> string +

Joins two path segments with a single /, inserting one only when needed (so join("a/", "b") and join("a", "b") both give a/b). If b is absolute it wins; if either side is empty the other is returned.

+

Parameters

athe leading path
bthe trailing path or filename
+

Example

program Join {
+  entry {
+    if Path.join("mods", "foo.ludic") == "mods/foo.ludic" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-normalize.html b/path-normalize.html new file mode 100644 index 00000000..06bbe79a --- /dev/null +++ b/path-normalize.html @@ -0,0 +1,32 @@ + + + + + +Path.normalize — Ludic + + + + + + +
+
API Reference › Path › Path.normalize
+
+ method +

Path.normalize

+
+ Path.normalize(p) -> string +

Canonicalizes a path lexically (without touching disk): duplicate separators collapse, . segments drop, and .. pops the preceding segment. An absolute path never rises above its root. The result is stable, so it is a good key for comparing or sandboxing paths.

+

Parameters

pthe path
+

Example

program Normalize {
+  entry {
+    if Path.normalize("a/./b/../c") == "a/c" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/path-stem.html b/path-stem.html new file mode 100644 index 00000000..1b0d2654 --- /dev/null +++ b/path-stem.html @@ -0,0 +1,32 @@ + + + + + +Path.stem — Ludic + + + + + + +
+
API Reference › Path › Path.stem
+
+ method +

Path.stem

+
+ Path.stem(p) -> string +

Returns the final component with its extension removed — the "stem". Useful for deriving an id or a sibling filename. stem("mods/foo/data.json") is "data".

+

Parameters

pthe path
+

Example

program Stem {
+  entry {
+    if Path.stem("data.json") == "data" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/percent-apply.html b/percent-apply.html new file mode 100644 index 00000000..1c4569fa --- /dev/null +++ b/percent-apply.html @@ -0,0 +1,32 @@ + + + + + +Percent.apply — Ludic + + + + + + +
+
API Reference › Percent › Percent.apply
+
+ method +

Percent.apply

+
+ Percent.apply(value, p) -> fixed +

Scales value by a clamped fraction p — for example, applying a damage-reduction percentage.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let dealt = Percent.apply(damage, 0.75)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/percent-clamp.html b/percent-clamp.html new file mode 100644 index 00000000..1f1c5601 --- /dev/null +++ b/percent-clamp.html @@ -0,0 +1,32 @@ + + + + + +Percent.clamp — Ludic + + + + + + +
+
API Reference › Percent › Percent.clamp
+
+ method +

Percent.clamp

+
+ Percent.clamp(v) -> fixed +

Pins any fixed into the range [0, 1] — a health fraction or volume that can never slip out of bounds.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let hp = Percent.clamp(health)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/percent-lerp.html b/percent-lerp.html new file mode 100644 index 00000000..76502e60 --- /dev/null +++ b/percent-lerp.html @@ -0,0 +1,32 @@ + + + + + +Percent.lerp — Ludic + + + + + + +
+
API Reference › Percent › Percent.lerp
+
+ method +

Percent.lerp

+
+ Percent.lerp(a, b, t) -> fixed +

Interpolates from a to b by t, clamping t to [0, 1] first so the result never overshoots the endpoints.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let x = Percent.lerp(startX, endX, progress)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/percent-of.html b/percent-of.html new file mode 100644 index 00000000..925dedeb --- /dev/null +++ b/percent-of.html @@ -0,0 +1,32 @@ + + + + + +Percent.of — Ludic + + + + + + +
+
API Reference › Percent › Percent.of
+
+ method +

Percent.of

+
+ Percent.of(num, den) -> fixed +

Forms the ratio num / den clamped to [0, 1] (0 when den is 0) — a filled-fraction from a part and a whole.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let filled = Percent.of(current, maximum)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-fixedupdate.html b/phase-fixedupdate.html new file mode 100644 index 00000000..51dd892f --- /dev/null +++ b/phase-fixedupdate.html @@ -0,0 +1,52 @@ + + + + + +FixedUpdate — Ludic + + + + + + +
+
API Reference › Phases › FixedUpdate
+
+ phase +

FixedUpdate

+
+ phase FixedUpdate +

FixedUpdate runs each frame after Input and before Update. It is the home for the deterministic core of the simulation — movement, collisions, physics — the logic you want to produce the identical result given the same inputs and RNG seed. The runtime groups Input, FixedUpdate, Update, and LateUpdate into the "sim" step (exposed together for replay, rollback, and headless tests), while Render is kept separate so drawing never affects the outcome. Put anything that must stay reproducible here and mark the handler @deterministic; put frame-dressing and non-critical logic in Update instead.

+ +

Example

program FixedUpdateExample {
+  property Position { column: int = 0, row: int = 0 }
+  property Velocity { delta_x: int = 0, delta_y: int = 0 }
+  model Projectile { Position, Velocity }
+
+  handler SpawnShot phase Start {
+    spawn Projectile { Position { column: 0, row: 8 };  Velocity { delta_x: 1, delta_y: 0 } }
+  }
+
+  @deterministic
+  handler AdvancePositions phase FixedUpdate {
+    for (current_position, current_velocity) in query [Position, Velocity] {
+      current_position.column = current_position.column + current_velocity.delta_x
+      current_position.row = current_position.row + current_velocity.delta_y
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (current_position) in query [Position, {Projectile}] {
+      Screen.fill_rectangle(x: current_position.column * 16, y: current_position.row * 16, width: 16, height: 16, color: Color.Crimson)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-input.html b/phase-input.html new file mode 100644 index 00000000..97b4a88e --- /dev/null +++ b/phase-input.html @@ -0,0 +1,46 @@ + + + + + +Input — Ludic + + + + + + +
+
API Reference › Phases › Input
+
+ phase +

Input

+
+ phase Input +

Input is the first phase of every frame. The runtime polls the keyboard just before it runs, so a handler … phase Input reads the current key with Input.key() and turns it into intent — a chosen direction, a menu selection, a pause toggle — usually by writing a program var that later phases act on. Keep it to reading input and recording decisions: do the actual movement and simulation in FixedUpdate/Update, and the drawing in Render. Because it runs before FixedUpdate, Update, LateUpdate, and Render, the intent it records is visible to every phase in the same frame.

+ +

Example

program InputExample {
+  const DIRECTION_LEFT: int = 0
+  const DIRECTION_RIGHT: int = 1
+  var facing: int = DIRECTION_RIGHT
+
+  handler ReadControls phase Input {
+    let pressed_key = Input.key()
+    if pressed_key == 'a' { facing = DIRECTION_LEFT }
+    if pressed_key == 'd' { facing = DIRECTION_RIGHT }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    var marker_x = 40
+    if facing == DIRECTION_RIGHT { marker_x = 200 }
+    Screen.fill_rectangle(x: marker_x, y: 100, width: 16, height: 16, color: Color.LimeGreen)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-lateupdate.html b/phase-lateupdate.html new file mode 100644 index 00000000..a22dbb52 --- /dev/null +++ b/phase-lateupdate.html @@ -0,0 +1,48 @@ + + + + + +LateUpdate — Ludic + + + + + + +
+
API Reference › Phases › LateUpdate
+
+ phase +

LateUpdate

+
+ phase LateUpdate +

LateUpdate runs once per frame, after every Update handler has finished and just before Render. Use it for logic that must observe the settled result of this frame's updates: following the camera to the player's new position, resolving queued despawns, or any end-of-step cleanup that would be wrong to do while other Update handlers are still moving models. The full per-frame order the engine runs is Input → FixedUpdate → Update → LateUpdate → Render, with Start running once at boot before any of them.

+ +

Example

program FollowCamera {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  var camera_column: int = 0
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 12, row: 0 } }
+  }
+
+  @Queries(these: [Position])
+  handler MovePlayer phase Update {
+    Position.column = Position.column + 1
+  }
+
+  # runs after MovePlayer, so the camera sees the final position
+  @Queries(these: [Position])
+  handler TrackPlayer phase LateUpdate {
+    camera_column = Position.column
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-overlay.html b/phase-overlay.html new file mode 100644 index 00000000..84805211 --- /dev/null +++ b/phase-overlay.html @@ -0,0 +1,53 @@ + + + + + +Overlay — Ludic + + + + + + +
+
API Reference › Phases › Overlay
+
+ phase +

Overlay

+
+ phase Overlay +

Overlay runs after Render and after every engine-owned drawing system (sprites, lights, the scene's shows menu), so a handler … phase Overlay paints on top of the finished world: health bars, score, prompts, a boss bar. With @ClearColor the engine owns the clear and the present, so an Overlay handler only draws — no Screen.clear, no Screen.show. Like Render, it should read the world and never change it.

+ +

Example

program OverlayExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+  var score: int = 0
+
+  handler PlacePlayer phase Start {
+    spawn Player { Position { column: 5, row: 6 } }
+  }
+
+  @ClearColor(Color.MidnightBlue)
+  handler DrawWorld phase Render {
+    for (current_position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: current_position.column * TILE_SIZE,
+        y: current_position.row * TILE_SIZE,
+        width: TILE_SIZE, height: TILE_SIZE, color: Color.LimeGreen)
+    }
+  }
+
+  handler DrawHud phase Overlay {
+    Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
+    Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-render.html b/phase-render.html new file mode 100644 index 00000000..b24e77b0 --- /dev/null +++ b/phase-render.html @@ -0,0 +1,51 @@ + + + + + +Render — Ludic + + + + + + +
+
API Reference › Phases › Render
+
+ phase +

Render

+
+ phase Render +

Render is the final phase of every frame, run after all the logic phases (Input, FixedUpdate, Update, LateUpdate) have finished. A handler … phase Render paints the frame with the Screen.* calls — start by clearing the surface with Screen.clear, draw everything back-to-front, and finish with a single Screen.show() to present the completed frame to the window. Keep Render free of game-state changes: it should read the world and draw it, not move things, so that the drawing never affects the simulation. When you use scenes, layers within a scene render in declaration order, so a HUD layer written after a world layer paints on top of it.

+ +

Example

program RenderExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+  var score: int = 0
+
+  handler PlacePlayer phase Start {
+    spawn Player { Position { column: 5, row: 6 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (current_position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: current_position.column * TILE_SIZE,
+        y: current_position.row * TILE_SIZE,
+        width: TILE_SIZE, height: TILE_SIZE, color: Color.LimeGreen)
+    }
+    Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
+    Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-start.html b/phase-start.html new file mode 100644 index 00000000..17c334b1 --- /dev/null +++ b/phase-start.html @@ -0,0 +1,47 @@ + + + + + +Start — Ludic + + + + + + +
+
API Reference › Phases › Start
+
+ phase +

Start

+
+ phase Start +

Start is the boot phase: a handler … phase Start runs exactly once, before the first frame and before the per-frame loop begins. Use it to build the world that everything else assumes — seed the RNG with Random.seed, set the tilemap size with Map.size, spawn the starting models, and initialise your program vars. It runs before any scene is entered, so it is the one phase a scene layer handler may not use; per-scene setup belongs in a scene's on enter block instead. Every other phase (Input, FixedUpdate, Update, LateUpdate, Render) then repeats each frame, but Start never runs again.

+ +

Example

program StartExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const GRID_WIDTH: int = 20
+  var score: int = 0
+
+  handler SetUpWorld phase Start {
+    Random.seed(value: 1)
+    Map.size(width: GRID_WIDTH, height: 15)
+    score = 0
+    spawn Player { Position { column: 10, row: 7 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 4, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/phase-update.html b/phase-update.html new file mode 100644 index 00000000..72ac152a --- /dev/null +++ b/phase-update.html @@ -0,0 +1,53 @@ + + + + + +Update — Ludic + + + + + + +
+
API Reference › Phases › Update
+
+ phase +

Update

+
+ phase Update +

Update is the everyday game-logic phase, run each frame after Input and FixedUpdate and before LateUpdate and Render. It is where most gameplay lives when you are not being strict about determinism: spawn waves on a timer, apply the intent that Input recorded, check win/lose conditions, advance animations. Reach for Update by default and reserve FixedUpdate for the simulation you specifically want reproducible. Because it runs after Input, the current frame's key press is already reflected in your program vars by the time Update reads them.

+ +

Example

program UpdateExample {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  const GRID_WIDTH: int = 20
+  var elapsed_frames: int = 0
+  var score: int = 0
+
+  handler SpawnWave phase Update {
+    elapsed_frames = elapsed_frames + 1
+    if elapsed_frames % 30 == 0 {
+      let spawn_column = Random.range(low: 0, high: GRID_WIDTH - 1)
+      spawn Enemy { Position { column: spawn_column, row: 0 } }
+      score = score + 1
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (current_position) in query [Position, {Enemy}] {
+      Screen.fill_rectangle(x: current_position.column * 16, y: current_position.row * 16, width: 16, height: 16, color: Color.Crimson)
+    }
+    Screen.draw_number(x: 4, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/pool-capacity.html b/pool-capacity.html new file mode 100644 index 00000000..a3ea48f7 --- /dev/null +++ b/pool-capacity.html @@ -0,0 +1,28 @@ + + + + + +Pool.capacity — Ludic + + + + + + +
+
API Reference › Pool › Pool.capacity
+
+ method +

Pool.capacity

+
+ Pool.capacity() -> int +

Returns the maximum number of live entities (the fixed entity-array size). Budget spawns against it — a horde/bullet-hell game keeps Pool.live under Pool.capacity().

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/pool-free.html b/pool-free.html new file mode 100644 index 00000000..176d5f6c --- /dev/null +++ b/pool-free.html @@ -0,0 +1,28 @@ + + + + + +Pool.free — Ludic + + + + + + +
+
API Reference › Pool › Pool.free
+
+ method +

Pool.free

+
+ Pool.free() -> int +

Returns how many freed entity slots are on the freelist, waiting to be reused by the next spawn. This is the pool depth — despawning bullets/enemies raises it, and the next wave draws them back down with no allocation.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/pool-live.html b/pool-live.html new file mode 100644 index 00000000..cde9740f --- /dev/null +++ b/pool-live.html @@ -0,0 +1,28 @@ + + + + + +Pool.live — Ludic + + + + + + +
+
API Reference › Pool › Pool.live
+
+ method +

Pool.live

+
+ Pool.live() -> int +

Returns the number of entities currently alive (reserved minus free). Despawning drops it and spawning raises it; a recycled slot does not change Pool.reserved.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/pool-reserved.html b/pool-reserved.html new file mode 100644 index 00000000..eb82470a --- /dev/null +++ b/pool-reserved.html @@ -0,0 +1,28 @@ + + + + + +Pool.reserved — Ludic + + + + + + +
+
API Reference › Pool › Pool.reserved
+
+ method +

Pool.reserved

+
+ Pool.reserved() -> int +

Returns the high-water mark — how many entity slots have ever been allocated. It only grows when a spawn finds the freelist empty; a spawn that reuses a freed slot leaves it unchanged, so a steady-state spawn/despawn loop keeps reserved flat (the proof that slots are pooled, not reallocated).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/prefab-spawn.html b/prefab-spawn.html new file mode 100644 index 00000000..0642127f --- /dev/null +++ b/prefab-spawn.html @@ -0,0 +1,30 @@ + + + + + +Prefab.spawn — Ludic + + + + + + +
+
API Reference › Prefab › Prefab.spawn
+
+ method +

Prefab.spawn

+
+ Prefab.spawn(name: s) -> entity +

Spawns the prefab whose name matches the string, with all its presets, and returns the new entity — or -1 when no prefab has that name. Set what varies per spawn afterwards through Prop.of(entity).

+ +

Example

# doc-check: skip — illustrative
+let e = Prefab.spawn(name: roster.roll())
+Position.of(e).x = tile.x * 16
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/prefab-spawn_at.html b/prefab-spawn_at.html new file mode 100644 index 00000000..61848eb6 --- /dev/null +++ b/prefab-spawn_at.html @@ -0,0 +1,29 @@ + + + + + +Prefab.spawn_at — Ludic + + + + + + +
+
API Reference › Prefab › Prefab.spawn_at
+
+ method +

Prefab.spawn_at

+
+ Prefab.spawn_at(name: s, at: IVec2) -> entity +

Prefab.spawn followed by writing the new entity's Position — the common case in one call.

+ +

Example

# doc-check: skip — illustrative
+let foe = Prefab.spawn_at(name: "Grunt", at: IVec2.make(60, 60))
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/process-free.html b/process-free.html new file mode 100644 index 00000000..654576e9 --- /dev/null +++ b/process-free.html @@ -0,0 +1,35 @@ + + + + + +Process.free — Ludic + + + + + + +
+
API Reference › Process › Process.free
+
+ method +

Process.free

+
+ Process.free(handle) -> void +

Releases the handle so it can be reused. It does not stop a child that is still running — use Process.kill first for that; such a child keeps running and is reaped by the runtime when it ends, so it never lingers as a zombie. After free, polling the old handle answers 255.

+ +

Example

program Demo {
+  entry {
+    let h = Process.spawn("/bin/sh", ["-c", "exit 0"])
+    var code = Process.poll(h)
+    while code == -1 { code = Process.poll(h) }
+    Process.free(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/process-kill.html b/process-kill.html new file mode 100644 index 00000000..2c5395cc --- /dev/null +++ b/process-kill.html @@ -0,0 +1,33 @@ + + + + + +Process.kill — Ludic + + + + + + +
+
API Reference › Process › Process.kill
+
+ method +

Process.kill

+
+ Process.kill(handle) -> void +

Ends the child at once — SIGKILL on macOS, TerminateProcess on Windows — with no chance for it to save anything. A later Process.poll answers 137 once it has gone. Does nothing for a child that has already ended, or a handle that is not one.

+ +

Example

program Demo {
+  entry {
+    let h = Process.spawn("/bin/sleep", ["30"])
+    Process.kill(h)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/process-poll.html b/process-poll.html new file mode 100644 index 00000000..28bbf136 --- /dev/null +++ b/process-poll.html @@ -0,0 +1,40 @@ + + + + + +Process.poll — Ludic + + + + + + +
+
API Reference › Process › Process.poll
+
+ method +

Process.poll

+
+ Process.poll(handle) -> int +

Returns -1 while the child is still running and its exit code once it has ended; it never waits. The code is kept, so asking again gives the same answer until Process.free.

A child ended by a signal (macOS) answers 128 plus the signal number, as a shell reports it — 137 after Process.kill, on both platforms. On Windows the code is the child's DWORD exit code as an int, so a crash such as 0xC0000005 arrives as a negative number (test for != 0, not > 0); a code of 0xFFFFFFFF is reported as 255 so that -1 always means "still running". A handle that is not one answers 255.

+ +

Example

program Demo {
+  var game: int = -1
+  handler Watch phase Update {
+    if game >= 0 {
+      let code = Process.poll(handle: game)
+      if code >= 0 or code < -1 {
+        if code != 0 { print("the game crashed") }
+        Process.free(handle: game)
+        game = -1
+      }
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/process-spawn.html b/process-spawn.html new file mode 100644 index 00000000..5ac228ec --- /dev/null +++ b/process-spawn.html @@ -0,0 +1,33 @@ + + + + + +Process.spawn — Ludic + + + + + + +
+
API Reference › Process › Process.spawn
+
+ method +

Process.spawn

+
+ Process.spawn(path, args) -> int +

Starts the program at path with the arguments in args (a []string; the child's argv[0] is path) and returns a handle >= 0 straight away, or -1 when the program could not be started — a missing file, a file that is not executable, or all 16 handles in use.

No shell is involved, so every argument reaches the child exactly as written: spaces, quotes and $ are not interpreted. The child inherits this process's environment and working directory. On Windows the arguments are joined into one command line quoted so the child's C runtime splits it back into the same list, the path and arguments cross as UTF-8 and are converted to UTF-16, and the child is started without a console window (a GUI program is unaffected; a console program runs hidden). Use an absolute path: there is no PATH search.

On macOS the child writes to this process's standard output and error. On Windows it inherits no handles, so a console child's output goes nowhere; nothing is captured on either platform.

See Process.poll for the exit code.

+ +

Example

program Demo {
+  entry {
+    let h = Process.spawn("/bin/sh", ["-c", "exit 3"])
+    print(h >= 0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/promise-all.html b/promise-all.html new file mode 100644 index 00000000..cd84c702 --- /dev/null +++ b/promise-all.html @@ -0,0 +1,31 @@ + + + + + +Promise.all — Ludic + + + + + + +
+
API Reference › Promise › Promise.all
+
+ method +

Promise.all

+
+ Promise.all(handles) -> Job +

Succeeds when every member succeeds.

+ +

Example

let hs = new []int
+push(hs, a)
+push(hs, b)
+let loaded = Promise.all(hs)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/promise-all_done.html b/promise-all_done.html new file mode 100644 index 00000000..dd72d619 --- /dev/null +++ b/promise-all_done.html @@ -0,0 +1,31 @@ + + + + + +Promise.all_done — Ludic + + + + + + +
+
API Reference › Promise › Promise.all_done
+
+ method +

Promise.all_done

+
+ Promise.all_done(handles) -> bool +

Have all members resolved?

+ +

Example

let hs = new []int
+push(hs, a)
+push(hs, b)
+let ready = Promise.all_done(hs)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/promise-count_done.html b/promise-count_done.html new file mode 100644 index 00000000..4c214f82 --- /dev/null +++ b/promise-count_done.html @@ -0,0 +1,31 @@ + + + + + +Promise.count_done — Ludic + + + + + + +
+
API Reference › Promise › Promise.count_done
+
+ method +

Promise.count_done

+
+ Promise.count_done(handles) -> int +

How many members have resolved.

+ +

Example

let hs = new []int
+push(hs, a)
+push(hs, b)
+let progress = Promise.count_done(hs)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/promise-race.html b/promise-race.html new file mode 100644 index 00000000..df65ba7b --- /dev/null +++ b/promise-race.html @@ -0,0 +1,31 @@ + + + + + +Promise.race — Ludic + + + + + + +
+
API Reference › Promise › Promise.race
+
+ method +

Promise.race

+
+ Promise.race(handles) -> Job +

Succeeds when the first member does.

+ +

Example

let hs = new []int
+push(hs, a)
+push(hs, b)
+let first = Promise.race(hs)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/query-count.html b/query-count.html new file mode 100644 index 00000000..d4765b2d --- /dev/null +++ b/query-count.html @@ -0,0 +1,37 @@ + + + + + +Query.count — Ludic + + + + + + +
+
API Reference › Query › Query.count
+
+ method +

Query.count

+
+ Query.count(prop) -> int +

Returns the number of live entities that carry the property prop (a property id from World.prop_id). A quick population count — how many enemies are alive, how many pickups remain — without writing a query loop.

+

Parameters

propa property id (from World.prop_id("Name"))
+

Example

program Demo {
+  property Enemy { hp: int = 0 }
+  model Slime { Enemy }
+  entry {
+    spawn Slime { Enemy { hp: 3 } }
+    spawn Slime { Enemy { hp: 3 } }
+    let E = World.prop_id("Enemy")
+    print(Query.count(E))   # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/query-first.html b/query-first.html new file mode 100644 index 00000000..20038d3e --- /dev/null +++ b/query-first.html @@ -0,0 +1,38 @@ + + + + + +Query.first — Ludic + + + + + + +
+
API Reference › Query › Query.first
+
+ method +

Query.first

+
+ Query.first(prop) -> int +

Returns the first (lowest entity id) live entity that carries prop, or -1 if none do. Handy for a singleton-ish lookup — the player, the current boss, the one active portal — and to test existence (Query.first(prop) >= 0). Read its fields with World.get.

+

Parameters

propa property id (from World.prop_id("Name"))
+

Example

program Demo {
+  property Player { hp: int = 0 }
+  model Hero { Player }
+  entry {
+    spawn Hero { Player { hp: 100 } }
+    let P  = World.prop_id("Player")
+    let hp = World.field_id(P, "hp")
+    let e  = Query.first(P)
+    if e >= 0 { print(World.get(e, P, hp)) }   # 100
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/query-nearest.html b/query-nearest.html new file mode 100644 index 00000000..4501d3f3 --- /dev/null +++ b/query-nearest.html @@ -0,0 +1,42 @@ + + + + + +Query.nearest — Ludic + + + + + + +
+
API Reference › Query › Query.nearest
+
+ method +

Query.nearest

+
+ Query.nearest(prop, pos, x_field, y_field, x, y) -> int +

Returns the live entity carrying prop whose position is closest to (x, y), or -1 if none. Position comes from property pos at its integer fields x_field and y_field — pass the same id for prop and pos to query the coordinate component itself, or a separate tag for prop to mean "nearest Enemy". Distance is compared by exact squared integer distance (no square root), and ties go to the lower entity id, so the result is fully deterministic. The go-to for target acquisition, "interact with the closest thing", or nearest-neighbour AI.

+

Parameters

propthe property that filters which entities are considered
posthe property holding the coordinates
+

Example

program Demo {
+  property Position { col: int = 0, row: int = 0 }
+  property Enemy { hp: int = 0 }
+  model Slime { Position, Enemy }
+  entry {
+    spawn Slime { Position { col: 2, row: 0 }, Enemy { hp: 3 } }
+    spawn Slime { Position { col: 9, row: 0 }, Enemy { hp: 3 } }
+    let E  = World.prop_id("Enemy")
+    let P  = World.prop_id("Position")
+    let cx = World.field_id(P, "col")
+    let cy = World.field_id(P, "row")
+    let e  = Query.nearest(E, P, cx, cy, 0, 0)   # the enemy at col 2
+    print(World.get(e, P, cx))                    # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/query-within.html b/query-within.html new file mode 100644 index 00000000..8799c69e --- /dev/null +++ b/query-within.html @@ -0,0 +1,42 @@ + + + + + +Query.within — Ludic + + + + + + +
+
API Reference › Query › Query.within
+
+ method +

Query.within

+
+ Query.within(prop, pos, x, y, radius, x_field, y_field) -> []int +

Returns the live entities carrying prop whose position is within radius of (x, y), as a []int of entity ids in ascending id order — index it with len / [i]. Position is read from property pos at x_field/y_field, exactly as in Query.nearest. The boundary is inclusive (distance <= radius), measured by exact integer distance. Use it for area-of-effect damage, proximity triggers, flocking neighbours, or "what's near the cursor".

+

Parameters

propthe property that filters which entities are considered
posthe property holding the coordinates
radiusthe inclusive radius
+

Example

program Demo {
+  property Position { col: int = 0, row: int = 0 }
+  property Unit { hp: int = 0 }
+  model Soldier { Position, Unit }
+  entry {
+    spawn Soldier { Position { col: 1, row: 1 }, Unit { hp: 5 } }
+    spawn Soldier { Position { col: 8, row: 8 }, Unit { hp: 5 } }
+    let U  = World.prop_id("Unit")
+    let P  = World.prop_id("Position")
+    let cx = World.field_id(P, "col")
+    let cy = World.field_id(P, "row")
+    let hit = Query.within(U, P, 0, 0, 3, cx, cy)   # just the (1,1) soldier
+    print(len(hit))                                  # 1
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-chance.html b/random-chance.html new file mode 100644 index 00000000..12c3b29d --- /dev/null +++ b/random-chance.html @@ -0,0 +1,59 @@ + + + + + +Random.chance — Ludic + + + + + + +
+
API Reference › Random › Random.chance
+
+ method +

Random.chance

+
+ Random.chance(percent) -> bool +

Returns true roughly percent times out of a hundred, and false otherwise — a convenient shorthand for "this happens X% of the time". Use it to gate random events such as spawning an enemy on a given frame, dropping loot, or triggering a rare animation. Passing 0 never fires and 100 always fires. Like the rest of the Random namespace it is driven by the seeded generator, so seed it with Random.seed for reproducible behavior.

+

Parameters

percentthe probability of returning true, from 0 to 100
+

Example

program SpawnWave {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  const GRID_WIDTH: int = 20
+  const TILE_SIZE: int = 16
+
+  handler SeedRandom phase Start {
+    Random.seed(value: 3)
+  }
+
+  handler MaybeSpawn phase Update {
+    if Random.chance(percent: 10) {
+      spawn Enemy {
+        Position { column: Random.range(low: 0, high: GRID_WIDTH - 1), row: 0 }
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Enemy}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.Crimson)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-int.html b/random-int.html new file mode 100644 index 00000000..1c659b54 --- /dev/null +++ b/random-int.html @@ -0,0 +1,35 @@ + + + + + +Random.int — Ludic + + + + + + +
+
API Reference › Random › Random.int
+
+ method +

Random.int

+
+ Random.int(max) -> int +

Returns a deterministic integer from 0 up to but not including max. Use it to pick a random index into a list of length max. Returns 0 when max is not positive.

+ +

Example

program Demo {
+  handler Seed phase Start { Random.seed(value: 7) }
+  handler Step phase Update {
+    let idx = Random.int(list_count)
+  }
+}
+}
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-range.html b/random-range.html new file mode 100644 index 00000000..8efd95ff --- /dev/null +++ b/random-range.html @@ -0,0 +1,60 @@ + + + + + +Random.range — Ludic + + + + + + +
+
API Reference › Random › Random.range
+
+ method +

Random.range

+
+ Random.range(low, high) -> int +

Returns a random integer between low and high, inclusive of both ends — so Random.range(low: 0, high: 3) can return 0, 1, 2, or 3. Use it to place things randomly on a grid, pick from a list of options by index, or add jitter to positions and timers. The sequence is deterministic for a given seed, so call Random.seed first if you need reproducible runs. To pick a random column across a grid of width GRID_WIDTH, pass high: GRID_WIDTH - 1, since the last valid index is one less than the count.

+

Parameters

lowthe smallest value that can be returned (inclusive)
highthe largest value that can be returned (inclusive)
+

Example

program ScatterPickups {
+  property Position { column: int = 0, row: int = 0 }
+  model Pickup { Position }
+
+  const GRID_WIDTH: int = 20
+  const GRID_HEIGHT: int = 15
+  const TILE_SIZE: int = 16
+
+  handler SeedPickups phase Start {
+    Random.seed(value: 7)
+    for spawn_index in 0 .. 5 {
+      spawn Pickup {
+        Position {
+          column: Random.range(low: 0, high: GRID_WIDTH - 1),
+          row: Random.range(low: 0, high: GRID_HEIGHT - 1)
+        }
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Pickup}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.Gold)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-seed.html b/random-seed.html new file mode 100644 index 00000000..f7ffe67c --- /dev/null +++ b/random-seed.html @@ -0,0 +1,60 @@ + + + + + +Random.seed — Ludic + + + + + + +
+
API Reference › Random › Random.seed
+
+ method +

Random.seed

+
+ Random.seed(value) +

Sets the starting state of the random-number generator. Every subsequent call to Random.range and Random.chance then follows a fixed, repeatable sequence for that seed, so seeding with the same value makes two runs produce identical randomness — invaluable for tests, replays, and debugging. Seed once, typically in a Start-phase handler before you draw any random numbers. Use different seed values (for example a frame count or a level number) when you want variety between runs.

+

Parameters

valuethe seed; the same value reproduces the same sequence
+

Example

program ReproducibleField {
+  property Position { column: int = 0, row: int = 0 }
+  model Star { Position }
+
+  const GRID_WIDTH: int = 20
+  const GRID_HEIGHT: int = 15
+  const TILE_SIZE: int = 16
+
+  handler SeedField phase Start {
+    Random.seed(value: 42)
+    for spawn_index in 0 .. 8 {
+      spawn Star {
+        Position {
+          column: Random.range(low: 0, high: GRID_WIDTH - 1),
+          row: Random.range(low: 0, high: GRID_HEIGHT - 1)
+        }
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Star}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.White)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-sign.html b/random-sign.html new file mode 100644 index 00000000..bdd95394 --- /dev/null +++ b/random-sign.html @@ -0,0 +1,35 @@ + + + + + +Random.sign — Ludic + + + + + + +
+
API Reference › Random › Random.sign
+
+ method +

Random.sign

+
+ Random.sign() -> int +

Returns 1 or -1 with equal probability — a deterministic coin flip for choosing a direction or jitter sign.

+ +

Example

program Demo {
+  handler Seed phase Start { Random.seed(value: 7) }
+  handler Step phase Update {
+    let dir = Random.sign()
+  }
+}
+}
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-value.html b/random-value.html new file mode 100644 index 00000000..79cfaa8b --- /dev/null +++ b/random-value.html @@ -0,0 +1,35 @@ + + + + + +Random.value — Ludic + + + + + + +
+
API Reference › Random › Random.value
+
+ method +

Random.value

+
+ Random.value() -> fixed +

Returns a deterministic fixed-point value in 0.0..1.0 (exclusive of 1). Multiply it into a range, or compare it against a probability. The sequence is fixed for a given Random.seed.

+ +

Example

program Demo {
+  handler Seed phase Start { Random.seed(value: 7) }
+  handler Step phase Update {
+    let t = Random.value()
+  }
+}
+}
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/random-weighted.html b/random-weighted.html new file mode 100644 index 00000000..e7b4c529 --- /dev/null +++ b/random-weighted.html @@ -0,0 +1,29 @@ + + + + + +Random.weighted — Ludic + + + + + + +
+
API Reference › Random › Random.weighted
+
+ method +

Random.weighted

+
+ Random.weighted(weights: []int) -> int +

Picks an index into weights with probability proportional to the weight (a weight of 0 is never picked); -1 when every weight is 0. Deterministic, from the seeded RNG. A loot table, a spawn roster, a random event.

+ +

Example

# doc-check: skip — illustrative
+let pick = roster[Random.weighted(weights: weights)]
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-bottom.html b/rect-bottom.html new file mode 100644 index 00000000..95bee15a --- /dev/null +++ b/rect-bottom.html @@ -0,0 +1,32 @@ + + + + + +Rect.bottom — Ludic + + + + + + +
+
API Reference › Rect › Rect.bottom
+
+ method +

Rect.bottom

+
+ Rect.bottom(r) -> fixed +

Returns the bottom edge, y + h — the y coordinate just below the rectangle.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let base = Rect.bottom(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-center.html b/rect-center.html new file mode 100644 index 00000000..48971a2b --- /dev/null +++ b/rect-center.html @@ -0,0 +1,32 @@ + + + + + +Rect.center — Ludic + + + + + + +
+
API Reference › Rect › Rect.center
+
+ method +

Rect.center

+
+ Rect.center(r) -> Vector +

Returns the center point (x + w/2, y + h/2) as a Vector — the anchor you want when placing a label or spawning at the middle of a box.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let mid = Rect.center(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-contains.html b/rect-contains.html new file mode 100644 index 00000000..e9722542 --- /dev/null +++ b/rect-contains.html @@ -0,0 +1,32 @@ + + + + + +Rect.contains — Ludic + + + + + + +
+
API Reference › Rect › Rect.contains
+
+ method +

Rect.contains

+
+ Rect.contains(r, px, py) -> bool +

Tests whether the point (px, py) falls inside the rectangle. The left and top edges are inclusive; the right and bottom edges are exclusive, so adjacent rectangles tile without overlap. The classic use is a mouse-in-button hit test.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Rect.contains(button, mx, my) { press() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-h.html b/rect-h.html new file mode 100644 index 00000000..1caeeec6 --- /dev/null +++ b/rect-h.html @@ -0,0 +1,32 @@ + + + + + +Rect.h — Ludic + + + + + + +
+
API Reference › Rect › Rect.h
+
+ method +

Rect.h

+
+ Rect.h(r) -> fixed +

Reads the height of the rectangle.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let height = Rect.h(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-intersects.html b/rect-intersects.html new file mode 100644 index 00000000..87b1e012 --- /dev/null +++ b/rect-intersects.html @@ -0,0 +1,32 @@ + + + + + +Rect.intersects — Ludic + + + + + + +
+
API Reference › Rect › Rect.intersects
+
+ method +

Rect.intersects

+
+ Rect.intersects(a, b) -> bool +

Tests whether two rectangles overlap (axis-aligned bounding-box test). Touching edges do not count as overlapping. This is the cheap broad-phase check before any finer collision work.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Rect.intersects(player, hazard) { hurt() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-make.html b/rect-make.html new file mode 100644 index 00000000..d0aa188c --- /dev/null +++ b/rect-make.html @@ -0,0 +1,32 @@ + + + + + +Rect.make — Ludic + + + + + + +
+
API Reference › Rect › Rect.make
+
+ method +

Rect.make

+
+ Rect.make(x, y, w, h) -> Rect +

Builds a Rect from its top-left corner (x, y) and size (w, h), all fixed. Read the parts back with Rect.x / Rect.y / Rect.w / Rect.h.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let hud = Rect.make(8.0, 8.0, 96.0, 16.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-right.html b/rect-right.html new file mode 100644 index 00000000..af2688de --- /dev/null +++ b/rect-right.html @@ -0,0 +1,32 @@ + + + + + +Rect.right — Ludic + + + + + + +
+
API Reference › Rect › Rect.right
+
+ method +

Rect.right

+
+ Rect.right(r) -> fixed +

Returns the right edge, x + w — the x coordinate just past the rectangle. Handy for anchoring something to a box's right side.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let edge = Rect.right(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-w.html b/rect-w.html new file mode 100644 index 00000000..82204847 --- /dev/null +++ b/rect-w.html @@ -0,0 +1,32 @@ + + + + + +Rect.w — Ludic + + + + + + +
+
API Reference › Rect › Rect.w
+
+ method +

Rect.w

+
+ Rect.w(r) -> fixed +

Reads the width of the rectangle.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let width = Rect.w(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-x.html b/rect-x.html new file mode 100644 index 00000000..32633416 --- /dev/null +++ b/rect-x.html @@ -0,0 +1,32 @@ + + + + + +Rect.x — Ludic + + + + + + +
+
API Reference › Rect › Rect.x
+
+ method +

Rect.x

+
+ Rect.x(r) -> fixed +

Reads the left edge — the x position of the rectangle's top-left corner.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let left = Rect.x(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/rect-y.html b/rect-y.html new file mode 100644 index 00000000..beaf9b66 --- /dev/null +++ b/rect-y.html @@ -0,0 +1,32 @@ + + + + + +Rect.y — Ludic + + + + + + +
+
API Reference › Rect › Rect.y
+
+ method +

Rect.y

+
+ Rect.y(r) -> fixed +

Reads the top edge — the y position of the rectangle's top-left corner.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let top = Rect.y(hud)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-apply.html b/reflect-apply.html new file mode 100644 index 00000000..bcb9c273 --- /dev/null +++ b/reflect-apply.html @@ -0,0 +1,28 @@ + + + + + +Reflect.apply — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.apply
+
+ method +

Reflect.apply

+
+ Reflect.apply(entity, value) -> int +

The inverse of Reflect.serialize: writes the fields of a value-tree value back into a live entity, and returns how many were set. Only components the entity already has and fields the schema knows are applied; reserved "@"-keys (like "@kind") and unknown names are skipped, so a partial or older save loads cleanly. Feed it the output of Json.parse to load a save.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-field.html b/reflect-field.html new file mode 100644 index 00000000..c9030403 --- /dev/null +++ b/reflect-field.html @@ -0,0 +1,36 @@ + + + + + +Reflect.field — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.field
+
+ method +

Reflect.field

+
+ Reflect.field(prop, name) -> int +

Resolves a field name within property prop to its field id (index), or -1 if the property has no such field. Pair it with Reflect.get/Reflect.set to read or write that field.

+

Parameters

propa property id (from Reflect.prop)
namethe field name
+

Example

program Demo {
+  property Health { hp: int = 0, max: int = 0 }
+  model Unit { Health }
+  entry {
+    let H  = Reflect.prop("Health")
+    let mx = Reflect.field(H, "max")
+    print(mx)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-field_count.html b/reflect-field_count.html new file mode 100644 index 00000000..7c490394 --- /dev/null +++ b/reflect-field_count.html @@ -0,0 +1,34 @@ + + + + + +Reflect.field_count — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.field_count
+
+ method +

Reflect.field_count

+
+ Reflect.field_count(prop) -> int +

Returns the number of fields in property prop. Walk 0 .. Reflect.field_count(prop) with Reflect.field_name and Reflect.field_type to enumerate a component's shape.

+

Parameters

propa property id (from Reflect.prop)
+

Example

program Demo {
+  property Health { hp: int = 0, max: int = 0 }
+  model Unit { Health }
+  entry {
+    print(Reflect.field_count(Reflect.prop("Health")))   # 2
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-field_name.html b/reflect-field_name.html new file mode 100644 index 00000000..b7b0564e --- /dev/null +++ b/reflect-field_name.html @@ -0,0 +1,35 @@ + + + + + +Reflect.field_name — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.field_name
+
+ method +

Reflect.field_name

+
+ Reflect.field_name(prop, index) -> string +

Returns the name of the field at index within property prop (0 .. Reflect.field_count(prop)), or the empty string if out of range. The key half of a generic serializer or inspector row.

+

Parameters

propa property id (from Reflect.prop)
indexthe field index
+

Example

program Demo {
+  property Health { hp: int = 0, max: int = 0 }
+  model Unit { Health }
+  entry {
+    let H = Reflect.prop("Health")
+    print(Reflect.field_name(H, 0))   # hp
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-field_type.html b/reflect-field_type.html new file mode 100644 index 00000000..dec7891c --- /dev/null +++ b/reflect-field_type.html @@ -0,0 +1,35 @@ + + + + + +Reflect.field_type — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.field_type
+
+ method +

Reflect.field_type

+
+ Reflect.field_type(prop, index) -> string +

Returns the declared type name of the field at index within property prop — "int", "fixed", "bool", and so on. A serializer uses it to format each value correctly, and an inspector uses it to pick the right editor widget.

+

Parameters

propa property id (from Reflect.prop)
indexthe field index
+

Example

program Demo {
+  property Velocity { dx: fixed = 0.0, dy: fixed = 0.0 }
+  model Mover { Velocity }
+  entry {
+    let V = Reflect.prop("Velocity")
+    print(Reflect.field_type(V, 0))   # fixed
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-get.html b/reflect-get.html new file mode 100644 index 00000000..33313ee1 --- /dev/null +++ b/reflect-get.html @@ -0,0 +1,36 @@ + + + + + +Reflect.get — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.get
+
+ method +

Reflect.get

+
+ Reflect.get(entity, prop, field) -> int +

Reads the value of field field of property prop on entity, as an int. A fixed field comes back as its raw Q16.16 bits (the same integer fixed stores), so a generic reader can move it without interpreting it. Same as World.get, under the reflection namespace.

+

Parameters

entitythe entity to read
propa property id (from Reflect.prop)
fielda field id (from Reflect.field)
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    spawn Unit { Health { hp: 7 } }
+    let H = Reflect.prop("Health")
+    print(Reflect.get(0, H, Reflect.field(H, "hp")))   # 7
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-has.html b/reflect-has.html new file mode 100644 index 00000000..f2b42cb4 --- /dev/null +++ b/reflect-has.html @@ -0,0 +1,35 @@ + + + + + +Reflect.has — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.has
+
+ method +

Reflect.has

+
+ Reflect.has(entity, prop) -> bool +

Returns whether entity carries property prop. A generic walker checks it before reading a component's fields, so it visits only the components an entity actually has.

+

Parameters

entitythe entity to test
propa property id (from Reflect.prop)
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    spawn Unit { Health { hp: 7 } }
+    if Reflect.has(0, Reflect.prop("Health")) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-kind.html b/reflect-kind.html new file mode 100644 index 00000000..60d42a19 --- /dev/null +++ b/reflect-kind.html @@ -0,0 +1,35 @@ + + + + + +Reflect.kind — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.kind
+
+ method +

Reflect.kind

+
+ Reflect.kind(entity) -> int +

Returns the model (archetype) id of entity — which kind of thing it is. Compare it against Reflect.model to branch on an entity's type, or use it to label a save record. Same as World.kind, under the reflection namespace.

+

Parameters

entitythe entity to identify
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    spawn Unit { Health { hp: 7 } }
+    if Reflect.kind(0) == Reflect.model("Unit") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-model.html b/reflect-model.html new file mode 100644 index 00000000..488804e2 --- /dev/null +++ b/reflect-model.html @@ -0,0 +1,34 @@ + + + + + +Reflect.model — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.model
+
+ method +

Reflect.model

+
+ Reflect.model(name) -> int +

Resolves a model (archetype) name to its id, or -1 if no such model exists. The counterpart to Reflect.kind: resolve the name once, then compare entities' kinds against it.

+

Parameters

namethe model name
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    print(Reflect.model("Unit"))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-prop.html b/reflect-prop.html new file mode 100644 index 00000000..17aacd9d --- /dev/null +++ b/reflect-prop.html @@ -0,0 +1,35 @@ + + + + + +Reflect.prop — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.prop
+
+ method +

Reflect.prop

+
+ Reflect.prop(name) -> int +

Resolves a property name to its stable id, or -1 if no such property exists. The id is what every other Reflect.* (and Query) call takes. Same as World.prop_id, under the reflection namespace.

+

Parameters

namethe property name
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    let H = Reflect.prop("Health")
+    print(H)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-prop_count.html b/reflect-prop_count.html new file mode 100644 index 00000000..8e5103e9 --- /dev/null +++ b/reflect-prop_count.html @@ -0,0 +1,34 @@ + + + + + +Reflect.prop_count — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.prop_count
+
+ method +

Reflect.prop_count

+
+ Reflect.prop_count() -> int +

Returns the number of properties in the world — every declared property, plus any the runtime or a mod registered. Walk 0 .. Reflect.prop_count() with Reflect.prop_name to enumerate the whole schema.

+ +

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    print(Reflect.prop_count())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-prop_name.html b/reflect-prop_name.html new file mode 100644 index 00000000..2464d832 --- /dev/null +++ b/reflect-prop_name.html @@ -0,0 +1,39 @@ + + + + + +Reflect.prop_name — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.prop_name
+
+ method +

Reflect.prop_name

+
+ Reflect.prop_name(index) -> string +

Returns the name of the property at index (0 .. Reflect.prop_count()), or the empty string if out of range. The reverse of Reflect.prop — together they let a generic tool list every component by name.

+

Parameters

indexthe property index
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    var p = 0
+    let n = Reflect.prop_count()
+    while p < n {
+      print(Reflect.prop_name(p))
+      p = p + 1
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-serialize.html b/reflect-serialize.html new file mode 100644 index 00000000..6bc346ab --- /dev/null +++ b/reflect-serialize.html @@ -0,0 +1,28 @@ + + + + + +Reflect.serialize — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.serialize
+
+ method +

Reflect.serialize

+
+ Reflect.serialize(entity) -> value +

Walks every component the entity carries and every field of each into a generic object value tree: one member per component, each a sub-object of its fields. A fixed field becomes a fixed node, everything else an int node, so a serialize → apply round-trip is bit-exact; the model id rides along under the reserved "@kind" key. Pair it with Json.encode for a one-call save. It names no component or field, so it works over any schema — the pattern auto-save and debug inspectors use.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/reflect-set.html b/reflect-set.html new file mode 100644 index 00000000..28053241 --- /dev/null +++ b/reflect-set.html @@ -0,0 +1,37 @@ + + + + + +Reflect.set — Ludic + + + + + + +
+
API Reference › Reflect › Reflect.set
+
+ method +

Reflect.set

+
+ Reflect.set(entity, prop, field, value) +

Writes value into field field of property prop on entity. For a fixed field, pass the raw Q16.16 bits. This is how a generic loader restores saved state and how a debug overlay applies an edit. Same as World.set, under the reflection namespace.

+

Parameters

entitythe entity to modify
propa property id (from Reflect.prop)
fielda field id (from Reflect.field)
valuethe integer value to store
+

Example

program Demo {
+  property Health { hp: int = 0 }
+  model Unit { Health }
+  entry {
+    spawn Unit { Health { hp: 7 } }
+    let H = Reflect.prop("Health")
+    Reflect.set(0, H, Reflect.field(H, "hp"), 99)
+    print(Reflect.get(0, H, 0))   # 99
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-compile.html b/regex-compile.html new file mode 100644 index 00000000..f76072b3 --- /dev/null +++ b/regex-compile.html @@ -0,0 +1,33 @@ + + + + + +Regex.compile — Ludic + + + + + + +
+
API Reference › Regex › Regex.compile
+
+ method +

Regex.compile

+
+ Regex.compile(pattern) -> Regex +

Compiles pattern to a reusable Regex and returns it, or null if the pattern is malformed — so a bad pattern is a value you can check, never a crash. Compiling once and reusing with Regex.test / Regex.exec avoids re-parsing the pattern every call, which matters in a hot loop.

+

Parameters

patternthe regular expression source
+

Example

program Demo {
+  entry {
+    let re = Regex.compile("^\\d{4}-\\d{2}-\\d{2}$")
+    if Regex.test("2026-08-31", re) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-end.html b/regex-end.html new file mode 100644 index 00000000..8516e250 --- /dev/null +++ b/regex-end.html @@ -0,0 +1,33 @@ + + + + + +Regex.end — Ludic + + + + + + +
+
API Reference › Regex › Regex.end
+
+ method +

Regex.end

+
+ Regex.end(match, n) -> int +

Returns the byte offset just past the end of group n of match, or -1 if the group did not participate. Feed Regex.end(m, 0) back into Regex.next to walk every match.

+

Parameters

matcha Match value
nthe group number
+

Example

program Demo {
+  entry {
+    let m = Regex.find("hi there", "hi")
+    print(Regex.end(m, 0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-exec.html b/regex-exec.html new file mode 100644 index 00000000..f1b73032 --- /dev/null +++ b/regex-exec.html @@ -0,0 +1,34 @@ + + + + + +Regex.exec — Ludic + + + + + + +
+
API Reference › Regex › Regex.exec
+
+ method +

Regex.exec

+
+ Regex.exec(text, re) -> Match +

The compiled-pattern form of Regex.find: returns the first Match of the pre-compiled re in text, or null.

+

Parameters

textthe string to search
rea compiled Regex
+

Example

program Demo {
+  entry {
+    let re = Regex.compile("#(\\w+)")
+    let m = Regex.exec("a #tag b", re)
+    print(Regex.group(m, 1))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-find.html b/regex-find.html new file mode 100644 index 00000000..819364ee --- /dev/null +++ b/regex-find.html @@ -0,0 +1,33 @@ + + + + + +Regex.find — Ludic + + + + + + +
+
API Reference › Regex › Regex.find
+
+ method +

Regex.find

+
+ Regex.find(text, pattern) -> Match +

Searches text and returns the first Match — carrying the overall span and every capture group — or null if the pattern does not match. Read groups with Regex.group and spans with Regex.start / Regex.end.

+

Parameters

textthe string to search
patternthe regular expression source
+

Example

program Demo {
+  entry {
+    let m = Regex.find("name: Alice", "(\\w+): (\\w+)")
+    if Regex.ok(m) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-group.html b/regex-group.html new file mode 100644 index 00000000..8f2dd840 --- /dev/null +++ b/regex-group.html @@ -0,0 +1,33 @@ + + + + + +Regex.group — Ludic + + + + + + +
+
API Reference › Regex › Regex.group
+
+ method +

Regex.group

+
+ Regex.group(match, n) -> string +

Returns the substring captured by group n of match — group 0 is the whole match, 1 the first parenthesized group, and so on. Returns an empty string if the group did not participate in the match.

+

Parameters

matcha Match from Regex.find / Regex.exec / Regex.next
nthe group number (0 = whole match)
+

Example

program Demo {
+  entry {
+    let m = Regex.find("key=value", "(\\w+)=(\\w+)")
+    print(Regex.group(m, 2))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-group_count.html b/regex-group_count.html new file mode 100644 index 00000000..120693c0 --- /dev/null +++ b/regex-group_count.html @@ -0,0 +1,33 @@ + + + + + +Regex.group_count — Ludic + + + + + + +
+
API Reference › Regex › Regex.group_count
+
+ method +

Regex.group_count

+
+ Regex.group_count(match) -> int +

Returns the number of capture groups in the pattern behind match (not counting group 0, the whole match). Valid group numbers for Regex.group are 0…group_count.

+

Parameters

matcha Match value
+

Example

program Demo {
+  entry {
+    let m = Regex.find("a-b", "(\\w)-(\\w)")
+    print(Regex.group_count(m))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-matches.html b/regex-matches.html new file mode 100644 index 00000000..0041c4ea --- /dev/null +++ b/regex-matches.html @@ -0,0 +1,32 @@ + + + + + +Regex.matches — Ludic + + + + + + +
+
API Reference › Regex › Regex.matches
+
+ method +

Regex.matches

+
+ Regex.matches(text, pattern) -> bool +

Searches text for pattern and returns whether it matches anywhere (like a search, not a full-string match — anchor with ^…$ for that). Compiles the pattern each call; for a pattern you use repeatedly, compile once with Regex.compile and use Regex.test.

+

Parameters

textthe string to search
patternthe regular expression source
+

Example

program Demo {
+  entry {
+    if Regex.matches("/quit now", "^/(help|quit)\\b") { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-next.html b/regex-next.html new file mode 100644 index 00000000..eaa1468c --- /dev/null +++ b/regex-next.html @@ -0,0 +1,37 @@ + + + + + +Regex.next — Ludic + + + + + + +
+
API Reference › Regex › Regex.next
+
+ method +

Regex.next

+
+ Regex.next(text, re, from) -> Match +

Returns the first match of re in text starting at or after byte offset from, or null. Loop it from the previous match's end (see Regex.end) to iterate every match — the find-all idiom.

+

Parameters

textthe string to search
rea compiled Regex
fromthe byte offset to resume from
+

Example

program Demo {
+  entry {
+    let re = Regex.compile("#(\\w+)")
+    var m = Regex.exec("#a #b #c", re)
+    while Regex.ok(m) {
+      print(Regex.group(m, 1))
+      m = Regex.next("#a #b #c", re, Regex.end(m, 0))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-ok.html b/regex-ok.html new file mode 100644 index 00000000..9fb4c896 --- /dev/null +++ b/regex-ok.html @@ -0,0 +1,32 @@ + + + + + +Regex.ok — Ludic + + + + + + +
+
API Reference › Regex › Regex.ok
+
+ method +

Regex.ok

+
+ Regex.ok(match) -> bool +

Returns whether match is a real match rather than null — the readable way to test the result of Regex.find / Regex.exec / Regex.next, and the loop condition for a find-all.

+

Parameters

matcha Match value (possibly null)
+

Example

program Demo {
+  entry {
+    if not Regex.ok(Regex.find("abc", "\\d")) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-replace.html b/regex-replace.html new file mode 100644 index 00000000..fd0afd6d --- /dev/null +++ b/regex-replace.html @@ -0,0 +1,32 @@ + + + + + +Regex.replace — Ludic + + + + + + +
+
API Reference › Regex › Regex.replace
+
+ method +

Regex.replace

+
+ Regex.replace(text, pattern, replacement) -> string +

Returns a new string with every non-overlapping match of pattern in text replaced by replacement. In the replacement, \0 is the whole match, \1…\9 are capture groups, and \\ is a literal backslash — the natural fit for dialogue/localization token substitution.

+

Parameters

textthe string to transform
patternthe regular expression source
replacementthe replacement, with \0..\9 group references
+

Example

program Demo {
+  entry {
+    print(Regex.replace("hi {name}!", "\\{(\\w+)\\}", "[\\1]"))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-start.html b/regex-start.html new file mode 100644 index 00000000..0eb3105c --- /dev/null +++ b/regex-start.html @@ -0,0 +1,33 @@ + + + + + +Regex.start — Ludic + + + + + + +
+
API Reference › Regex › Regex.start
+
+ method +

Regex.start

+
+ Regex.start(match, n) -> int +

Returns the byte offset where group n of match begins, or -1 if the group did not participate. Group 0 is the whole match, so Regex.start(m, 0) is where the match begins.

+

Parameters

matcha Match value
nthe group number
+

Example

program Demo {
+  entry {
+    let m = Regex.find("  hi", "hi")
+    print(Regex.start(m, 0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-test.html b/regex-test.html new file mode 100644 index 00000000..014070e1 --- /dev/null +++ b/regex-test.html @@ -0,0 +1,33 @@ + + + + + +Regex.test — Ludic + + + + + + +
+
API Reference › Regex › Regex.test
+
+ method +

Regex.test

+
+ Regex.test(text, re) -> bool +

The compiled-pattern form of Regex.matches: returns whether the pre-compiled re matches anywhere in text. No per-call parsing, so this is the one to call every frame.

+

Parameters

textthe string to search
rea compiled Regex from Regex.compile
+

Example

program Demo {
+  entry {
+    let re = Regex.compile("[a-z]+")
+    if Regex.test("hello", re) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/regex-valid.html b/regex-valid.html new file mode 100644 index 00000000..0100aba6 --- /dev/null +++ b/regex-valid.html @@ -0,0 +1,33 @@ + + + + + +Regex.valid — Ludic + + + + + + +
+
API Reference › Regex › Regex.valid
+
+ method +

Regex.valid

+
+ Regex.valid(pattern) -> bool +

A convenience over Regex.compile: true when pattern compiles, false when it is malformed. Use it to validate a user- or mod-supplied pattern before relying on it.

+

Parameters

patternthe regular expression source
+

Example

program Demo {
+  entry {
+    if Regex.valid("(a|b)+") { print(1) }
+    if not Regex.valid("(unterminated") { print(2) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/save-read.html b/save-read.html new file mode 100644 index 00000000..6ac05a18 --- /dev/null +++ b/save-read.html @@ -0,0 +1,28 @@ + + + + + +Save.read — Ludic + + + + + + +
+
API Reference › Save › Save.read
+
+ method +

Save.read

+
+ Save.read() -> bool +

Restores the world from the save slot, returning false when there is no snapshot to load.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/save-write.html b/save-write.html new file mode 100644 index 00000000..3bbe4931 --- /dev/null +++ b/save-write.html @@ -0,0 +1,28 @@ + + + + + +Save.write — Ludic + + + + + + +
+
API Reference › Save › Save.write
+
+ method +

Save.write

+
+ Save.write() -> void +

Writes a binary snapshot of the whole ECS world to the save slot.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-bar.html b/screen-bar.html new file mode 100644 index 00000000..1ff6bb43 --- /dev/null +++ b/screen-bar.html @@ -0,0 +1,29 @@ + + + + + +Screen.bar — Ludic + + + + + + +
+ +
+ method +

Screen.bar

+
+ Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:) +

Draws a back-coloured track and fills value / max of its width in color. Health bars, cooldowns, loading progress.

+ +

Example

# doc-check: skip — illustrative
+Screen.bar(x: 80, y: 228, width: 160, height: 6, value: hp, max: max_hp, color: Color.Crimson, back: Color.RaisinBlack)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-blend_mode.html b/screen-blend_mode.html new file mode 100644 index 00000000..22002c04 --- /dev/null +++ b/screen-blend_mode.html @@ -0,0 +1,36 @@ + + + + + +Screen.blend_mode — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.blend_mode
+
+ method +

Screen.blend_mode

+
+ Screen.blend_mode(mode) +

Selects how drawn pixels combine with the framebuffer: 0 replaces (the default), 1 adds colours channel-wise and clamps at 255. Additive blending is how you draw glows, fire, lasers, and light — stack several bright shapes and the overlaps brighten toward white. Set it back to 0 when done.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.blend_mode(1)
+    Screen.fill_circle(x: 150, y: 120, radius: 30, color: Color.rgb(80, 20, 0))
+    Screen.fill_circle(x: 170, y: 120, radius: 30, color: Color.rgb(80, 20, 0))
+    Screen.blend_mode(0)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-camera.html b/screen-camera.html new file mode 100644 index 00000000..b3201c3d --- /dev/null +++ b/screen-camera.html @@ -0,0 +1,38 @@ + + + + + +Screen.camera — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.camera
+
+ method +

Screen.camera

+
+ Screen.camera(x, y) +

Sets a camera offset applied to every draw: a world point (wx, wy) lands on screen at (wx - x, wy - y). Move it to scroll the world under a fixed viewport. The camera moves everything drawn, so reset it to (0, 0) before drawing a fixed HUD. Same control as Camera.set. Deterministic.

+ +

Example

program Demo {
+  property Position { x: int = 0, y: int = 0 }
+  model Player { Position }
+  handler DrawWorld phase Render {
+    Screen.camera(Position.x - 160, Position.y - 120)   # centre on the player
+    Screen.fill_rectangle(x: 0, y: 0, width: 16, height: 16, color: Color.Red)
+    Screen.camera(0, 0)                                  # HUD in screen space
+    Screen.draw_text(x: 4, y: 4, text: "SCORE", color: Color.White, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-circle.html b/screen-circle.html new file mode 100644 index 00000000..1188fefb --- /dev/null +++ b/screen-circle.html @@ -0,0 +1,34 @@ + + + + + +Screen.circle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.circle
+
+ method +

Screen.circle

+
+ Screen.circle(x, y, radius, color) +

Draws the outline of a circle centred at (x, y) with the given radius in color. Use it for rings, radii, and selection markers; for a solid disc use Screen.fill_circle.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.circle(x: 160, y: 120, radius: 40, color: Color.Cyan)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-clear.html b/screen-clear.html new file mode 100644 index 00000000..e2b1261c --- /dev/null +++ b/screen-clear.html @@ -0,0 +1,40 @@ + + + + + +Screen.clear — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.clear
+
+ method +

Screen.clear

+
+ Screen.clear(color) +

Paints every pixel of the framebuffer a single color, wiping whatever the previous frame drew. Call it first inside your Render handler so each frame starts from a known background instead of leftover pixels. The one argument is a color, which can be a named palette value such as Color.MidnightBlue or a raw hex literal like 0x0d1020. After clearing, draw the scene on top and finish with Screen.show().

+

Parameters

colorthe background color to fill with, e.g. a Color.* name or a 0xRRGGBB literal
+

Example

program ClearBackground {
+  var elapsed_frames: int = 0
+
+  handler CountFrames phase Update {
+    elapsed_frames = elapsed_frames + 1
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_number(x: 8, y: 8, value: elapsed_frames, color: Color.White, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-clip.html b/screen-clip.html new file mode 100644 index 00000000..642c38f8 --- /dev/null +++ b/screen-clip.html @@ -0,0 +1,35 @@ + + + + + +Screen.clip — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.clip
+
+ method +

Screen.clip

+
+ Screen.clip(x, y, width, height) +

Restricts all subsequent drawing to the screen-space rectangle (x, y, width, height) — pixels outside it are discarded. Use it to keep a panel's contents inside its frame, mask a minimap, or draw a wipe transition. Call Screen.clip_reset to lift it. The clip rectangle is in screen space, applied after the camera offset.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clip(x: 20, y: 20, width: 100, height: 60)
+    Screen.fill_rectangle(x: 0, y: 0, width: 320, height: 240, color: Color.Green)
+    Screen.clip_reset()
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-clip_reset.html b/screen-clip_reset.html new file mode 100644 index 00000000..350cae76 --- /dev/null +++ b/screen-clip_reset.html @@ -0,0 +1,35 @@ + + + + + +Screen.clip_reset — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.clip_reset
+
+ method +

Screen.clip_reset

+
+ Screen.clip_reset() +

Clears any clip rectangle set by Screen.clip, restoring drawing to the whole framebuffer. Call it once you are done drawing inside a masked region.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clip(x: 0, y: 0, width: 64, height: 64)
+    Screen.circle(x: 32, y: 32, radius: 40, color: Color.Yellow)
+    Screen.clip_reset()
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-draw_number.html b/screen-draw_number.html new file mode 100644 index 00000000..a83cfff4 --- /dev/null +++ b/screen-draw_number.html @@ -0,0 +1,41 @@ + + + + + +Screen.draw_number — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.draw_number
+
+ method +

Screen.draw_number

+
+ Screen.draw_number(x, y, value, color, scale) +

Draws an integer as digits using the built-in font, without first turning it into a string — so it never allocates and is safe to call every frame for constantly-changing values. Use it for scores, timers, frame counts, health totals, and any HUD number, while reserving Screen.draw_text for fixed labels. It takes the same top-left position, color, and integer scale as draw_text. Negative values are drawn with a leading minus sign.

+

Parameters

xthe left edge of the first digit, in pixels
ythe top edge of the number, in pixels
valuethe integer to draw
colorthe digit color, e.g. a Color.* name or a 0xRRGGBB literal
scaleinteger size multiplier (1 = native size)
+

Example

program ScoreHud {
+  var score: int = 0
+
+  handler EarnPoints phase Update {
+    score = score + 1
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
+    Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-draw_rectangle.html b/screen-draw_rectangle.html new file mode 100644 index 00000000..b5314fab --- /dev/null +++ b/screen-draw_rectangle.html @@ -0,0 +1,48 @@ + + + + + +Screen.draw_rectangle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.draw_rectangle
+
+ method +

Screen.draw_rectangle

+
+ Screen.draw_rectangle(x, y, width, height, color) +

Draws just the border of a rectangle — a one-pixel-thick outline — leaving the interior untouched so whatever is behind it shows through. It takes the same top-left position and size as Screen.fill_rectangle, but is meant for framing rather than filling: selection boxes, UI panels, health-bar borders, or highlighting the tile under a cursor. Use it together with fill_rectangle when you want a filled shape with a contrasting edge. Coordinates are in pixels from the top-left of the screen.

+

Parameters

xthe left edge, in pixels from the left of the screen
ythe top edge, in pixels from the top of the screen
widththe outline width in pixels
heightthe outline height in pixels
colorthe outline color, e.g. a Color.* name or a 0xRRGGBB literal
+

Example

program SelectionFrame {
+  property Position { column: int = 0, row: int = 0 }
+  model Enemy { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler SpawnEnemy phase Start {
+    spawn Enemy { Position { column: 6, row: 5 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Enemy}] {
+      let pixel_x = Position.column * TILE_SIZE
+      let pixel_y = Position.row * TILE_SIZE
+      Screen.fill_rectangle(x: pixel_x, y: pixel_y, width: TILE_SIZE, height: TILE_SIZE, color: Color.Crimson)
+      Screen.draw_rectangle(x: pixel_x, y: pixel_y, width: TILE_SIZE, height: TILE_SIZE, color: Color.Gold)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-draw_text.html b/screen-draw_text.html new file mode 100644 index 00000000..450fcc2a --- /dev/null +++ b/screen-draw_text.html @@ -0,0 +1,41 @@ + + + + + +Screen.draw_text — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.draw_text
+
+ method +

Screen.draw_text

+
+ Screen.draw_text(x, y, text, color, scale) +

Draws a string using Ludic's built-in bitmap font, starting at the top-left pixel (x, y). The scale is an integer multiplier: 1 is the native font size, 2 is double-height and double-width, and so on — there are no fractional sizes. Use it for labels, menus, and messages such as titles or "GAME OVER"; for numbers that change every frame prefer Screen.draw_number, which needs no string conversion. The text is drawn in the given color over whatever is already on screen.

+

Parameters

xthe left edge of the first character, in pixels
ythe top edge of the text, in pixels
textthe string to draw
colorthe text color, e.g. a Color.* name or a 0xRRGGBB literal
scaleinteger size multiplier (1 = native size)
+

Example

program TitleScreen {
+  var elapsed_frames: int = 0
+
+  handler CountFrames phase Update {
+    elapsed_frames = elapsed_frames + 1
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.draw_text(x: 40, y: 60, text: "CHRONORIFT", color: Color.Gold, scale: 3)
+    Screen.draw_text(x: 64, y: 120, text: "PRESS ANY KEY", color: Color.White, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-fill_circle.html b/screen-fill_circle.html new file mode 100644 index 00000000..c34efcc1 --- /dev/null +++ b/screen-fill_circle.html @@ -0,0 +1,34 @@ + + + + + +Screen.fill_circle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.fill_circle
+
+ method +

Screen.fill_circle

+
+ Screen.fill_circle(x, y, radius, color) +

Draws a solid disc centred at (x, y) with the given radius in color, one horizontal span per row. Use it for round bodies, bullets, and glows.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.fill_circle(x: bx, y: by, radius: 6, color: Color.Gold)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-fill_rectangle.html b/screen-fill_rectangle.html new file mode 100644 index 00000000..d8c9585c --- /dev/null +++ b/screen-fill_rectangle.html @@ -0,0 +1,50 @@ + + + + + +Screen.fill_rectangle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.fill_rectangle
+
+ method +

Screen.fill_rectangle

+
+ Screen.fill_rectangle(x, y, width, height, color) +

Draws a solid block of color from the top-left corner (x, y) spanning width by height pixels. This is the workhorse for drawing tiles, sprites-from-primitives, health bars, and background panels, since a Ludic game paints every pixel itself rather than loading image assets. Coordinates are in pixels measured from the top-left of the screen, so multiply a grid column/row by your tile size to place things on a board. Parts of the rectangle that fall outside the framebuffer are simply clipped.

+

Parameters

xthe left edge, in pixels from the left of the screen
ythe top edge, in pixels from the top of the screen
widththe rectangle width in pixels
heightthe rectangle height in pixels
colorthe fill color, e.g. a Color.* name or a 0xRRGGBB literal
+

Example

program FilledTiles {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 4, row: 3 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-fill_triangle.html b/screen-fill_triangle.html new file mode 100644 index 00000000..21ae4863 --- /dev/null +++ b/screen-fill_triangle.html @@ -0,0 +1,34 @@ + + + + + +Screen.fill_triangle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.fill_triangle
+
+ method +

Screen.fill_triangle

+
+ Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color) +

Fills the triangle with corners (x1, y1), (x2, y2), (x3, y3) in color, scanning its bounding box with an inside test. The building block for flat-shaded polygons and arrowheads.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.fill_triangle(x1: 10, y1: 10, x2: 30, y2: 10, x3: 20, y3: 26, color: Color.Red)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-height.html b/screen-height.html new file mode 100644 index 00000000..549dc23a --- /dev/null +++ b/screen-height.html @@ -0,0 +1,51 @@ + + + + + +Screen.height — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.height
+
+ method +

Screen.height

+
+ Screen.height() -> int +

Returns the height of the framebuffer in pixels — the number of drawable rows. Use it, together with Screen.width, to position elements relative to the bottom or vertical center of the screen instead of assuming a fixed size. A common use is keeping a moving object on screen by clamping its pixel row against this value. It takes no arguments and can be read during any handler.

+ +

Example

program GroundLine {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 3, row: 0 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    let ground_y = Screen.height() - TILE_SIZE
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: ground_y,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-line.html b/screen-line.html new file mode 100644 index 00000000..970a7e80 --- /dev/null +++ b/screen-line.html @@ -0,0 +1,34 @@ + + + + + +Screen.line — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.line
+
+ method +

Screen.line

+
+ Screen.line(x1, y1, x2, y2, color) +

Draws a one-pixel line from (x1, y1) to (x2, y2) in color, using an integer Bresenham walk so it looks the same on every platform. Use it for lasers, tethers, debug rays, and vector art. Draw during the Render phase.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.line(x1: px, y1: py, x2: tx, y2: ty, color: Color.White)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-measure_text.html b/screen-measure_text.html new file mode 100644 index 00000000..a8b4be5b --- /dev/null +++ b/screen-measure_text.html @@ -0,0 +1,35 @@ + + + + + +Screen.measure_text — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.measure_text
+
+ method +

Screen.measure_text

+
+ Screen.measure_text(text) -> int +

Returns the width in pixels that text occupies in the built-in 5x7 font at scale 1 — 6 pixels per glyph, matching how Screen.draw_text advances. Use it to right-align or centre text, size a label's background, or lay out a menu.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    let label = "READY"
+    let w = Screen.measure_text(label)
+    Screen.draw_text(x: 160 - w / 2, y: 100, text: label, color: Color.White, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-oval.html b/screen-oval.html new file mode 100644 index 00000000..5c40cbf5 --- /dev/null +++ b/screen-oval.html @@ -0,0 +1,34 @@ + + + + + +Screen.oval — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.oval
+
+ method +

Screen.oval

+
+ Screen.oval(x, y, rx, ry, color) +

Draws the outline of an ellipse centred at (x, y) with horizontal radius rx and vertical radius ry, in color, by the integer midpoint algorithm. Equal radii draw a circle; use it for eyes, planets, health auras, and selection ovals.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.Black)
+    Screen.oval(x: 160, y: 120, rx: 60, ry: 30, color: Color.Cyan)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-pixel.html b/screen-pixel.html new file mode 100644 index 00000000..f6d078fd --- /dev/null +++ b/screen-pixel.html @@ -0,0 +1,34 @@ + + + + + +Screen.pixel — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.pixel
+
+ method +

Screen.pixel

+
+ Screen.pixel(x, y) -> int +

Reads the colour of the framebuffer pixel at screen (x, y) as 0x00RRGGBB, or 0 if the coordinate is off-screen. Unlike drawing, it ignores the camera — it reports the actual screen. Handy for colour-based collision or hit-testing against what was rendered, and for pixel-exact tests.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.put_pixel(x: 10, y: 10, color: Color.Red)
+    if Screen.pixel(10, 10) == Color.Red { Screen.status("hit") }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-put_pixel.html b/screen-put_pixel.html new file mode 100644 index 00000000..29e32ba3 --- /dev/null +++ b/screen-put_pixel.html @@ -0,0 +1,51 @@ + + + + + +Screen.put_pixel — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.put_pixel
+
+ method +

Screen.put_pixel

+
+ Screen.put_pixel(x, y, color) +

Sets exactly one pixel at (x, y) to the given color — the finest-grained drawing operation Ludic offers. Reach for it when you are plotting individual points such as stars, particles, a scatter of sparks, or hand-drawn curves where a full rectangle would be too coarse. For anything larger than a point prefer Screen.fill_rectangle, since one call per pixel is far more expensive than one call per block. Coordinates are pixels from the top-left of the screen, and out-of-range coordinates are ignored.

+

Parameters

xthe pixel column, in pixels from the left of the screen
ythe pixel row, in pixels from the top of the screen
colorthe pixel color, e.g. a Color.* name or a 0xRRGGBB literal
+

Example

program Starfield {
+  property Position { column: int = 0, row: int = 0 }
+  model Star { Position }
+
+  handler SeedStars phase Start {
+    Random.seed(value: 1)
+    for spawn_index in 0 .. 40 {
+      spawn Star {
+        Position {
+          column: Random.range(low: 0, high: 255),
+          row: Random.range(low: 0, high: 223)
+        }
+      }
+    }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Star}] {
+      Screen.put_pixel(x: Position.column, y: Position.row, color: Color.White)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-show.html b/screen-show.html new file mode 100644 index 00000000..6ce52a16 --- /dev/null +++ b/screen-show.html @@ -0,0 +1,50 @@ + + + + + +Screen.show — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.show
+
+ method +

Screen.show

+
+ Screen.show() +

Presents the frame: it copies everything you have drawn into the framebuffer this pass onto the visible window all at once. Call it exactly once, as the very last statement of your Render handler, after every Screen.clear, fill_rectangle, draw_text, and other draw call. Because drawing happens off-screen and only show makes it visible, the player never sees a half-drawn frame (no flicker or tearing). Forgetting to call it means your drawing never appears; it takes no arguments.

+ +

Example

program PresentFrame {
+  property Position { column: int = 0, row: int = 0 }
+  model Player { Position }
+
+  const TILE_SIZE: int = 16
+
+  handler SpawnPlayer phase Start {
+    spawn Player { Position { column: 5, row: 5 } }
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    for (Position) in query [Position, {Player}] {
+      Screen.fill_rectangle(
+        x: Position.column * TILE_SIZE,
+        y: Position.row * TILE_SIZE,
+        width: TILE_SIZE,
+        height: TILE_SIZE,
+        color: Color.LimeGreen)
+    }
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-sprite.html b/screen-sprite.html new file mode 100644 index 00000000..d58f5714 --- /dev/null +++ b/screen-sprite.html @@ -0,0 +1,34 @@ + + + + + +Screen.sprite — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.sprite
+
+ method +

Screen.sprite

+
+ Screen.sprite(id, x, y) +

Draws sprite id from the loaded sprite sheet at (x, y), at its natural 16x16 size. Load the sheet once at start, then draw sprites each frame during Render.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.sprite(id: hero_frame, x: px, y: py)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-sprite_scaled.html b/screen-sprite_scaled.html new file mode 100644 index 00000000..9be29841 --- /dev/null +++ b/screen-sprite_scaled.html @@ -0,0 +1,34 @@ + + + + + +Screen.sprite_scaled — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.sprite_scaled
+
+ method +

Screen.sprite_scaled

+
+ Screen.sprite_scaled(id, x, y, scale) +

Draws sprite id at (x, y) enlarged by an integer scale (2 doubles it, and so on) — for chunky pixel-art zoom without blurring.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.sprite_scaled(id: boss, x: bx, y: by, scale: 3)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-status.html b/screen-status.html new file mode 100644 index 00000000..9070eaa4 --- /dev/null +++ b/screen-status.html @@ -0,0 +1,40 @@ + + + + + +Screen.status — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.status
+
+ method +

Screen.status

+
+ Screen.status(text) +

Sets a single persistent line of status text that the runtime displays outside your normal drawing — a lightweight HUD line for debugging or a title-bar style message. Unlike Screen.draw_text, it is not part of the framebuffer you clear and present each frame: you set it once (or whenever it changes) and it persists until you set it again. Use it for coarse status such as the current level, connection state, or a frame-rate readout while iterating. It takes a single string argument.

+

Parameters

textthe status line to display
+

Example

program StatusLine {
+  var elapsed_frames: int = 0
+
+  handler UpdateStatus phase Update {
+    elapsed_frames = elapsed_frames + 1
+    Screen.status(text: `frame {elapsed_frames}`)
+  }
+
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-triangle.html b/screen-triangle.html new file mode 100644 index 00000000..d1929e5a --- /dev/null +++ b/screen-triangle.html @@ -0,0 +1,34 @@ + + + + + +Screen.triangle — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.triangle
+
+ method +

Screen.triangle

+
+ Screen.triangle(x1, y1, x2, y2, x3, y3, color) +

Draws the outline of the triangle with corners (x1, y1), (x2, y2), (x3, y3) in color — three lines. Use it for arrows, wedges, and vector shapes.

+ +

Example

program Demo {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    Screen.triangle(x1: 10, y1: 10, x2: 30, y2: 10, x3: 20, y3: 26, color: Color.White)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/screen-width.html b/screen-width.html new file mode 100644 index 00000000..04ac1c7b --- /dev/null +++ b/screen-width.html @@ -0,0 +1,36 @@ + + + + + +Screen.width — Ludic + + + + + + +
+
API Reference › Screen — drawing › Screen.width
+
+ method +

Screen.width

+
+ Screen.width() -> int +

Returns the width of the framebuffer in pixels — the number of drawable columns. Use it to lay out and center content relative to the actual screen size rather than hard-coding a fixed number, so your HUD or menu stays positioned correctly if the resolution changes. It pairs with Screen.height for full-screen calculations such as clamping a moving object inside the visible area. It takes no arguments and can be read during any handler.

+ +

Example

program CenteredLabel {
+  handler DrawWorld phase Render {
+    Screen.clear(Color.MidnightBlue)
+    let label_width = 10 * 6
+    let centered_x = (Screen.width() - label_width) / 2
+    Screen.draw_text(x: centered_x, y: 40, text: "CHRONORIFT", color: Color.Gold, scale: 1)
+    Screen.show()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-add.html b/set-add.html new file mode 100644 index 00000000..80b213f5 --- /dev/null +++ b/set-add.html @@ -0,0 +1,32 @@ + + + + + +Set.add — Ludic + + + + + + +
+
API Reference › Set › Set.add
+
+ method +

Set.add

+
+ Set.add(s, key) +

Adds key to the set. Adding a member that is already present is a harmless no-op.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Set.add(tags, "poison")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-clear.html b/set-clear.html new file mode 100644 index 00000000..e56875d4 --- /dev/null +++ b/set-clear.html @@ -0,0 +1,32 @@ + + + + + +Set.clear — Ludic + + + + + + +
+
API Reference › Set › Set.clear
+
+ method +

Set.clear

+
+ Set.clear(s) +

Empties the set, removing every member while keeping its allocated capacity.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Set.clear(tags)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-has.html b/set-has.html new file mode 100644 index 00000000..7f8f36e0 --- /dev/null +++ b/set-has.html @@ -0,0 +1,32 @@ + + + + + +Set.has — Ludic + + + + + + +
+
API Reference › Set › Set.has
+
+ method +

Set.has

+
+ Set.has(s, key) -> bool +

Returns true when key is a member of the set.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Set.has(tags, "poison") { tick() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-members.html b/set-members.html new file mode 100644 index 00000000..7884f7a9 --- /dev/null +++ b/set-members.html @@ -0,0 +1,32 @@ + + + + + +Set.members — Ludic + + + + + + +
+
API Reference › Set › Set.members
+
+ method +

Set.members

+
+ Set.members(s) -> []pointer +

Returns every member as a slice of strings, in unspecified order — iterate it with len and indexing.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let all = Set.members(tags)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-new.html b/set-new.html new file mode 100644 index 00000000..827b5da6 --- /dev/null +++ b/set-new.html @@ -0,0 +1,32 @@ + + + + + +Set.new — Ludic + + + + + + +
+
API Reference › Set › Set.new
+
+ method +

Set.new

+
+ Set.new() -> Set +

Creates an empty Set of string members. Add to it with Set.add and test membership with Set.has.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let tags = Set.new()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-remove.html b/set-remove.html new file mode 100644 index 00000000..96cefb84 --- /dev/null +++ b/set-remove.html @@ -0,0 +1,32 @@ + + + + + +Set.remove — Ludic + + + + + + +
+
API Reference › Set › Set.remove
+
+ method +

Set.remove

+
+ Set.remove(s, key) +

Removes key from the set (a no-op if it is not a member).

+ +

Example

program Demo {
+  handler Step phase Update {
+    Set.remove(tags, "fire")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/set-size.html b/set-size.html new file mode 100644 index 00000000..61151139 --- /dev/null +++ b/set-size.html @@ -0,0 +1,32 @@ + + + + + +Set.size — Ludic + + + + + + +
+
API Reference › Set › Set.size
+
+ method +

Set.size

+
+ Set.size(s) -> int +

Returns the number of members in the set.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let n = Set.size(tags)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/site.css b/site.css new file mode 100644 index 00000000..1c6a4bd1 --- /dev/null +++ b/site.css @@ -0,0 +1,271 @@ +/* site.css — the landing page. Loaded after base.css, which owns the tokens. + * + * The page is a single column of sections separated by rules, not a stack of + * floating cards. Nothing animates on scroll, nothing glows, and the only + * saturated colour on the page is inside a code block. */ + +section { padding: 56px 0 } +section + section { border-top: 1px solid var(--rule) } + +/* ---------- hero ---------- */ + +.hero { padding: 64px 0 56px } +.hero-grid { display: grid; gap: 40px; align-items: start } +@media (min-width: 940px) { + .hero-grid { grid-template-columns: 1fr 1fr; gap: 52px; align-items: center } +} + +.hero h1 { + font-size: clamp(34px, 5.2vw, 52px); + line-height: 1.08; + letter-spacing: -0.02em; + margin: 0 0 18px; + text-wrap: balance; +} + +.lead { + font-size: 17.5px; + color: var(--ink-2); + max-width: 54ch; + margin: 0 0 28px; +} +.lead b { color: var(--ink); font-weight: 600 } +.lead code { font-size: 0.88em } + +.cta-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center } + +.btn { + display: inline-flex; + align-items: center; + gap: 8px; + font-family: inherit; + font-size: 15px; + font-weight: 500; + padding: 10px 18px; + border-radius: var(--radius); + border: 1px solid transparent; + cursor: pointer; +} +.btn:hover { text-decoration: none } +.btn-primary { background: var(--accent); color: #fff; border-color: var(--accent) } +.btn-primary:hover { background: var(--accent-ink); border-color: var(--accent-ink) } +@media (prefers-color-scheme: dark) { + .btn-primary { color: #10202e } +} +.btn-ghost { border-color: var(--rule-2); color: var(--ink); background: var(--panel) } +.btn-ghost:hover { border-color: var(--muted) } + +/* ---------- code cards ---------- */ + +.code-card { + background: var(--code-bg); + border: 1px solid var(--code-rule); + border-radius: var(--radius); + overflow: hidden; +} +.code-top { + display: flex; + align-items: center; + padding: 9px 14px; + border-bottom: 1px solid var(--code-rule); + background: var(--paper-2); +} +.code-name { + font-family: var(--mono); + font-size: 12px; + color: var(--muted); +} +.code-card pre { + margin: 0; + border: none; + border-radius: 0; + background: none; +} + +/* ---------- pipeline ---------- */ + +.pipeline { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 10px; + padding: 22px 0 0; +} +.stage { + font-family: var(--mono); + font-size: 12.5px; + color: var(--ink-2); + border: 1px solid var(--rule-2); + border-radius: var(--radius); + padding: 5px 11px; + background: var(--panel); +} +.stage.hl { color: var(--accent); border-color: var(--accent); background: var(--accent-bg) } +.arrow { color: var(--muted); font-size: 13px } + +/* ---------- section headings ---------- */ + +.sec-head { max-width: 62ch; margin-bottom: 34px } +.kicker { + font-family: var(--mono); + font-size: 11.5px; + letter-spacing: 0.09em; + text-transform: uppercase; + color: var(--muted); + margin-bottom: 10px; +} +.sec-head h2 { + font-size: clamp(24px, 3.4vw, 32px); + letter-spacing: -0.015em; + margin: 0 0 12px; +} +.sec-head p { margin: 0; color: var(--muted); font-size: 16px } + +/* ---------- features ---------- */ + +/* The container draws the top and left edges and each cell draws its own right + and bottom, so a final row with fewer cards than columns simply stops — the + bleed-through trick (a ruled background behind gaps) leaves a grey block in + the hole instead, and the card count is not a multiple of the column count at + any breakpoint. Same construction for .editors, .idx-grid and .swatch-row. */ +.feat-grid { + display: grid; + border-top: 1px solid var(--rule); + border-left: 1px solid var(--rule); + border-radius: var(--radius); + overflow: hidden; +} +@media (min-width: 620px) { .feat-grid { grid-template-columns: 1fr 1fr } } +@media (min-width: 940px) { .feat-grid { grid-template-columns: 1fr 1fr 1fr } } + +.feat { + padding: 22px 22px 24px; + border-right: 1px solid var(--rule); + border-bottom: 1px solid var(--rule); +} +.feat h3 { + font-family: var(--sans); + font-size: 15px; + font-weight: 600; + letter-spacing: 0; + margin: 0 0 7px; +} +.feat p { margin: 0; font-size: 14.5px; color: var(--muted) } +.feat code { font-size: 0.87em } + +/* ---------- showcase ---------- */ + +.tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 16px } +.tab { + font-family: var(--mono); + font-size: 12.5px; + color: var(--muted); + background: none; + border: 1px solid transparent; + border-radius: var(--radius); + padding: 6px 12px; + cursor: pointer; +} +.tab:hover { color: var(--ink) } +.tab.active { + color: var(--accent); + border-color: var(--rule-2); + background: var(--panel); +} + +.panel-code { display: none } +.panel-code.active { display: block } +.panel-code pre { max-height: 62vh } + +.showcase-note { + display: flex; + gap: 10px; + margin-top: 16px; + font-size: 14.5px; + color: var(--muted); + max-width: 74ch; +} +.showcase-note .b { color: var(--rule-2) } + +/* ---------- philosophy ---------- */ + +.banner { display: grid; gap: 28px } +@media (min-width: 860px) { .banner { grid-template-columns: 1.5fr 1fr; gap: 48px } } +.banner h2 { font-size: clamp(22px, 3vw, 28px); margin: 0 0 14px } +.banner p { color: var(--muted); font-size: 15.5px; max-width: 60ch } + +.stat-row { display: grid; gap: 18px; align-content: start } +.stat { border-left: 2px solid var(--rule-2); padding-left: 14px } +.stat .big { + font-family: var(--serif); + font-size: 26px; + font-weight: 600; + color: var(--ink); + line-height: 1.1; +} +.stat .lbl { font-size: 13.5px; color: var(--muted); margin-top: 2px } + +/* ---------- get started ---------- */ + +.steps { display: grid; gap: 34px } +@media (min-width: 940px) { .steps { grid-template-columns: 1fr 1fr; gap: 48px; align-items: start } } + +.step { display: flex; gap: 14px; padding: 16px 0; border-top: 1px solid var(--rule) } +.step:first-child { border-top: none; padding-top: 0 } +.step .n { + flex: none; + width: 22px; height: 22px; + display: grid; place-items: center; + border-radius: 50%; + border: 1px solid var(--rule-2); + font-family: var(--mono); + font-size: 11.5px; + color: var(--muted); + margin-top: 2px; +} +.step h4 { font-family: var(--sans); font-size: 15px; font-weight: 600; margin: 0 0 4px } +.step p { margin: 0; font-size: 14.5px; color: var(--muted) } + +.term { + background: var(--code-bg); + border: 1px solid var(--code-rule); + border-radius: var(--radius); + overflow: hidden; + position: sticky; + top: calc(var(--hdr) + 20px); +} +.term pre { margin: 0; border: none; border-radius: 0; background: none } +.term .prompt { color: var(--muted); user-select: none } +.term .out { color: var(--c-type) } + +/* ---------- editors ---------- */ + +.editors { + display: grid; + border-top: 1px solid var(--rule); + border-left: 1px solid var(--rule); + border-radius: var(--radius); + overflow: hidden; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); +} +.ed { + padding: 13px 16px; + font-size: 14.5px; + color: var(--ink-2); + border-right: 1px solid var(--rule); + border-bottom: 1px solid var(--rule); +} +.ed .k { color: var(--rule-2); margin-right: 4px } + +/* the second half of the headline, set back so the first clause carries it */ +.accent { color: var(--muted) } + +.pipe-note { + text-align: center; + color: var(--muted); + font-size: 13.5px; + margin: 12px auto 0; + max-width: 62ch; +} +.ed-note { margin-top: 18px; color: var(--muted); font-size: 14.5px } diff --git a/sprite-cell.html b/sprite-cell.html new file mode 100644 index 00000000..294b876a --- /dev/null +++ b/sprite-cell.html @@ -0,0 +1,28 @@ + + + + + +Sprite.cell — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.cell
+
+ method +

Sprite.cell

+
+ Sprite.cell(sheet, col, row) -> int +

Returns a sprite id for the cell at column col, row row of sheet (the sub-rect (col*cellw, row*cellh, cellw, cellh)). Draw it with Sprite.draw.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-cell_span.html b/sprite-cell_span.html new file mode 100644 index 00000000..f39ef46c --- /dev/null +++ b/sprite-cell_span.html @@ -0,0 +1,28 @@ + + + + + +Sprite.cell_span — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.cell_span
+
+ method +

Sprite.cell_span

+
+ Sprite.cell_span(sheet, col, row, cols, rows) -> int +

Returns a sprite id for a sub-rect spanning cols x rows cells from (col, row) — for a sprite that covers more than one cell (a tall character, a wide object). Same as Sprite.cell but sized to the span.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-define.html b/sprite-define.html new file mode 100644 index 00000000..65664eb7 --- /dev/null +++ b/sprite-define.html @@ -0,0 +1,28 @@ + + + + + +Sprite.define — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.define
+
+ method +

Sprite.define

+
+ Sprite.define(name, sheet, col, row) -> int +

Creates the cell at (col, row) of sheet and registers it under name, returning its sprite id. Look it up later with Sprite.named — content data (item/tile names) can drive rendering without hard-coded ids.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-draw.html b/sprite-draw.html new file mode 100644 index 00000000..e3438e5a --- /dev/null +++ b/sprite-draw.html @@ -0,0 +1,28 @@ + + + + + +Sprite.draw — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.draw
+
+ method +

Sprite.draw

+
+ Sprite.draw(id, x, y) +

Draws atlas sprite id with its top-left at (x, y). Sufficiently-opaque pixels are drawn through rt_put_px, so the camera, zoom and clip apply exactly like Screen.sprite.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-draw_meter.html b/sprite-draw_meter.html new file mode 100644 index 00000000..075bfc5b --- /dev/null +++ b/sprite-draw_meter.html @@ -0,0 +1,29 @@ + + + + + +Sprite.draw_meter — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.draw_meter
+
+ method +

Sprite.draw_meter

+
+ Sprite.draw_meter(x:, y:, value:, max:, per_icon:, spacing:, full:, half:, empty:) +

Draws max / per_icon icons, each full, half or empty according to value — hearts, stars, ammo pips.

+ +

Example

# doc-check: skip — illustrative
+Sprite.draw_meter(x: 8, y: 8, value: hp, max: max_hp, per_icon: 20, spacing: 15, full: heart, half: half_heart, empty: empty_heart)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-draw_scaled.html b/sprite-draw_scaled.html new file mode 100644 index 00000000..607aa4fe --- /dev/null +++ b/sprite-draw_scaled.html @@ -0,0 +1,28 @@ + + + + + +Sprite.draw_scaled — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.draw_scaled
+
+ method +

Sprite.draw_scaled

+
+ Sprite.draw_scaled(id, x, y, scale) +

Draws atlas sprite id at (x, y) scaled by the integer scale (each source pixel becomes a scale x scale block). For sub-integer / camera zoom, use Camera.zoom around a plain Sprite.draw.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-height.html b/sprite-height.html new file mode 100644 index 00000000..7ecfd94a --- /dev/null +++ b/sprite-height.html @@ -0,0 +1,28 @@ + + + + + +Sprite.height — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.height
+
+ method +

Sprite.height

+
+ Sprite.height(id) -> int +

Returns the pixel height of atlas sprite id (its sub-rect height).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-named.html b/sprite-named.html new file mode 100644 index 00000000..901370c8 --- /dev/null +++ b/sprite-named.html @@ -0,0 +1,28 @@ + + + + + +Sprite.named — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.named
+
+ method +

Sprite.named

+
+ Sprite.named(name) -> int +

Returns the sprite id registered under name by Sprite.define, or -1 if none. Names compare by content.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-sheet.html b/sprite-sheet.html new file mode 100644 index 00000000..3737cbec --- /dev/null +++ b/sprite-sheet.html @@ -0,0 +1,28 @@ + + + + + +Sprite.sheet — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.sheet
+
+ method +

Sprite.sheet

+
+ Sprite.sheet(path, cellw, cellh) -> int +

Loads the PNG at path and records that its cells are cellw x cellh px, returning a sheet handle. Address its cells with Sprite.cell / Sprite.cell_span. The whole image is kept (variable size), so cells are not restricted to the 16x16 sprite table.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-strip.html b/sprite-strip.html new file mode 100644 index 00000000..2d78b05e --- /dev/null +++ b/sprite-strip.html @@ -0,0 +1,30 @@ + + + + + +Sprite.strip — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.strip
+
+ method +

Sprite.strip

+
+ Sprite.strip(sheet, col, row, count, rows) -> int +

Registers count frames starting at cell (col, row), each rows cells tall, as consecutive ids and returns the first. With a SpriteAnim on the entity the engine adds the current frame to Sprite.id, so the strip's first id is all a spawn needs.

+ +

Example

# doc-check: skip — illustrative
+let hero_run = Sprite.strip(sheet: sheet, col: 8, row: 2, count: 8, rows: 2)
+spawn Hero { Sprite { id: hero_run, atlas: 1 }, SpriteAnim { fps: 8, frames: 4 } }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sprite-width.html b/sprite-width.html new file mode 100644 index 00000000..ec16ac92 --- /dev/null +++ b/sprite-width.html @@ -0,0 +1,28 @@ + + + + + +Sprite.width — Ludic + + + + + + +
+
API Reference › Sprite › Sprite.width
+
+ method +

Sprite.width

+
+ Sprite.width(id) -> int +

Returns the pixel width of atlas sprite id (its sub-rect width) — handy for centering and layout.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/symbols.json b/symbols.json new file mode 100644 index 00000000..07b5afe9 --- /dev/null +++ b/symbols.json @@ -0,0 +1,21540 @@ +{ + "items": { + "kw-program": { + "id": "kw-program", + "name": "program", + "sig": "program Name { … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "The top-level unit — one program compiles to one native game.", + "page": "kw-program.html" + }, + "kw-property": { + "id": "kw-property", + "name": "property", + "sig": "property Name { field: T = default, … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A named record of typed fields — a per-model component, or a plain heap record.", + "page": "kw-property.html" + }, + "kw-model": { + "id": "kw-model", + "name": "model", + "sig": "model Name { PropA, PropB, … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A named kind of thing — a fixed bundle of properties you spawn by one name.", + "page": "kw-model.html" + }, + "kw-handler": { + "id": "kw-handler", + "name": "handler", + "sig": "handler Name phase P { … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A named block the engine runs each frame during phase P.", + "page": "kw-handler.html" + }, + "kw-phase": { + "id": "kw-phase", + "name": "phase", + "sig": "phase Start | Input | Update | FixedUpdate | Render", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Names which stage of the frame a handler runs in.", + "page": "kw-phase.html" + }, + "kw-const": { + "id": "kw-const", + "name": "const", + "sig": "const NAME: T = value", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A compile-time constant, folded into the code with no storage.", + "page": "kw-const.html" + }, + "kw-var": { + "id": "kw-var", + "name": "var", + "sig": "var name: T = value", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A mutable binding — at program scope, your game's persistent named state.", + "page": "kw-var.html" + }, + "kw-let": { + "id": "kw-let", + "name": "let", + "sig": "let name = value", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "An immutable binding — the default choice for a value that never changes.", + "page": "kw-let.html" + }, + "kw-function": { + "id": "kw-function", + "name": "function", + "sig": "function name(a: T, b: T) -> R { … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A function — reusable logic called positionally or with named arguments.", + "page": "kw-function.html" + }, + "kw-fn": { + "id": "kw-fn", + "name": "fn", + "sig": "fn name", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A top-level function named as a value - the entry point handed to Job.parallel_for.", + "page": "kw-fn.html" + }, + "kw-prefab": { + "id": "kw-prefab", + "name": "prefab", + "sig": "prefab Name: Model { Comp { field: value }, … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A model with preset component fields — spawn it, override what varies.", + "page": "kw-prefab.html" + }, + "kw-return": { + "id": "kw-return", + "name": "return", + "sig": "return value", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Hand a value back from a function and stop running it.", + "page": "kw-return.html" + }, + "kw-entry": { + "id": "kw-entry", + "name": "entry", + "sig": "entry { … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "The program's main block — runs once, top to bottom.", + "page": "kw-entry.html" + }, + "kw-import": { + "id": "kw-import", + "name": "import", + "sig": "import \"file.ludic\"", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Splice another Ludic file's declarations into this program.", + "page": "kw-import.html" + }, + "kw-public": { + "id": "kw-public", + "name": "public", + "sig": "scene Name start public { … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Expose a declaration's lifecycle on the public event bus.", + "page": "kw-public.html" + }, + "kw-extern": { + "id": "kw-extern", + "name": "extern", + "sig": "extern function name(a: T) -> R = \"symbol\"", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Bind a name to an external native symbol — the seam for platform and library calls.", + "page": "kw-extern.html" + }, + "kw-new": { + "id": "kw-new", + "name": "new", + "sig": "new Record { … } new []T", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Allocate a property record or an empty slice on the heap.", + "page": "kw-new.html" + }, + "kw-namespace": { + "id": "kw-namespace", + "name": "namespace", + "sig": "namespace Name { export function foo(…) … internal function bar(…) … }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Group functions into a Name.* namespace and control its public surface.", + "page": "kw-namespace.html" + }, + "kw-enum": { + "id": "kw-enum", + "name": "enum", + "sig": "enum Name { A, B, C }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "A named set of integer constants — names for a magic-number space.", + "page": "kw-enum.html" + }, + "kw-ui": { + "id": "kw-ui", + "name": "ui", + "sig": "ui { panel { … } }", + "kind": "keyword", + "category": "structure", + "section": "Program structure", + "tip": "Declare a retained widget tree as data; the engine lays it out and draws it.", + "page": "kw-ui.html" + }, + "kw-spawn": { + "id": "kw-spawn", + "name": "spawn", + "sig": "spawn Label { Prop { field: v }; … }", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Create a model instance carrying the listed properties, seeding their fields.", + "page": "kw-spawn.html" + }, + "kw-despawn": { + "id": "kw-despawn", + "name": "despawn", + "sig": "despawn entity", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Remove a model instance from the world, firing any @OnDespawn hooks.", + "page": "kw-despawn.html" + }, + "kw-query": { + "id": "kw-query", + "name": "query", + "sig": "query [PropA, PropB, {Tag}]", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Match every model instance carrying all the listed properties.", + "page": "kw-query.html" + }, + "kw-self": { + "id": "kw-self", + "name": "self", + "sig": "self()", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "The model instance currently bound by the enclosing query loop.", + "page": "kw-self.html" + }, + "kw-enable": { + "id": "kw-enable", + "name": "enable", + "sig": "enable Prop on entity", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Re-activate a disabled property, model, or handler — its data is intact.", + "page": "kw-enable.html" + }, + "kw-disable": { + "id": "kw-disable", + "name": "disable", + "sig": "disable Prop on entity", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Deactivate a property, model, or handler without destroying its data.", + "page": "kw-disable.html" + }, + "kw-attach": { + "id": "kw-attach", + "name": "attach", + "sig": "attach Prop on entity { overrides }", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Structurally add a property to a live model instance, seeding its fields.", + "page": "kw-attach.html" + }, + "kw-detach": { + "id": "kw-detach", + "name": "detach", + "sig": "detach Prop on entity", + "kind": "keyword", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Structurally remove a property from a live model instance.", + "page": "kw-detach.html" + }, + "fn-world_attach_dyn": { + "id": "fn-world_attach_dyn", + "name": "world_attach_dyn", + "sig": "world_attach_dyn(target, property)", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Attach a property to an instance by numeric id at runtime (dynamic ECS).", + "page": "fn-world_attach_dyn.html" + }, + "fn-world_count": { + "id": "fn-world_count", + "name": "world_count", + "sig": "world_count() -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "The total number of live model instances in the world.", + "page": "fn-world_count.html" + }, + "fn-world_detach_dyn": { + "id": "fn-world_detach_dyn", + "name": "world_detach_dyn", + "sig": "world_detach_dyn(target, property)", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Detach a property from an instance by numeric id at runtime (dynamic ECS).", + "page": "fn-world_detach_dyn.html" + }, + "fn-world_field_id": { + "id": "fn-world_field_id", + "name": "world_field_id", + "sig": "world_field_id(property, name) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Resolve a field name within a property to its numeric index.", + "page": "fn-world_field_id.html" + }, + "fn-world_get": { + "id": "fn-world_get", + "name": "world_get", + "sig": "world_get(target, property, field) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Read one field of a model instance by numeric id (reflection ABI).", + "page": "fn-world_get.html" + }, + "fn-world_has": { + "id": "fn-world_has", + "name": "world_has", + "sig": "world_has(target, property) -> bool", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Test whether a model instance currently carries a property.", + "page": "fn-world_has.html" + }, + "fn-world_kind": { + "id": "fn-world_kind", + "name": "world_kind", + "sig": "world_kind(target) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "The model id of an instance — which kind of thing it is.", + "page": "fn-world_kind.html" + }, + "fn-world_load": { + "id": "fn-world_load", + "name": "world_load", + "sig": "world_load(buf, len)", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Restore the whole world from a serialized byte buffer.", + "page": "fn-world_load.html" + }, + "fn-world_model_id": { + "id": "fn-world_model_id", + "name": "world_model_id", + "sig": "world_model_id(name) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Resolve a model's name to its stable numeric id.", + "page": "fn-world_model_id.html" + }, + "fn-world_prop_id": { + "id": "fn-world_prop_id", + "name": "world_prop_id", + "sig": "world_prop_id(name) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Resolve a property's name to its stable numeric id.", + "page": "fn-world_prop_id.html" + }, + "fn-world_query_next": { + "id": "fn-world_query_next", + "name": "world_query_next", + "sig": "world_query_next(property, cursor) -> entity", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Step to the next instance carrying a property, walking the world by id.", + "page": "fn-world_query_next.html" + }, + "fn-world_register_prop": { + "id": "fn-world_register_prop", + "name": "world_register_prop", + "sig": "world_register_prop(name, fields) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Register a brand-new property at runtime and get its id (dynamic ECS).", + "page": "fn-world_register_prop.html" + }, + "fn-world_save": { + "id": "fn-world_save", + "name": "world_save", + "sig": "world_save(buf) -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Serialize the whole world into a buffer; returns the number of bytes written.", + "page": "fn-world_save.html" + }, + "fn-world_set": { + "id": "fn-world_set", + "name": "world_set", + "sig": "world_set(target, property, field, value)", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Write one field of a model instance by numeric id (reflection ABI).", + "page": "fn-world_set.html" + }, + "fn-world_size": { + "id": "fn-world_size", + "name": "world_size", + "sig": "world_size() -> int", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "The number of live model instances in the world.", + "page": "fn-world_size.html" + }, + "fn-world_spawn": { + "id": "fn-world_spawn", + "name": "world_spawn", + "sig": "world_spawn(model) -> entity", + "kind": "builtin", + "category": "ecs", + "section": "Entities & the ECS", + "tip": "Spawn an instance of a model chosen by numeric id at runtime.", + "page": "fn-world_spawn.html" + }, + "kw-if": { + "id": "kw-if", + "name": "if / else", + "sig": "if cond { … } else { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "A branch — run one block when a condition holds, another when it doesn't.", + "page": "kw-if.html" + }, + "kw-while": { + "id": "kw-while", + "name": "while", + "sig": "while cond { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Loop as long as a condition holds, re-checking it before each pass.", + "page": "kw-while.html" + }, + "kw-for": { + "id": "kw-for", + "name": "for … in", + "sig": "for name in a .. b { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Range loop — iterate the half-open range from a up to but not including b.", + "page": "kw-for.html" + }, + "kw-in": { + "id": "kw-in", + "name": "in", + "sig": "for x in range | query", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "The part of a for loop that names what to iterate — a range or a query.", + "page": "kw-in.html" + }, + "kw-match": { + "id": "kw-match", + "name": "match", + "sig": "match value { 0, 1 => … _ => … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Multi-way branch on one value, matching one or more literals per arm.", + "page": "kw-match.html" + }, + "kw-machine": { + "id": "kw-machine", + "name": "machine", + "sig": "machine store { state Name { … } }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "An explicit state machine over an int var — dispatches on the store's value.", + "page": "kw-machine.html" + }, + "kw-state": { + "id": "kw-state", + "name": "state", + "sig": "state Name { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "One state of a machine — its body runs while the machine sits in it.", + "page": "kw-state.html" + }, + "kw-become": { + "id": "kw-become", + "name": "become", + "sig": "become Name", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Transition to another state of the enclosing machine, or to another scene.", + "page": "kw-become.html" + }, + "kw-break": { + "id": "kw-break", + "name": "break", + "sig": "break", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Leave the enclosing loop immediately.", + "page": "kw-break.html" + }, + "kw-continue": { + "id": "kw-continue", + "name": "continue", + "sig": "continue", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Skip to the next iteration of the loop.", + "page": "kw-continue.html" + }, + "kw-where": { + "id": "kw-where", + "name": "where", + "sig": "for (Binds) in query [Comps] where cond { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Filter a query loop to entities that satisfy a condition.", + "page": "kw-where.html" + }, + "kw-try": { + "id": "kw-try", + "name": "try", + "sig": "try EXPR else { … }", + "kind": "keyword", + "category": "control", + "section": "Control flow", + "tip": "Recover a fallible result as a value, with a fallback — no exceptions, no unwinding.", + "page": "kw-try.html" + }, + "kw-scene": { + "id": "kw-scene", + "name": "scene", + "sig": "scene Name [start] { on enter{} on exit{} layer … }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "A mutually-exclusive game state — a title screen, the overworld, a battle.", + "page": "kw-scene.html" + }, + "kw-layer": { + "id": "kw-layer", + "name": "layer", + "sig": "layer Name { handlers }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "A named group of handlers inside a scene; layers render in declaration order.", + "page": "kw-layer.html" + }, + "kw-on": { + "id": "kw-on", + "name": "on enter / on exit", + "sig": "on enter { … } on exit { … }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "Scene lifecycle hooks that fire as a scene becomes active or is left.", + "page": "kw-on.html" + }, + "kw-enter": { + "id": "kw-enter", + "name": "enter", + "sig": "on enter { … }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "The scene-entry hook — runs once as a scene becomes active.", + "page": "kw-enter.html" + }, + "kw-exit": { + "id": "kw-exit", + "name": "exit", + "sig": "on exit { … }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "The scene-exit hook — runs once as a scene is left.", + "page": "kw-exit.html" + }, + "kw-start": { + "id": "kw-start", + "name": "start", + "sig": "scene Name start { … }", + "kind": "keyword", + "category": "scenes", + "section": "Scenes & layers", + "tip": "Marks the one scene the game begins in.", + "page": "kw-start.html" + }, + "kw-event": { + "id": "kw-event", + "name": "event", + "sig": "event Name { field: T = default }", + "kind": "keyword", + "category": "events", + "section": "Events", + "tip": "Declare a public event carrying a flat payload that listeners react to.", + "page": "kw-event.html" + }, + "kw-emit": { + "id": "kw-emit", + "name": "emit", + "sig": "emit Name(field: value)", + "kind": "keyword", + "category": "events", + "section": "Events", + "tip": "Fire an event, running every listener; as an expression it yields the veto flag.", + "page": "kw-emit.html" + }, + "kw-cancel": { + "id": "kw-cancel", + "name": "cancel", + "sig": "cancel", + "kind": "keyword", + "category": "events", + "section": "Events", + "tip": "Inside a listener, veto the cancellable event being emitted.", + "page": "kw-cancel.html" + }, + "kw-cancellable": { + "id": "kw-cancellable", + "name": "cancellable", + "sig": "event cancellable Name { … }", + "kind": "keyword", + "category": "events", + "section": "Events", + "tip": "Marks an event whose listeners may veto it with cancel.", + "page": "kw-cancellable.html" + }, + "screen-clear": { + "id": "screen-clear", + "name": "Screen.clear", + "sig": "Screen.clear(color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Fill the whole framebuffer with one color to start a fresh frame.", + "page": "screen-clear.html" + }, + "screen-fill_rectangle": { + "id": "screen-fill_rectangle", + "name": "Screen.fill_rectangle", + "sig": "Screen.fill_rectangle(x, y, width, height, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a solid, filled rectangle at a pixel position.", + "page": "screen-fill_rectangle.html" + }, + "screen-draw_rectangle": { + "id": "screen-draw_rectangle", + "name": "Screen.draw_rectangle", + "sig": "Screen.draw_rectangle(x, y, width, height, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a one-pixel-thick rectangle outline (not filled).", + "page": "screen-draw_rectangle.html" + }, + "screen-put_pixel": { + "id": "screen-put_pixel", + "name": "Screen.put_pixel", + "sig": "Screen.put_pixel(x, y, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Set a single pixel at a pixel coordinate to one color.", + "page": "screen-put_pixel.html" + }, + "screen-draw_text": { + "id": "screen-draw_text", + "name": "Screen.draw_text", + "sig": "Screen.draw_text(x, y, text, color, scale)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a string with the built-in font at an integer scale.", + "page": "screen-draw_text.html" + }, + "screen-draw_number": { + "id": "screen-draw_number", + "name": "Screen.draw_number", + "sig": "Screen.draw_number(x, y, value, color, scale)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw an integer directly, with no string allocation or conversion.", + "page": "screen-draw_number.html" + }, + "screen-show": { + "id": "screen-show", + "name": "Screen.show", + "sig": "Screen.show()", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Present the finished frame — copy everything drawn this frame to the window.", + "page": "screen-show.html" + }, + "screen-width": { + "id": "screen-width", + "name": "Screen.width", + "sig": "Screen.width() -> int", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "The framebuffer width in pixels.", + "page": "screen-width.html" + }, + "screen-height": { + "id": "screen-height", + "name": "Screen.height", + "sig": "Screen.height() -> int", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "The framebuffer height in pixels.", + "page": "screen-height.html" + }, + "screen-status": { + "id": "screen-status", + "name": "Screen.status", + "sig": "Screen.status(text)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Set the persistent one-line status/HUD string shown by the runtime.", + "page": "screen-status.html" + }, + "screen-line": { + "id": "screen-line", + "name": "Screen.line", + "sig": "Screen.line(x1, y1, x2, y2, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a straight line between two points.", + "page": "screen-line.html" + }, + "screen-circle": { + "id": "screen-circle", + "name": "Screen.circle", + "sig": "Screen.circle(x, y, radius, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a circle outline.", + "page": "screen-circle.html" + }, + "screen-fill_circle": { + "id": "screen-fill_circle", + "name": "Screen.fill_circle", + "sig": "Screen.fill_circle(x, y, radius, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a filled disc.", + "page": "screen-fill_circle.html" + }, + "screen-triangle": { + "id": "screen-triangle", + "name": "Screen.triangle", + "sig": "Screen.triangle(x1, y1, x2, y2, x3, y3, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a triangle outline.", + "page": "screen-triangle.html" + }, + "screen-fill_triangle": { + "id": "screen-fill_triangle", + "name": "Screen.fill_triangle", + "sig": "Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw a filled triangle.", + "page": "screen-fill_triangle.html" + }, + "screen-sprite": { + "id": "screen-sprite", + "name": "Screen.sprite", + "sig": "Screen.sprite(id, x, y)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Blit a sprite at its natural size.", + "page": "screen-sprite.html" + }, + "screen-sprite_scaled": { + "id": "screen-sprite_scaled", + "name": "Screen.sprite_scaled", + "sig": "Screen.sprite_scaled(id, x, y, scale)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Blit a sprite at an integer scale.", + "page": "screen-sprite_scaled.html" + }, + "screen-oval": { + "id": "screen-oval", + "name": "Screen.oval", + "sig": "Screen.oval(x, y, rx, ry, color)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Draw an axis-aligned ellipse outline.", + "page": "screen-oval.html" + }, + "screen-camera": { + "id": "screen-camera", + "name": "Screen.camera", + "sig": "Screen.camera(x, y)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Set the world-space camera offset for the draw path.", + "page": "screen-camera.html" + }, + "screen-clip": { + "id": "screen-clip", + "name": "Screen.clip", + "sig": "Screen.clip(x, y, width, height)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Restrict drawing to a screen-space rectangle.", + "page": "screen-clip.html" + }, + "screen-clip_reset": { + "id": "screen-clip_reset", + "name": "Screen.clip_reset", + "sig": "Screen.clip_reset()", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Lift the clip rectangle (draw to the whole screen again).", + "page": "screen-clip_reset.html" + }, + "screen-blend_mode": { + "id": "screen-blend_mode", + "name": "Screen.blend_mode", + "sig": "Screen.blend_mode(mode)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Choose replace or additive pixel blending.", + "page": "screen-blend_mode.html" + }, + "screen-measure_text": { + "id": "screen-measure_text", + "name": "Screen.measure_text", + "sig": "Screen.measure_text(text) -> int", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "The pixel advance width of text in the built-in font.", + "page": "screen-measure_text.html" + }, + "screen-pixel": { + "id": "screen-pixel", + "name": "Screen.pixel", + "sig": "Screen.pixel(x, y) -> int", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "Read a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.", + "page": "screen-pixel.html" + }, + "screen-bar": { + "id": "screen-bar", + "name": "Screen.bar", + "sig": "Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:)", + "kind": "namespace-method", + "category": "screen", + "section": "Screen — drawing", + "tip": "A filled meter: value of max over a track.", + "page": "screen-bar.html" + }, + "clock-now": { + "id": "clock-now", + "name": "Clock.now", + "sig": "Clock.now() -> int", + "kind": "namespace-method", + "category": "clock", + "section": "Clock", + "tip": "The current simulated instant.", + "page": "clock-now.html" + }, + "clock-set": { + "id": "clock-set", + "name": "Clock.set", + "sig": "Clock.set(t)", + "kind": "namespace-method", + "category": "clock", + "section": "Clock", + "tip": "Set the clock to an instant.", + "page": "clock-set.html" + }, + "clock-advance": { + "id": "clock-advance", + "name": "Clock.advance", + "sig": "Clock.advance(span)", + "kind": "namespace-method", + "category": "clock", + "section": "Clock", + "tip": "Move the clock forward by a Duration.", + "page": "clock-advance.html" + }, + "clock-reset": { + "id": "clock-reset", + "name": "Clock.reset", + "sig": "Clock.reset()", + "kind": "namespace-method", + "category": "clock", + "section": "Clock", + "tip": "Reset the clock to the epoch (0).", + "page": "clock-reset.html" + }, + "collision-rects": { + "id": "collision-rects", + "name": "Collision.rects", + "sig": "Collision.rects(ax, ay, aw, ah, bx, by, bw, bh) -> bool", + "kind": "namespace-method", + "category": "collision", + "section": "Collision", + "tip": "Do two rectangles overlap?", + "page": "collision-rects.html" + }, + "collision-point_rect": { + "id": "collision-point_rect", + "name": "Collision.point_rect", + "sig": "Collision.point_rect(px, py, rx, ry, rw, rh) -> bool", + "kind": "namespace-method", + "category": "collision", + "section": "Collision", + "tip": "Is a point inside a rectangle?", + "page": "collision-point_rect.html" + }, + "collision-circles": { + "id": "collision-circles", + "name": "Collision.circles", + "sig": "Collision.circles(ax, ay, ar, bx, by, br) -> bool", + "kind": "namespace-method", + "category": "collision", + "section": "Collision", + "tip": "Do two circles overlap?", + "page": "collision-circles.html" + }, + "collision-rect_circle": { + "id": "collision-rect_circle", + "name": "Collision.rect_circle", + "sig": "Collision.rect_circle(rx, ry, rw, rh, cx, cy, cr) -> bool", + "kind": "namespace-method", + "category": "collision", + "section": "Collision", + "tip": "Does a rectangle overlap a circle?", + "page": "collision-rect_circle.html" + }, + "color-rgb": { + "id": "color-rgb", + "name": "Color.rgb", + "sig": "Color.rgb(r, g, b) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Build a color from red, green, blue.", + "page": "color-rgb.html" + }, + "color-rgba": { + "id": "color-rgba", + "name": "Color.rgba", + "sig": "Color.rgba(r, g, b, a) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Build a color with an alpha byte.", + "page": "color-rgba.html" + }, + "color-lerp": { + "id": "color-lerp", + "name": "Color.lerp", + "sig": "Color.lerp(c0, c1, t) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Blend between two colors.", + "page": "color-lerp.html" + }, + "color-darken": { + "id": "color-darken", + "name": "Color.darken", + "sig": "Color.darken(c, amount) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Scale a color toward black.", + "page": "color-darken.html" + }, + "color-lighten": { + "id": "color-lighten", + "name": "Color.lighten", + "sig": "Color.lighten(c, amount) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Scale a color toward white.", + "page": "color-lighten.html" + }, + "color-with_alpha": { + "id": "color-with_alpha", + "name": "Color.with_alpha", + "sig": "Color.with_alpha(c, a) -> int", + "kind": "namespace-method", + "category": "color", + "section": "Color functions", + "tip": "Replace a color's alpha byte.", + "page": "color-with_alpha.html" + }, + "crypto-sha256": { + "id": "crypto-sha256", + "name": "Crypto.sha256", + "sig": "Crypto.sha256(s) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "SHA-256 of a string, as 64 hex characters.", + "page": "crypto-sha256.html" + }, + "crypto-hmac_sha256": { + "id": "crypto-hmac_sha256", + "name": "Crypto.hmac_sha256", + "sig": "Crypto.hmac_sha256(key, msg) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Sign a message with a shared secret key.", + "page": "crypto-hmac_sha256.html" + }, + "crypto-verify_hmac": { + "id": "crypto-verify_hmac", + "name": "Crypto.verify_hmac", + "sig": "Crypto.verify_hmac(key, msg, mac) -> bool", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Constant-time check that a MAC matches.", + "page": "crypto-verify_hmac.html" + }, + "crypto-hex": { + "id": "crypto-hex", + "name": "Crypto.hex", + "sig": "Crypto.hex(s) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Lowercase hex of a string's bytes.", + "page": "crypto-hex.html" + }, + "crypto-ct_equal": { + "id": "crypto-ct_equal", + "name": "Crypto.ct_equal", + "sig": "Crypto.ct_equal(a, b) -> bool", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Constant-time string equality for secrets.", + "page": "crypto-ct_equal.html" + }, + "crypto-random_bytes": { + "id": "crypto-random_bytes", + "name": "Crypto.random_bytes", + "sig": "Crypto.random_bytes(n) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "n bytes from the OS CSPRNG, as a 2n-character hex string.", + "page": "crypto-random_bytes.html" + }, + "crypto-random_hex": { + "id": "crypto-random_hex", + "name": "Crypto.random_hex", + "sig": "Crypto.random_hex(n) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Alias for random_bytes — n secure bytes as a 2n-char hex string.", + "page": "crypto-random_hex.html" + }, + "crypto-random_u32": { + "id": "crypto-random_u32", + "name": "Crypto.random_u32", + "sig": "Crypto.random_u32() -> int", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "One CSPRNG-drawn 32-bit integer.", + "page": "crypto-random_u32.html" + }, + "crypto-base64": { + "id": "crypto-base64", + "name": "Crypto.base64", + "sig": "Crypto.base64(s) -> string", + "kind": "namespace-method", + "category": "crypto", + "section": "Crypto", + "tip": "Standard base64 (RFC 4648) of a string's bytes.", + "page": "crypto-base64.html" + }, + "date-new": { + "id": "date-new", + "name": "Date.new", + "sig": "Date.new(year, month, day) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "A calendar day as days-since-1970.", + "page": "date-new.html" + }, + "date-year": { + "id": "date-year", + "name": "Date.year", + "sig": "Date.year(date) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "The calendar year of a date.", + "page": "date-year.html" + }, + "date-month": { + "id": "date-month", + "name": "Date.month", + "sig": "Date.month(date) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "The month (1-12) of a date.", + "page": "date-month.html" + }, + "date-day": { + "id": "date-day", + "name": "Date.day", + "sig": "Date.day(date) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "The day of the month (1-31) of a date.", + "page": "date-day.html" + }, + "date-weekday": { + "id": "date-weekday", + "name": "Date.weekday", + "sig": "Date.weekday(date) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "Day of the week, 0=Sunday..6=Saturday.", + "page": "date-weekday.html" + }, + "date-is_leap": { + "id": "date-is_leap", + "name": "Date.is_leap", + "sig": "Date.is_leap(year) -> bool", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "True if the year is a leap year.", + "page": "date-is_leap.html" + }, + "date-days_in_month": { + "id": "date-days_in_month", + "name": "Date.days_in_month", + "sig": "Date.days_in_month(year, month) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "Number of days in a given month.", + "page": "date-days_in_month.html" + }, + "date-to_epoch": { + "id": "date-to_epoch", + "name": "Date.to_epoch", + "sig": "Date.to_epoch(date) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "Midnight UTC of the day, as a DateTime.", + "page": "date-to_epoch.html" + }, + "date-add_days": { + "id": "date-add_days", + "name": "Date.add_days", + "sig": "Date.add_days(date, n) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "The date n days later (n may be negative).", + "page": "date-add_days.html" + }, + "date-diff_days": { + "id": "date-diff_days", + "name": "Date.diff_days", + "sig": "Date.diff_days(a, b) -> int", + "kind": "namespace-method", + "category": "date", + "section": "Date", + "tip": "Whole days from b to a (a - b).", + "page": "date-diff_days.html" + }, + "datetime-from": { + "id": "datetime-from", + "name": "DateTime.from", + "sig": "DateTime.from(year, month, day, hour, minute, second) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "Build an instant from its calendar parts.", + "page": "datetime-from.html" + }, + "datetime-date": { + "id": "datetime-date", + "name": "DateTime.date", + "sig": "DateTime.date(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The calendar day the instant falls on.", + "page": "datetime-date.html" + }, + "datetime-add": { + "id": "datetime-add", + "name": "DateTime.add", + "sig": "DateTime.add(dt, span) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The instant `span` seconds later.", + "page": "datetime-add.html" + }, + "datetime-year": { + "id": "datetime-year", + "name": "DateTime.year", + "sig": "DateTime.year(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The calendar year of an instant.", + "page": "datetime-year.html" + }, + "datetime-month": { + "id": "datetime-month", + "name": "DateTime.month", + "sig": "DateTime.month(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The month (1-12) of an instant.", + "page": "datetime-month.html" + }, + "datetime-day": { + "id": "datetime-day", + "name": "DateTime.day", + "sig": "DateTime.day(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The day of the month (1-31) of an instant.", + "page": "datetime-day.html" + }, + "datetime-weekday": { + "id": "datetime-weekday", + "name": "DateTime.weekday", + "sig": "DateTime.weekday(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "Day of the week, 0=Sunday..6=Saturday.", + "page": "datetime-weekday.html" + }, + "datetime-hour": { + "id": "datetime-hour", + "name": "DateTime.hour", + "sig": "DateTime.hour(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The hour of day (0-23) of an instant.", + "page": "datetime-hour.html" + }, + "datetime-minute": { + "id": "datetime-minute", + "name": "DateTime.minute", + "sig": "DateTime.minute(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The minute of the hour (0-59) of an instant.", + "page": "datetime-minute.html" + }, + "datetime-second": { + "id": "datetime-second", + "name": "DateTime.second", + "sig": "DateTime.second(dt) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "The second of the minute (0-59) of an instant.", + "page": "datetime-second.html" + }, + "datetime-format": { + "id": "datetime-format", + "name": "DateTime.format", + "sig": "DateTime.format(dt, pattern) -> string", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "Render an instant as text using a token pattern.", + "page": "datetime-format.html" + }, + "datetime-parse": { + "id": "datetime-parse", + "name": "DateTime.parse", + "sig": "DateTime.parse(text, pattern) -> int", + "kind": "namespace-method", + "category": "datetime", + "section": "DateTime", + "tip": "Parse text into an instant; -1 on failure.", + "page": "datetime-parse.html" + }, + "duration-seconds": { + "id": "duration-seconds", + "name": "Duration.seconds", + "sig": "Duration.seconds(n) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "A span of n whole seconds.", + "page": "duration-seconds.html" + }, + "duration-minutes": { + "id": "duration-minutes", + "name": "Duration.minutes", + "sig": "Duration.minutes(n) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "A span of n minutes, in seconds.", + "page": "duration-minutes.html" + }, + "duration-hours": { + "id": "duration-hours", + "name": "Duration.hours", + "sig": "Duration.hours(n) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "A span of n hours, in seconds.", + "page": "duration-hours.html" + }, + "duration-days": { + "id": "duration-days", + "name": "Duration.days", + "sig": "Duration.days(n) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "A span of n days, in seconds.", + "page": "duration-days.html" + }, + "duration-as_seconds": { + "id": "duration-as_seconds", + "name": "Duration.as_seconds", + "sig": "Duration.as_seconds(span) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "The span in whole seconds.", + "page": "duration-as_seconds.html" + }, + "duration-as_minutes": { + "id": "duration-as_minutes", + "name": "Duration.as_minutes", + "sig": "Duration.as_minutes(span) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "The span in whole minutes.", + "page": "duration-as_minutes.html" + }, + "duration-as_hours": { + "id": "duration-as_hours", + "name": "Duration.as_hours", + "sig": "Duration.as_hours(span) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "The span in whole hours.", + "page": "duration-as_hours.html" + }, + "duration-as_days": { + "id": "duration-as_days", + "name": "Duration.as_days", + "sig": "Duration.as_days(span) -> int", + "kind": "namespace-method", + "category": "duration", + "section": "Duration", + "tip": "The span in whole days.", + "page": "duration-as_days.html" + }, + "ease-in": { + "id": "ease-in", + "name": "Ease.in", + "sig": "Ease.in(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Accelerate from rest (quadratic ease-in).", + "page": "ease-in.html" + }, + "ease-out": { + "id": "ease-out", + "name": "Ease.out", + "sig": "Ease.out(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Decelerate to rest (quadratic ease-out).", + "page": "ease-out.html" + }, + "ease-in_out": { + "id": "ease-in_out", + "name": "Ease.in_out", + "sig": "Ease.in_out(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Ease in and then out (smoothstep).", + "page": "ease-in_out.html" + }, + "ease-back": { + "id": "ease-back", + "name": "Ease.back", + "sig": "Ease.back(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Ease in with a small backward anticipation.", + "page": "ease-back.html" + }, + "ease-bounce": { + "id": "ease-bounce", + "name": "Ease.bounce", + "sig": "Ease.bounce(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Ease out with a settling bounce.", + "page": "ease-bounce.html" + }, + "ease-elastic": { + "id": "ease-elastic", + "name": "Ease.elastic", + "sig": "Ease.elastic(t) -> fixed", + "kind": "namespace-method", + "category": "ease", + "section": "Ease", + "tip": "Ease out with a springy wobble.", + "page": "ease-elastic.html" + }, + "fs-exists": { + "id": "fs-exists", + "name": "Fs.exists", + "sig": "Fs.exists(p) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Does a path exist?", + "page": "fs-exists.html" + }, + "fs-is_dir": { + "id": "fs-is_dir", + "name": "Fs.is_dir", + "sig": "Fs.is_dir(p) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Is a path a directory?", + "page": "fs-is_dir.html" + }, + "fs-read_text": { + "id": "fs-read_text", + "name": "Fs.read_text", + "sig": "Fs.read_text(p) -> string", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Read a whole file as a string.", + "page": "fs-read_text.html" + }, + "fs-write_text": { + "id": "fs-write_text", + "name": "Fs.write_text", + "sig": "Fs.write_text(p, s) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Write a string to a file (atomically).", + "page": "fs-write_text.html" + }, + "fs-append_text": { + "id": "fs-append_text", + "name": "Fs.append_text", + "sig": "Fs.append_text(p, s) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Append a string to a file.", + "page": "fs-append_text.html" + }, + "fs-remove": { + "id": "fs-remove", + "name": "Fs.remove", + "sig": "Fs.remove(p) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Delete a file.", + "page": "fs-remove.html" + }, + "fs-size": { + "id": "fs-size", + "name": "Fs.size", + "sig": "Fs.size(p) -> int", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "File size in bytes.", + "page": "fs-size.html" + }, + "fs-mkdir": { + "id": "fs-mkdir", + "name": "Fs.mkdir", + "sig": "Fs.mkdir(p) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Create a directory and its parents.", + "page": "fs-mkdir.html" + }, + "fs-copy": { + "id": "fs-copy", + "name": "Fs.copy", + "sig": "Fs.copy(src, dst) -> bool", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Copy a file byte-for-byte.", + "page": "fs-copy.html" + }, + "fs-list": { + "id": "fs-list", + "name": "Fs.list", + "sig": "Fs.list(dir) -> []string", + "kind": "namespace-method", + "category": "fs", + "section": "Fs", + "tip": "Directory entries, sorted.", + "page": "fs-list.html" + }, + "hash-of": { + "id": "hash-of", + "name": "Hash.of", + "sig": "Hash.of(s) -> int", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "The fast default string hash.", + "page": "hash-of.html" + }, + "hash-fnv1a": { + "id": "hash-fnv1a", + "name": "Hash.fnv1a", + "sig": "Hash.fnv1a(s) -> int", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "FNV-1a 32-bit, named explicitly.", + "page": "hash-fnv1a.html" + }, + "hash-crc32": { + "id": "hash-crc32", + "name": "Hash.crc32", + "sig": "Hash.crc32(s) -> int", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "CRC-32 checksum for corruption detection.", + "page": "hash-crc32.html" + }, + "hash-mix": { + "id": "hash-mix", + "name": "Hash.mix", + "sig": "Hash.mix(x) -> int", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "Avalanche one integer into a well-scrambled value.", + "page": "hash-mix.html" + }, + "hash-combine": { + "id": "hash-combine", + "name": "Hash.combine", + "sig": "Hash.combine(...) -> int", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "Fold several ints into one deterministic seed.", + "page": "hash-combine.html" + }, + "hash-of64": { + "id": "hash-of64", + "name": "Hash.of64", + "sig": "Hash.of64(s) -> long", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "The fast default 64-bit string hash.", + "page": "hash-of64.html" + }, + "hash-fnv1a_64": { + "id": "hash-fnv1a_64", + "name": "Hash.fnv1a_64", + "sig": "Hash.fnv1a_64(s) -> long", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "FNV-1a 64-bit, named explicitly.", + "page": "hash-fnv1a_64.html" + }, + "hash-mix64": { + "id": "hash-mix64", + "name": "Hash.mix64", + "sig": "Hash.mix64(x) -> long", + "kind": "namespace-method", + "category": "hash", + "section": "Hash", + "tip": "Avalanche one 64-bit integer into a well-scrambled value.", + "page": "hash-mix64.html" + }, + "input-key": { + "id": "input-key", + "name": "Input.key", + "sig": "Input.key() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The key pressed this frame as a character code (0 when nothing is pressed).", + "page": "input-key.html" + }, + "input-bind": { + "id": "input-bind", + "name": "Input.bind", + "sig": "Input.bind(action: str, key: int) -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Bind a physical key to a named action, so gameplay reads the action, not the key.", + "page": "input-bind.html" + }, + "input-rebind": { + "id": "input-rebind", + "name": "Input.rebind", + "sig": "Input.rebind(action: str, from: int, to: int) -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Remap an action from one key to another at runtime (rebinding menus).", + "page": "input-rebind.html" + }, + "input-poll": { + "id": "input-poll", + "name": "Input.poll", + "sig": "Input.poll() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Advance one frame of input; the single per-frame read behind actions and replay.", + "page": "input-poll.html" + }, + "input-down": { + "id": "input-down", + "name": "Input.down", + "sig": "Input.down(action: str) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a named action held on the frame last polled?", + "page": "input-down.html" + }, + "input-pressed": { + "id": "input-pressed", + "name": "Input.pressed", + "sig": "Input.pressed(action: str) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Did a named action go down this frame (a one-shot edge)?", + "page": "input-pressed.html" + }, + "input-record": { + "id": "input-record", + "name": "Input.record", + "sig": "Input.record() -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Start recording polled input each frame (for deterministic replay).", + "page": "input-record.html" + }, + "input-replay": { + "id": "input-replay", + "name": "Input.replay", + "sig": "Input.replay() -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Replay recorded input; poll then reads the tape, not the device.", + "page": "input-replay.html" + }, + "input-key_down": { + "id": "input-key_down", + "name": "Input.key_down", + "sig": "Input.key_down(key) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a physical key held this frame?", + "page": "input-key_down.html" + }, + "input-key_pressed": { + "id": "input-key_pressed", + "name": "Input.key_pressed", + "sig": "Input.key_pressed(key) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Did a key go down this frame (edge)?", + "page": "input-key_pressed.html" + }, + "input-key_released": { + "id": "input-key_released", + "name": "Input.key_released", + "sig": "Input.key_released(key) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Did a key go up this frame (edge)?", + "page": "input-key_released.html" + }, + "input-key_label": { + "id": "input-key_label", + "name": "Input.key_label", + "sig": "Input.key_label(key) -> string", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The name to show a player for a key - in their own keyboard layout.", + "page": "input-key_label.html" + }, + "input-press": { + "id": "input-press", + "name": "Input.press", + "sig": "Input.press(key)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Inject a held key (AI, tutorial, testing, network).", + "page": "input-press.html" + }, + "input-release": { + "id": "input-release", + "name": "Input.release", + "sig": "Input.release(key)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Release an injected key.", + "page": "input-release.html" + }, + "input-axis": { + "id": "input-axis", + "name": "Input.axis", + "sig": "Input.axis(neg, pos) -> fixed", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "A -1..+1 axis from two keys.", + "page": "input-axis.html" + }, + "input-axis_i": { + "id": "input-axis_i", + "name": "Input.axis_i", + "sig": "Input.axis_i(neg, pos) -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "A -1/0/1 directional intent as a plain int, no bool->int glue.", + "page": "input-axis_i.html" + }, + "input-vector": { + "id": "input-vector", + "name": "Input.vector", + "sig": "Input.vector(left, right, up, down) -> Vector", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "A normalized 2D vector from four keys.", + "page": "input-vector.html" + }, + "input-strength": { + "id": "input-strength", + "name": "Input.strength", + "sig": "Input.strength(action) -> fixed", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "0..1 strength of a named action.", + "page": "input-strength.html" + }, + "input-mouse_x": { + "id": "input-mouse_x", + "name": "Input.mouse_x", + "sig": "Input.mouse_x() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The mouse x position this frame.", + "page": "input-mouse_x.html" + }, + "input-mouse_y": { + "id": "input-mouse_y", + "name": "Input.mouse_y", + "sig": "Input.mouse_y() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The mouse y position this frame.", + "page": "input-mouse_y.html" + }, + "input-mouse_dx": { + "id": "input-mouse_dx", + "name": "Input.mouse_dx", + "sig": "Input.mouse_dx() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Mouse x movement since the last poll.", + "page": "input-mouse_dx.html" + }, + "input-mouse_dy": { + "id": "input-mouse_dy", + "name": "Input.mouse_dy", + "sig": "Input.mouse_dy() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Mouse y movement since the last poll.", + "page": "input-mouse_dy.html" + }, + "input-mouse_down": { + "id": "input-mouse_down", + "name": "Input.mouse_down", + "sig": "Input.mouse_down(button) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a mouse button held?", + "page": "input-mouse_down.html" + }, + "input-wheel": { + "id": "input-wheel", + "name": "Input.wheel", + "sig": "Input.wheel() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Scroll-wheel delta this frame.", + "page": "input-wheel.html" + }, + "input-set_mouse": { + "id": "input-set_mouse", + "name": "Input.set_mouse", + "sig": "Input.set_mouse(x, y, buttons, wheel)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Inject the mouse state (headless / AI / testing).", + "page": "input-set_mouse.html" + }, + "input-pad_connected": { + "id": "input-pad_connected", + "name": "Input.pad_connected", + "sig": "Input.pad_connected(pad) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a gamepad connected?", + "page": "input-pad_connected.html" + }, + "input-pad_button": { + "id": "input-pad_button", + "name": "Input.pad_button", + "sig": "Input.pad_button(pad, button) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a gamepad button held?", + "page": "input-pad_button.html" + }, + "input-pad_axis": { + "id": "input-pad_axis", + "name": "Input.pad_axis", + "sig": "Input.pad_axis(pad, axis) -> fixed", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "A gamepad analog axis, -1..+1.", + "page": "input-pad_axis.html" + }, + "input-set_pad": { + "id": "input-set_pad", + "name": "Input.set_pad", + "sig": "Input.set_pad(pad, connected, buttons, lx, ly, rx, ry)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Inject a gamepad's whole state.", + "page": "input-set_pad.html" + }, + "input-touch_count": { + "id": "input-touch_count", + "name": "Input.touch_count", + "sig": "Input.touch_count() -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "How many touch points are active?", + "page": "input-touch_count.html" + }, + "input-touch_x": { + "id": "input-touch_x", + "name": "Input.touch_x", + "sig": "Input.touch_x(index) -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The x of a touch point.", + "page": "input-touch_x.html" + }, + "input-touch_y": { + "id": "input-touch_y", + "name": "Input.touch_y", + "sig": "Input.touch_y(index) -> int", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The y of a touch point.", + "page": "input-touch_y.html" + }, + "input-set_touch": { + "id": "input-set_touch", + "name": "Input.set_touch", + "sig": "Input.set_touch(index, x, y, active)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Inject a touch point.", + "page": "input-set_touch.html" + }, + "input-action": { + "id": "input-action", + "name": "Input.action", + "sig": "Input.action(action: str, key: int) -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Register a default key binding for an action (kept if already bound).", + "page": "input-action.html" + }, + "input-bind_pad": { + "id": "input-bind_pad", + "name": "Input.bind_pad", + "sig": "Input.bind_pad(action: str, button: int) -> void", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Also fire a named action from a gamepad button (device-agnostic).", + "page": "input-bind_pad.html" + }, + "input-active": { + "id": "input-active", + "name": "Input.active", + "sig": "Input.active(action: str) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Is a named action active right now (any bound key held, or pad button down)?", + "page": "input-active.html" + }, + "input-just_pressed": { + "id": "input-just_pressed", + "name": "Input.just_pressed", + "sig": "Input.just_pressed(action: str) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Did a named action go active this frame (the deterministic on-press)?", + "page": "input-just_pressed.html" + }, + "input-just_released": { + "id": "input-just_released", + "name": "Input.just_released", + "sig": "Input.just_released(action: str) -> bool", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Did a named action go inactive this frame (the on-release edge)?", + "page": "input-just_released.html" + }, + "input-cursor_mode": { + "id": "input-cursor_mode", + "name": "Input.cursor_mode", + "sig": "Input.cursor_mode(mode)", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "Hide / lock / confine the OS mouse cursor to the window (windowed).", + "page": "input-cursor_mode.html" + }, + "input-move_i": { + "id": "input-move_i", + "name": "Input.move_i", + "sig": "Input.move_i() -> IVec2", + "kind": "namespace-method", + "category": "input", + "section": "Input", + "tip": "The standard top-down movement intent, -1/0/1 per axis.", + "page": "input-move_i.html" + }, + "list-len": { + "id": "list-len", + "name": "List.len", + "sig": "List.len(s) -> int", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "The number of elements in a slice.", + "page": "list-len.html" + }, + "list-push": { + "id": "list-push", + "name": "List.push", + "sig": "List.push(s, v) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Append an element to the end of a slice.", + "page": "list-push.html" + }, + "list-clear": { + "id": "list-clear", + "name": "List.clear", + "sig": "List.clear(s) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Remove all elements, keeping capacity.", + "page": "list-clear.html" + }, + "list-first": { + "id": "list-first", + "name": "List.first", + "sig": "List.first(s) -> T", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "The first element of a slice.", + "page": "list-first.html" + }, + "list-last": { + "id": "list-last", + "name": "List.last", + "sig": "List.last(s) -> T", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "The last element of a slice.", + "page": "list-last.html" + }, + "list-pop": { + "id": "list-pop", + "name": "List.pop", + "sig": "List.pop(s) -> T", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Remove and return the last element.", + "page": "list-pop.html" + }, + "list-swap": { + "id": "list-swap", + "name": "List.swap", + "sig": "List.swap(s, i, j) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Exchange the elements at two indices.", + "page": "list-swap.html" + }, + "list-contains": { + "id": "list-contains", + "name": "List.contains", + "sig": "List.contains(s, v) -> bool", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Whether a value appears in a slice.", + "page": "list-contains.html" + }, + "list-index_of": { + "id": "list-index_of", + "name": "List.index_of", + "sig": "List.index_of(s, v) -> int", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "The index of a value in a slice, or -1 if absent.", + "page": "list-index_of.html" + }, + "list-reverse": { + "id": "list-reverse", + "name": "List.reverse", + "sig": "List.reverse(s) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Reverse the order of a slice in place.", + "page": "list-reverse.html" + }, + "list-insert": { + "id": "list-insert", + "name": "List.insert", + "sig": "List.insert(s, i, v) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Insert an element at an index, shifting the rest up.", + "page": "list-insert.html" + }, + "list-remove_at": { + "id": "list-remove_at", + "name": "List.remove_at", + "sig": "List.remove_at(s, i) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Remove the element at an index, shifting the rest down.", + "page": "list-remove_at.html" + }, + "list-remove": { + "id": "list-remove", + "name": "List.remove", + "sig": "List.remove(s, v) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Remove the first element equal to a value.", + "page": "list-remove.html" + }, + "list-sort": { + "id": "list-sort", + "name": "List.sort", + "sig": "List.sort(s) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Sort a slice in ascending order, in place.", + "page": "list-sort.html" + }, + "list-sort_by": { + "id": "list-sort_by", + "name": "List.sort_by", + "sig": "List.sort_by(s, key) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Sort a slice ascending by a key each element maps to.", + "page": "list-sort_by.html" + }, + "list-sort_desc_by": { + "id": "list-sort_desc_by", + "name": "List.sort_desc_by", + "sig": "List.sort_desc_by(s, key) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Sort a slice descending by a key each element maps to.", + "page": "list-sort_desc_by.html" + }, + "list-sort_with": { + "id": "list-sort_with", + "name": "List.sort_with", + "sig": "List.sort_with(s, cmp) -> void", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Sort a slice with a full two-argument comparator.", + "page": "list-sort_with.html" + }, + "list-sample": { + "id": "list-sample", + "name": "List.sample", + "sig": "List.sample(pool, count) -> []int", + "kind": "namespace-method", + "category": "list", + "section": "List", + "tip": "Random picks from an int slice, distinct while it can.", + "page": "list-sample.html" + }, + "log-trace": { + "id": "log-trace", + "name": "Log.trace", + "sig": "Log.trace(msg, [key, value]...) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Log at the trace level (0) — the most verbose.", + "page": "log-trace.html" + }, + "log-debug": { + "id": "log-debug", + "name": "Log.debug", + "sig": "Log.debug(msg, [key, value]...) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Log at the debug level (1) — development detail.", + "page": "log-debug.html" + }, + "log-info": { + "id": "log-info", + "name": "Log.info", + "sig": "Log.info(msg, [key, value]...) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Log at the info level (2) — normal operation.", + "page": "log-info.html" + }, + "log-warn": { + "id": "log-warn", + "name": "Log.warn", + "sig": "Log.warn(msg, [key, value]...) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Log at the warn level (3) — something looks wrong.", + "page": "log-warn.html" + }, + "log-error": { + "id": "log-error", + "name": "Log.error", + "sig": "Log.error(msg, [key, value]...) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Log at the error level (4) — a failure.", + "page": "log-error.html" + }, + "log-set_level": { + "id": "log-set_level", + "name": "Log.set_level", + "sig": "Log.set_level(n) -> void", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "Show only messages at level n or above (0 = all).", + "page": "log-set_level.html" + }, + "log-level": { + "id": "log-level", + "name": "Log.level", + "sig": "Log.level() -> int", + "kind": "namespace-method", + "category": "log", + "section": "Log", + "tip": "The current logging threshold.", + "page": "log-level.html" + }, + "math-min": { + "id": "math-min", + "name": "Math.min", + "sig": "Math.min(a, b) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The smaller of two values.", + "page": "math-min.html" + }, + "math-max": { + "id": "math-max", + "name": "Math.max", + "sig": "Math.max(a, b) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The larger of two values.", + "page": "math-max.html" + }, + "math-abs": { + "id": "math-abs", + "name": "Math.abs", + "sig": "Math.abs(x) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The magnitude of a value, dropping its sign.", + "page": "math-abs.html" + }, + "math-clamp": { + "id": "math-clamp", + "name": "Math.clamp", + "sig": "Math.clamp(v, lo, hi) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Constrain a value to the range [lo, hi].", + "page": "math-clamp.html" + }, + "math-sign": { + "id": "math-sign", + "name": "Math.sign", + "sig": "Math.sign(x) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The sign of a value as -1, 0, or 1.", + "page": "math-sign.html" + }, + "math-floor": { + "id": "math-floor", + "name": "Math.floor", + "sig": "Math.floor(x) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Round a fixed value down to the nearest whole int.", + "page": "math-floor.html" + }, + "math-ceil": { + "id": "math-ceil", + "name": "Math.ceil", + "sig": "Math.ceil(x) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Round a fixed value up to the nearest whole int.", + "page": "math-ceil.html" + }, + "math-round": { + "id": "math-round", + "name": "Math.round", + "sig": "Math.round(x) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Round a fixed value to the nearest whole int.", + "page": "math-round.html" + }, + "math-lerp": { + "id": "math-lerp", + "name": "Math.lerp", + "sig": "Math.lerp(a, b, t) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Blend between two values by a 0..1 amount.", + "page": "math-lerp.html" + }, + "math-inverse_lerp": { + "id": "math-inverse_lerp", + "name": "Math.inverse_lerp", + "sig": "Math.inverse_lerp(a, b, v) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Find where a value sits between two endpoints as a 0..1 fraction.", + "page": "math-inverse_lerp.html" + }, + "math-remap": { + "id": "math-remap", + "name": "Math.remap", + "sig": "Math.remap(v, in0, in1, out0, out1) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Rescale a value from one range into another.", + "page": "math-remap.html" + }, + "math-sqrt": { + "id": "math-sqrt", + "name": "Math.sqrt", + "sig": "Math.sqrt(x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The square root of a fixed value.", + "page": "math-sqrt.html" + }, + "math-sin": { + "id": "math-sin", + "name": "Math.sin", + "sig": "Math.sin(radians) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Sine of an angle in radians.", + "page": "math-sin.html" + }, + "math-cos": { + "id": "math-cos", + "name": "Math.cos", + "sig": "Math.cos(radians) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Cosine of an angle in radians.", + "page": "math-cos.html" + }, + "math-tan": { + "id": "math-tan", + "name": "Math.tan", + "sig": "Math.tan(radians) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Tangent of an angle in radians.", + "page": "math-tan.html" + }, + "math-hypot": { + "id": "math-hypot", + "name": "Math.hypot", + "sig": "Math.hypot(x, y) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Length of the vector (x, y).", + "page": "math-hypot.html" + }, + "math-dist": { + "id": "math-dist", + "name": "Math.dist", + "sig": "Math.dist(x0, y0, x1, y1) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Distance between two points.", + "page": "math-dist.html" + }, + "math-dist2": { + "id": "math-dist2", + "name": "Math.dist2", + "sig": "Math.dist2(x0, y0, x1, y1) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Squared distance between two points.", + "page": "math-dist2.html" + }, + "math-deg_to_rad": { + "id": "math-deg_to_rad", + "name": "Math.deg_to_rad", + "sig": "Math.deg_to_rad(degrees) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Convert degrees to radians.", + "page": "math-deg_to_rad.html" + }, + "math-rad_to_deg": { + "id": "math-rad_to_deg", + "name": "Math.rad_to_deg", + "sig": "Math.rad_to_deg(radians) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Convert radians to degrees.", + "page": "math-rad_to_deg.html" + }, + "math-posmod": { + "id": "math-posmod", + "name": "Math.posmod", + "sig": "Math.posmod(a, m) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Modulo that is always non-negative.", + "page": "math-posmod.html" + }, + "math-wrap": { + "id": "math-wrap", + "name": "Math.wrap", + "sig": "Math.wrap(v, lo, hi) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Wrap a value into the range [lo, hi).", + "page": "math-wrap.html" + }, + "math-ping_pong": { + "id": "math-ping_pong", + "name": "Math.ping_pong", + "sig": "Math.ping_pong(t, len) -> int", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Bounce a counter back and forth in [0, len].", + "page": "math-ping_pong.html" + }, + "math-snapped": { + "id": "math-snapped", + "name": "Math.snapped", + "sig": "Math.snapped(v, step) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Round a value to the nearest multiple of step.", + "page": "math-snapped.html" + }, + "math-move_toward": { + "id": "math-move_toward", + "name": "Math.move_toward", + "sig": "Math.move_toward(from, to, delta) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "Step from one value toward another by at most delta.", + "page": "math-move_toward.html" + }, + "math-smoothstep": { + "id": "math-smoothstep", + "name": "Math.smoothstep", + "sig": "Math.smoothstep(e0, e1, x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "A smooth 0..1 ramp between two edges.", + "page": "math-smoothstep.html" + }, + "math-atan2": { + "id": "math-atan2", + "name": "Math.atan2", + "sig": "Math.atan2(y, x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The angle of the vector (x, y), in radians.", + "page": "math-atan2.html" + }, + "math-asin": { + "id": "math-asin", + "name": "Math.asin", + "sig": "Math.asin(x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The arcsine of a value, in radians.", + "page": "math-asin.html" + }, + "math-acos": { + "id": "math-acos", + "name": "Math.acos", + "sig": "Math.acos(x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The arccosine of a value, in radians.", + "page": "math-acos.html" + }, + "math-exp": { + "id": "math-exp", + "name": "Math.exp", + "sig": "Math.exp(x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "e raised to the power x.", + "page": "math-exp.html" + }, + "math-log": { + "id": "math-log", + "name": "Math.log", + "sig": "Math.log(x) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "The natural logarithm of x.", + "page": "math-log.html" + }, + "math-pow": { + "id": "math-pow", + "name": "Math.pow", + "sig": "Math.pow(base, exp) -> fixed", + "kind": "namespace-method", + "category": "math", + "section": "Math", + "tip": "base raised to a fixed exponent.", + "page": "math-pow.html" + }, + "memory-bytes": { + "id": "memory-bytes", + "name": "Memory.bytes", + "sig": "Memory.bytes(n) -> pointer", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Allocate n bytes.", + "page": "memory-bytes.html" + }, + "memory-words": { + "id": "memory-words", + "name": "Memory.words", + "sig": "Memory.words(n) -> words", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Allocate n 32-bit words.", + "page": "memory-words.html" + }, + "memory-copy": { + "id": "memory-copy", + "name": "Memory.copy", + "sig": "Memory.copy(dst, src, n) -> void", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Copy n bytes between buffers.", + "page": "memory-copy.html" + }, + "memory-fill": { + "id": "memory-fill", + "name": "Memory.fill", + "sig": "Memory.fill(buf, value, n) -> void", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Set n bytes to a value.", + "page": "memory-fill.html" + }, + "memory-peek": { + "id": "memory-peek", + "name": "Memory.peek", + "sig": "Memory.peek(buf, i) -> int", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Read one byte.", + "page": "memory-peek.html" + }, + "memory-poke": { + "id": "memory-poke", + "name": "Memory.poke", + "sig": "Memory.poke(buf, i, value) -> void", + "kind": "namespace-method", + "category": "memory", + "section": "Memory", + "tip": "Write one byte.", + "page": "memory-poke.html" + }, + "mime-of": { + "id": "mime-of", + "name": "Mime.of", + "sig": "Mime.of(path) -> string", + "kind": "namespace-method", + "category": "mime", + "section": "Mime", + "tip": "MIME type from a file extension.", + "page": "mime-of.html" + }, + "mime-sniff": { + "id": "mime-sniff", + "name": "Mime.sniff", + "sig": "Mime.sniff(path) -> string", + "kind": "namespace-method", + "category": "mime", + "section": "Mime", + "tip": "MIME type refined by magic bytes.", + "page": "mime-sniff.html" + }, + "network-send": { + "id": "network-send", + "name": "Network.send", + "sig": "Network.send(peer, buf, len) -> void", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Put a datagram on the wire.", + "page": "network-send.html" + }, + "network-poll": { + "id": "network-poll", + "name": "Network.poll", + "sig": "Network.poll(buf, cap) -> int", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Read an inbound datagram.", + "page": "network-poll.html" + }, + "network-serialize": { + "id": "network-serialize", + "name": "Network.serialize", + "sig": "Network.serialize(entity, buf) -> int", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Serialize an entity's synced state.", + "page": "network-serialize.html" + }, + "network-apply": { + "id": "network-apply", + "name": "Network.apply", + "sig": "Network.apply(entity, buf) -> int", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Apply serialized state to an entity.", + "page": "network-apply.html" + }, + "network-owner": { + "id": "network-owner", + "name": "Network.owner", + "sig": "Network.owner(entity) -> int", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "The peer that owns an entity.", + "page": "network-owner.html" + }, + "network-set_owner": { + "id": "network-set_owner", + "name": "Network.set_owner", + "sig": "Network.set_owner(entity, peer) -> void", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Assign ownership of an entity.", + "page": "network-set_owner.html" + }, + "network-is_server": { + "id": "network-is_server", + "name": "Network.is_server", + "sig": "Network.is_server() -> bool", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Is this peer the server?", + "page": "network-is_server.html" + }, + "network-is_owner": { + "id": "network-is_owner", + "name": "Network.is_owner", + "sig": "Network.is_owner(entity) -> bool", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "Does this peer own the entity?", + "page": "network-is_owner.html" + }, + "network-local_id": { + "id": "network-local_id", + "name": "Network.local_id", + "sig": "Network.local_id() -> int", + "kind": "namespace-method", + "category": "network", + "section": "Network", + "tip": "This peer's own id.", + "page": "network-local_id.html" + }, + "noise-value2": { + "id": "noise-value2", + "name": "Noise.value2", + "sig": "Noise.value2(x, y, seed) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "2D value (lattice) noise, deterministic, in [-1, 1].", + "page": "noise-value2.html" + }, + "noise-perlin2": { + "id": "noise-perlin2", + "name": "Noise.perlin2", + "sig": "Noise.perlin2(x, y, seed) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "2D Perlin gradient noise, deterministic, in [-1, 1].", + "page": "noise-perlin2.html" + }, + "noise-simplex2": { + "id": "noise-simplex2", + "name": "Noise.simplex2", + "sig": "Noise.simplex2(x, y, seed) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "2D simplex noise, the organic default, in [-1, 1].", + "page": "noise-simplex2.html" + }, + "noise-fbm2": { + "id": "noise-fbm2", + "name": "Noise.fbm2", + "sig": "Noise.fbm2(x, y, seed, octaves) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "Fractal Brownian motion — octaves of simplex, in [-1, 1].", + "page": "noise-fbm2.html" + }, + "noise-cellular2": { + "id": "noise-cellular2", + "name": "Noise.cellular2", + "sig": "Noise.cellular2(x, y, seed) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "Worley (cellular) F1 distance to the nearest cell point.", + "page": "noise-cellular2.html" + }, + "noise-cellular2_id": { + "id": "noise-cellular2_id", + "name": "Noise.cellular2_id", + "sig": "Noise.cellular2_id(x, y, seed) -> int", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "The id of the nearest Worley cell — stable per cell.", + "page": "noise-cellular2_id.html" + }, + "noise-unit": { + "id": "noise-unit", + "name": "Noise.unit", + "sig": "Noise.unit(n) -> fixed", + "kind": "namespace-method", + "category": "noise", + "section": "Noise", + "tip": "Remap a [-1,1] noise sample to [0,1].", + "page": "noise-unit.html" + }, + "os-args": { + "id": "os-args", + "name": "Os.args", + "sig": "Os.args() -> []string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Every command-line argument, as a list.", + "page": "os-args.html" + }, + "os-arg_count": { + "id": "os-arg_count", + "name": "Os.arg_count", + "sig": "Os.arg_count() -> int", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "How many command-line arguments there are.", + "page": "os-arg_count.html" + }, + "os-arg": { + "id": "os-arg", + "name": "Os.arg", + "sig": "Os.arg(i) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "The i-th command-line argument.", + "page": "os-arg.html" + }, + "os-env": { + "id": "os-env", + "name": "Os.env", + "sig": "Os.env(name) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Read an environment variable (null if unset).", + "page": "os-env.html" + }, + "os-env_or": { + "id": "os-env_or", + "name": "Os.env_or", + "sig": "Os.env_or(name, fallback) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Read an environment variable, or a fallback.", + "page": "os-env_or.html" + }, + "os-has_env": { + "id": "os-has_env", + "name": "Os.has_env", + "sig": "Os.has_env(name) -> bool", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Is an environment variable set?", + "page": "os-has_env.html" + }, + "os-set_env": { + "id": "os-set_env", + "name": "Os.set_env", + "sig": "Os.set_env(name, value) -> bool", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Set an environment variable.", + "page": "os-set_env.html" + }, + "os-unset_env": { + "id": "os-unset_env", + "name": "Os.unset_env", + "sig": "Os.unset_env(name) -> bool", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Remove an environment variable.", + "page": "os-unset_env.html" + }, + "os-exit": { + "id": "os-exit", + "name": "Os.exit", + "sig": "Os.exit(code)", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Terminate the process with a status code.", + "page": "os-exit.html" + }, + "os-platform": { + "id": "os-platform", + "name": "Os.platform", + "sig": "Os.platform() -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "The host platform id.", + "page": "os-platform.html" + }, + "os-arch": { + "id": "os-arch", + "name": "Os.arch", + "sig": "Os.arch() -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "The host CPU architecture.", + "page": "os-arch.html" + }, + "os-stdout_write": { + "id": "os-stdout_write", + "name": "Os.stdout_write", + "sig": "Os.stdout_write(s)", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Write a string to standard output.", + "page": "os-stdout_write.html" + }, + "os-stderr_write": { + "id": "os-stderr_write", + "name": "Os.stderr_write", + "sig": "Os.stderr_write(s)", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Write a string to standard error.", + "page": "os-stderr_write.html" + }, + "os-save_dir": { + "id": "os-save_dir", + "name": "Os.save_dir", + "sig": "Os.save_dir(app) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Per-user save directory for an app.", + "page": "os-save_dir.html" + }, + "os-config_dir": { + "id": "os-config_dir", + "name": "Os.config_dir", + "sig": "Os.config_dir(app) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Per-user config directory for an app.", + "page": "os-config_dir.html" + }, + "os-cache_dir": { + "id": "os-cache_dir", + "name": "Os.cache_dir", + "sig": "Os.cache_dir(app) -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "Per-user cache directory for an app.", + "page": "os-cache_dir.html" + }, + "os-temp_dir": { + "id": "os-temp_dir", + "name": "Os.temp_dir", + "sig": "Os.temp_dir() -> string", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "The system temporary directory.", + "page": "os-temp_dir.html" + }, + "os-pid": { + "id": "os-pid", + "name": "Os.pid", + "sig": "Os.pid() -> int", + "kind": "namespace-method", + "category": "os", + "section": "Os", + "tip": "This process's id.", + "page": "os-pid.html" + }, + "path-join": { + "id": "path-join", + "name": "Path.join", + "sig": "Path.join(a, b) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "Join two path segments with the separator.", + "page": "path-join.html" + }, + "path-dir": { + "id": "path-dir", + "name": "Path.dir", + "sig": "Path.dir(p) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "The directory part of a path.", + "page": "path-dir.html" + }, + "path-base": { + "id": "path-base", + "name": "Path.base", + "sig": "Path.base(p) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "The final component of a path.", + "page": "path-base.html" + }, + "path-ext": { + "id": "path-ext", + "name": "Path.ext", + "sig": "Path.ext(p) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "The file extension, including the dot.", + "page": "path-ext.html" + }, + "path-stem": { + "id": "path-stem", + "name": "Path.stem", + "sig": "Path.stem(p) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "The final component without its extension.", + "page": "path-stem.html" + }, + "path-normalize": { + "id": "path-normalize", + "name": "Path.normalize", + "sig": "Path.normalize(p) -> string", + "kind": "namespace-method", + "category": "path", + "section": "Path", + "tip": "Collapse . / .. / duplicate separators.", + "page": "path-normalize.html" + }, + "save-write": { + "id": "save-write", + "name": "Save.write", + "sig": "Save.write() -> void", + "kind": "namespace-method", + "category": "save", + "section": "Save", + "tip": "Save a snapshot of the world.", + "page": "save-write.html" + }, + "save-read": { + "id": "save-read", + "name": "Save.read", + "sig": "Save.read() -> bool", + "kind": "namespace-method", + "category": "save", + "section": "Save", + "tip": "Restore the saved snapshot.", + "page": "save-read.html" + }, + "system-run": { + "id": "system-run", + "name": "System.run", + "sig": "System.run(command) -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Run a shell command.", + "page": "system-run.html" + }, + "system-read_char": { + "id": "system-read_char", + "name": "System.read_char", + "sig": "System.read_char() -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Read one byte from standard input.", + "page": "system-read_char.html" + }, + "system-file_open": { + "id": "system-file_open", + "name": "System.file_open", + "sig": "System.file_open(path, mode) -> pointer", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Open a file.", + "page": "system-file_open.html" + }, + "system-file_read": { + "id": "system-file_read", + "name": "System.file_read", + "sig": "System.file_read(file, buf, n) -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Read bytes from a file.", + "page": "system-file_read.html" + }, + "system-file_write": { + "id": "system-file_write", + "name": "System.file_write", + "sig": "System.file_write(file, buf, n) -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Write bytes to a file.", + "page": "system-file_write.html" + }, + "system-file_seek": { + "id": "system-file_seek", + "name": "System.file_seek", + "sig": "System.file_seek(file, offset, whence) -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Move a file's read/write position.", + "page": "system-file_seek.html" + }, + "system-file_tell": { + "id": "system-file_tell", + "name": "System.file_tell", + "sig": "System.file_tell(file) -> int", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "The current file position.", + "page": "system-file_tell.html" + }, + "system-file_close": { + "id": "system-file_close", + "name": "System.file_close", + "sig": "System.file_close(file) -> void", + "kind": "namespace-method", + "category": "system", + "section": "System", + "tip": "Close a file.", + "page": "system-file_close.html" + }, + "text-length": { + "id": "text-length", + "name": "Text.length", + "sig": "Text.length(s) -> int", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "The number of bytes in a string.", + "page": "text-length.html" + }, + "text-char_at": { + "id": "text-char_at", + "name": "Text.char_at", + "sig": "Text.char_at(s, i) -> int", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "The byte value at an index, 0..255.", + "page": "text-char_at.html" + }, + "text-slice": { + "id": "text-slice", + "name": "Text.slice", + "sig": "Text.slice(s, a, b) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "A substring covering bytes [a, b).", + "page": "text-slice.html" + }, + "text-starts_with": { + "id": "text-starts_with", + "name": "Text.starts_with", + "sig": "Text.starts_with(s, prefix) -> bool", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Whether a string begins with a given prefix.", + "page": "text-starts_with.html" + }, + "text-ends_with": { + "id": "text-ends_with", + "name": "Text.ends_with", + "sig": "Text.ends_with(s, suffix) -> bool", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Whether a string ends with a given suffix.", + "page": "text-ends_with.html" + }, + "text-contains": { + "id": "text-contains", + "name": "Text.contains", + "sig": "Text.contains(s, sub) -> bool", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Whether a substring appears anywhere in a string.", + "page": "text-contains.html" + }, + "text-index_of": { + "id": "text-index_of", + "name": "Text.index_of", + "sig": "Text.index_of(s, sub) -> int", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "The byte index of a substring, or -1 if absent.", + "page": "text-index_of.html" + }, + "text-equals": { + "id": "text-equals", + "name": "Text.equals", + "sig": "Text.equals(a, b) -> bool", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Whether two strings have identical bytes.", + "page": "text-equals.html" + }, + "text-concat": { + "id": "text-concat", + "name": "Text.concat", + "sig": "Text.concat(a, b) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Join two strings into a new one.", + "page": "text-concat.html" + }, + "text-to_int": { + "id": "text-to_int", + "name": "Text.to_int", + "sig": "Text.to_int(s) -> int", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Parse a leading integer from a string.", + "page": "text-to_int.html" + }, + "text-from_int": { + "id": "text-from_int", + "name": "Text.from_int", + "sig": "Text.from_int(n) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Render an integer as a string.", + "page": "text-from_int.html" + }, + "text-upper": { + "id": "text-upper", + "name": "Text.upper", + "sig": "Text.upper(s) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "An uppercased copy of a string.", + "page": "text-upper.html" + }, + "text-lower": { + "id": "text-lower", + "name": "Text.lower", + "sig": "Text.lower(s) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "A lowercased copy of a string.", + "page": "text-lower.html" + }, + "text-trim": { + "id": "text-trim", + "name": "Text.trim", + "sig": "Text.trim(s) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "A copy with surrounding whitespace removed.", + "page": "text-trim.html" + }, + "text-repeat": { + "id": "text-repeat", + "name": "Text.repeat", + "sig": "Text.repeat(s, n) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "A string repeated n times.", + "page": "text-repeat.html" + }, + "text-pad_left": { + "id": "text-pad_left", + "name": "Text.pad_left", + "sig": "Text.pad_left(s, width) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Pad with spaces on the left to a width.", + "page": "text-pad_left.html" + }, + "text-pad_right": { + "id": "text-pad_right", + "name": "Text.pad_right", + "sig": "Text.pad_right(s, width) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Pad with spaces on the right to a width.", + "page": "text-pad_right.html" + }, + "text-split": { + "id": "text-split", + "name": "Text.split", + "sig": "Text.split(s, sep) -> []string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Split a string on a separator into a list.", + "page": "text-split.html" + }, + "text-join": { + "id": "text-join", + "name": "Text.join", + "sig": "Text.join(parts, sep) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Join a list of strings with a separator.", + "page": "text-join.html" + }, + "text-replace": { + "id": "text-replace", + "name": "Text.replace", + "sig": "Text.replace(s, from, to) -> string", + "kind": "namespace-method", + "category": "text", + "section": "Text", + "tip": "Replace every occurrence of a substring.", + "page": "text-replace.html" + }, + "time-frame": { + "id": "time-frame", + "name": "Time.frame", + "sig": "Time.frame() -> int", + "kind": "namespace-method", + "category": "time", + "section": "Time", + "tip": "The number of frames elapsed since start.", + "page": "time-frame.html" + }, + "time-delta": { + "id": "time-delta", + "name": "Time.delta", + "sig": "Time.delta() -> fixed", + "kind": "namespace-method", + "category": "time", + "section": "Time", + "tip": "Seconds per frame (a fixed 1/60).", + "page": "time-delta.html" + }, + "time-elapsed": { + "id": "time-elapsed", + "name": "Time.elapsed", + "sig": "Time.elapsed() -> fixed", + "kind": "namespace-method", + "category": "time", + "section": "Time", + "tip": "Seconds since start (deterministic).", + "page": "time-elapsed.html" + }, + "time-now": { + "id": "time-now", + "name": "Time.now", + "sig": "Time.now() -> int", + "kind": "namespace-method", + "category": "time", + "section": "Time", + "tip": "Wall-clock seconds (non-deterministic).", + "page": "time-now.html" + }, + "time-since": { + "id": "time-since", + "name": "Time.since", + "sig": "Time.since(past) -> int", + "kind": "namespace-method", + "category": "time", + "section": "Time", + "tip": "Seconds elapsed from a past instant to now (non-deterministic).", + "page": "time-since.html" + }, + "unicode-len": { + "id": "unicode-len", + "name": "Unicode.len", + "sig": "Unicode.len(s) -> int", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Number of code points in a string (not bytes).", + "page": "unicode-len.html" + }, + "unicode-byte_len": { + "id": "unicode-byte_len", + "name": "Unicode.byte_len", + "sig": "Unicode.byte_len(s) -> int", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Number of bytes in a string.", + "page": "unicode-byte_len.html" + }, + "unicode-is_valid_utf8": { + "id": "unicode-is_valid_utf8", + "name": "Unicode.is_valid_utf8", + "sig": "Unicode.is_valid_utf8(s) -> bool", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Is a string well-formed UTF-8?", + "page": "unicode-is_valid_utf8.html" + }, + "unicode-char_at": { + "id": "unicode-char_at", + "name": "Unicode.char_at", + "sig": "Unicode.char_at(s, i) -> int", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "The i-th code point of a string.", + "page": "unicode-char_at.html" + }, + "unicode-chars": { + "id": "unicode-chars", + "name": "Unicode.chars", + "sig": "Unicode.chars(s) -> []int", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Every code point of a string, in order.", + "page": "unicode-chars.html" + }, + "unicode-upper": { + "id": "unicode-upper", + "name": "Unicode.upper", + "sig": "Unicode.upper(s) -> string", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Uppercase a string.", + "page": "unicode-upper.html" + }, + "unicode-lower": { + "id": "unicode-lower", + "name": "Unicode.lower", + "sig": "Unicode.lower(s) -> string", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Lowercase a string.", + "page": "unicode-lower.html" + }, + "unicode-truncate": { + "id": "unicode-truncate", + "name": "Unicode.truncate", + "sig": "Unicode.truncate(s, n) -> string", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "First n code points of a string.", + "page": "unicode-truncate.html" + }, + "unicode-grapheme_len": { + "id": "unicode-grapheme_len", + "name": "Unicode.grapheme_len", + "sig": "Unicode.grapheme_len(s) -> int", + "kind": "namespace-method", + "category": "unicode", + "section": "Unicode", + "tip": "Number of user-perceived characters.", + "page": "unicode-grapheme_len.html" + }, + "uuid-new": { + "id": "uuid-new", + "name": "Uuid.new", + "sig": "Uuid.new() -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "A new random (v4) UUID as a canonical 36-char string.", + "page": "uuid-new.html" + }, + "uuid-v4": { + "id": "uuid-v4", + "name": "Uuid.v4", + "sig": "Uuid.v4() -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "Explicit alias for Uuid.new — a random (v4) UUID.", + "page": "uuid-v4.html" + }, + "uuid-new_v7": { + "id": "uuid-new_v7", + "name": "Uuid.new_v7", + "sig": "Uuid.new_v7() -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "A new time-ordered (v7) UUID; sorts by creation time.", + "page": "uuid-new_v7.html" + }, + "uuid-v7": { + "id": "uuid-v7", + "name": "Uuid.v7", + "sig": "Uuid.v7() -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "Explicit alias for Uuid.new_v7 — a time-ordered UUID.", + "page": "uuid-v7.html" + }, + "uuid-parse": { + "id": "uuid-parse", + "name": "Uuid.parse", + "sig": "Uuid.parse(s) -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "Normalise an untrusted string to a lowercase UUID, or the nil UUID.", + "page": "uuid-parse.html" + }, + "uuid-is_valid": { + "id": "uuid-is_valid", + "name": "Uuid.is_valid", + "sig": "Uuid.is_valid(s) -> bool", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "Is s a well-formed UUID string?", + "page": "uuid-is_valid.html" + }, + "uuid-to_text": { + "id": "uuid-to_text", + "name": "Uuid.to_text", + "sig": "Uuid.to_text(id) -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "The canonical text form of a UUID.", + "page": "uuid-to_text.html" + }, + "uuid-equals": { + "id": "uuid-equals", + "name": "Uuid.equals", + "sig": "Uuid.equals(a, b) -> bool", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "Case-insensitive UUID equality.", + "page": "uuid-equals.html" + }, + "uuid-nil": { + "id": "uuid-nil", + "name": "Uuid.nil", + "sig": "Uuid.nil() -> string", + "kind": "namespace-method", + "category": "uuid", + "section": "Uuid", + "tip": "The all-zero UUID.", + "page": "uuid-nil.html" + }, + "world-get": { + "id": "world-get", + "name": "World.get", + "sig": "World.get(prop_id, field_id, entity) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Read a component field by id.", + "page": "world-get.html" + }, + "world-set": { + "id": "world-set", + "name": "World.set", + "sig": "World.set(prop_id, field_id, entity, value) -> void", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Write a component field by id.", + "page": "world-set.html" + }, + "world-has": { + "id": "world-has", + "name": "World.has", + "sig": "World.has(entity, prop_id) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Does an entity have a component?", + "page": "world-has.html" + }, + "world-count": { + "id": "world-count", + "name": "World.count", + "sig": "World.count() -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "The number of live entities.", + "page": "world-count.html" + }, + "world-size": { + "id": "world-size", + "name": "World.size", + "sig": "World.size() -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "The serialized size of the world.", + "page": "world-size.html" + }, + "world-spawn": { + "id": "world-spawn", + "name": "World.spawn", + "sig": "World.spawn(model_id) -> entity", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Spawn an entity of a model by id.", + "page": "world-spawn.html" + }, + "world-save": { + "id": "world-save", + "name": "World.save", + "sig": "World.save(buf) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Serialize the world into a buffer.", + "page": "world-save.html" + }, + "world-load": { + "id": "world-load", + "name": "World.load", + "sig": "World.load(buf, len) -> void", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Restore the world from a buffer.", + "page": "world-load.html" + }, + "world-prop_id": { + "id": "world-prop_id", + "name": "World.prop_id", + "sig": "World.prop_id(name) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Look up a component's id by name.", + "page": "world-prop_id.html" + }, + "world-field_id": { + "id": "world-field_id", + "name": "World.field_id", + "sig": "World.field_id(prop_id, name) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Look up a field's id by name.", + "page": "world-field_id.html" + }, + "world-model_id": { + "id": "world-model_id", + "name": "World.model_id", + "sig": "World.model_id(name) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Look up a model's id by name.", + "page": "world-model_id.html" + }, + "world-kind": { + "id": "world-kind", + "name": "World.kind", + "sig": "World.kind(entity) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "The model id of an entity.", + "page": "world-kind.html" + }, + "world-register_prop": { + "id": "world-register_prop", + "name": "World.register_prop", + "sig": "World.register_prop(name, size) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Register a new component at runtime.", + "page": "world-register_prop.html" + }, + "world-attach": { + "id": "world-attach", + "name": "World.attach", + "sig": "World.attach(entity, prop_id) -> void", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Attach a component to an entity at runtime.", + "page": "world-attach.html" + }, + "world-detach": { + "id": "world-detach", + "name": "World.detach", + "sig": "World.detach(entity, prop_id) -> void", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Detach a component from an entity at runtime.", + "page": "world-detach.html" + }, + "world-query_next": { + "id": "world-query_next", + "name": "World.query_next", + "sig": "World.query_next(query_id, cursor) -> int", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Advance a reflective query.", + "page": "world-query_next.html" + }, + "world-despawn": { + "id": "world-despawn", + "name": "World.despawn", + "sig": "World.despawn(entity)", + "kind": "namespace-method", + "category": "world", + "section": "World", + "tip": "Despawn an entity by id — runs its @OnDespawn hooks and frees the slot.", + "page": "world-despawn.html" + }, + "audio-load": { + "id": "audio-load", + "name": "Audio.load", + "sig": "Audio.load(path) -> int", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Load a sound file into a handle.", + "page": "audio-load.html" + }, + "audio-play": { + "id": "audio-play", + "name": "Audio.play", + "sig": "Audio.play(id) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Fire a one-shot sound.", + "page": "audio-play.html" + }, + "audio-play_at": { + "id": "audio-play_at", + "name": "Audio.play_at", + "sig": "Audio.play_at(id, gain, pitch, pan) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Fire a one-shot with its own gain, pitch and stereo position.", + "page": "audio-play_at.html" + }, + "audio-play_sound": { + "id": "audio-play_sound", + "name": "Audio.play_sound", + "sig": "Audio.play_sound(id) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Fire a one-shot sound (alias of play).", + "page": "audio-play_sound.html" + }, + "audio-play_music": { + "id": "audio-play_music", + "name": "Audio.play_music", + "sig": "Audio.play_music(id) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Loop a sound as background music.", + "page": "audio-play_music.html" + }, + "audio-stop": { + "id": "audio-stop", + "name": "Audio.stop", + "sig": "Audio.stop(id) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Stop one sound.", + "page": "audio-stop.html" + }, + "audio-stop_music": { + "id": "audio-stop_music", + "name": "Audio.stop_music", + "sig": "Audio.stop_music() -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Stop the music channel.", + "page": "audio-stop_music.html" + }, + "audio-stop_all": { + "id": "audio-stop_all", + "name": "Audio.stop_all", + "sig": "Audio.stop_all() -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Stop every sound.", + "page": "audio-stop_all.html" + }, + "audio-volume": { + "id": "audio-volume", + "name": "Audio.volume", + "sig": "Audio.volume(v) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Set the master volume.", + "page": "audio-volume.html" + }, + "audio-pitch": { + "id": "audio-pitch", + "name": "Audio.pitch", + "sig": "Audio.pitch(v) -> void", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Set the playback rate / pitch.", + "page": "audio-pitch.html" + }, + "audio-is_playing": { + "id": "audio-is_playing", + "name": "Audio.is_playing", + "sig": "Audio.is_playing(id) -> bool", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Is this sound playing?", + "page": "audio-is_playing.html" + }, + "audio-define": { + "id": "audio-define", + "name": "Audio.define", + "sig": "Audio.define(name: s, path: p) -> int", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "Load a sound into the bank under a name.", + "page": "audio-define.html" + }, + "audio-named": { + "id": "audio-named", + "name": "Audio.named", + "sig": "Audio.named(name: s) -> int", + "kind": "namespace-method", + "category": "audio", + "section": "Audio", + "tip": "The handle registered under a name, or 0.", + "page": "audio-named.html" + }, + "random-range": { + "id": "random-range", + "name": "Random.range", + "sig": "Random.range(low, high) -> int", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "A random integer in the inclusive range [low, high].", + "page": "random-range.html" + }, + "random-chance": { + "id": "random-chance", + "name": "Random.chance", + "sig": "Random.chance(percent) -> bool", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "Return true with the given percent probability.", + "page": "random-chance.html" + }, + "random-seed": { + "id": "random-seed", + "name": "Random.seed", + "sig": "Random.seed(value)", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "Seed the random generator so runs are reproducible.", + "page": "random-seed.html" + }, + "random-value": { + "id": "random-value", + "name": "Random.value", + "sig": "Random.value() -> fixed", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "A random fixed value in [0, 1).", + "page": "random-value.html" + }, + "random-int": { + "id": "random-int", + "name": "Random.int", + "sig": "Random.int(max) -> int", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "A random integer in [0, max).", + "page": "random-int.html" + }, + "random-sign": { + "id": "random-sign", + "name": "Random.sign", + "sig": "Random.sign() -> int", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "A random +1 or -1.", + "page": "random-sign.html" + }, + "random-weighted": { + "id": "random-weighted", + "name": "Random.weighted", + "sig": "Random.weighted(weights: []int) -> int", + "kind": "namespace-method", + "category": "random", + "section": "Random", + "tip": "An index drawn in proportion to its weight.", + "page": "random-weighted.html" + }, + "vector-make": { + "id": "vector-make", + "name": "Vector.make", + "sig": "Vector.make(x, y) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Build a vector from x and y components.", + "page": "vector-make.html" + }, + "vector-zero": { + "id": "vector-zero", + "name": "Vector.zero", + "sig": "Vector.zero() -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The zero vector, (0, 0).", + "page": "vector-zero.html" + }, + "vector-x": { + "id": "vector-x", + "name": "Vector.x", + "sig": "Vector.x(v) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The x component of a vector.", + "page": "vector-x.html" + }, + "vector-y": { + "id": "vector-y", + "name": "Vector.y", + "sig": "Vector.y(v) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The y component of a vector.", + "page": "vector-y.html" + }, + "vector-add": { + "id": "vector-add", + "name": "Vector.add", + "sig": "Vector.add(a, b) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Component-wise sum a + b.", + "page": "vector-add.html" + }, + "vector-sub": { + "id": "vector-sub", + "name": "Vector.sub", + "sig": "Vector.sub(a, b) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Component-wise difference a - b.", + "page": "vector-sub.html" + }, + "vector-scale": { + "id": "vector-scale", + "name": "Vector.scale", + "sig": "Vector.scale(v, s) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Scale a vector by a scalar.", + "page": "vector-scale.html" + }, + "vector-dot": { + "id": "vector-dot", + "name": "Vector.dot", + "sig": "Vector.dot(a, b) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The dot product of two vectors.", + "page": "vector-dot.html" + }, + "vector-length": { + "id": "vector-length", + "name": "Vector.length", + "sig": "Vector.length(v) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The length (magnitude) of a vector.", + "page": "vector-length.html" + }, + "vector-distance": { + "id": "vector-distance", + "name": "Vector.distance", + "sig": "Vector.distance(a, b) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The distance between two points.", + "page": "vector-distance.html" + }, + "vector-normalize": { + "id": "vector-normalize", + "name": "Vector.normalize", + "sig": "Vector.normalize(v) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "A unit vector in the same direction.", + "page": "vector-normalize.html" + }, + "vector-lerp": { + "id": "vector-lerp", + "name": "Vector.lerp", + "sig": "Vector.lerp(a, b, t) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Linear interpolation between two vectors.", + "page": "vector-lerp.html" + }, + "vector-rotate": { + "id": "vector-rotate", + "name": "Vector.rotate", + "sig": "Vector.rotate(v, angle) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "Rotate a vector by an angle in radians.", + "page": "vector-rotate.html" + }, + "vector-angle": { + "id": "vector-angle", + "name": "Vector.angle", + "sig": "Vector.angle(v) -> fixed", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "The angle of a vector in radians.", + "page": "vector-angle.html" + }, + "vector-from_angle": { + "id": "vector-from_angle", + "name": "Vector.from_angle", + "sig": "Vector.from_angle(a) -> Vector", + "kind": "namespace-method", + "category": "vector", + "section": "Vector", + "tip": "A unit vector pointing at an angle.", + "page": "vector-from_angle.html" + }, + "http-get": { + "id": "http-get", + "name": "Http.get", + "sig": "Http.get(url) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Start an async GET request.", + "page": "http-get.html" + }, + "http-post": { + "id": "http-post", + "name": "Http.post", + "sig": "Http.post(url, body) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Start an async POST with a body.", + "page": "http-post.html" + }, + "http-request": { + "id": "http-request", + "name": "Http.request", + "sig": "Http.request(method, url, body) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Start a request with any method.", + "page": "http-request.html" + }, + "http-open": { + "id": "http-open", + "name": "Http.open", + "sig": "Http.open(method, url) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Open a request to configure before sending.", + "page": "http-open.html" + }, + "http-set": { + "id": "http-set", + "name": "Http.set", + "sig": "Http.set(handle, name, value) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Set a request header before sending.", + "page": "http-set.html" + }, + "http-body": { + "id": "http-body", + "name": "Http.body", + "sig": "Http.body(handle, body) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Set a string request body.", + "page": "http-body.html" + }, + "http-body_bytes": { + "id": "http-body_bytes", + "name": "Http.body_bytes", + "sig": "Http.body_bytes(handle, bytes, len) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Set a raw byte request body.", + "page": "http-body_bytes.html" + }, + "http-send": { + "id": "http-send", + "name": "Http.send", + "sig": "Http.send(handle) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Dispatch an opened request.", + "page": "http-send.html" + }, + "http-poll": { + "id": "http-poll", + "name": "Http.poll", + "sig": "Http.poll(handle) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Poll a request; -1 pending, 0 error, else status.", + "page": "http-poll.html" + }, + "http-status": { + "id": "http-status", + "name": "Http.status", + "sig": "Http.status(handle) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "The response HTTP status code.", + "page": "http-status.html" + }, + "http-ok": { + "id": "http-ok", + "name": "Http.ok", + "sig": "Http.ok(handle) -> bool", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Was the response status 2xx?", + "page": "http-ok.html" + }, + "http-text": { + "id": "http-text", + "name": "Http.text", + "sig": "Http.text(handle) -> str", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "The response body as text.", + "page": "http-text.html" + }, + "http-body_len": { + "id": "http-body_len", + "name": "Http.body_len", + "sig": "Http.body_len(handle) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "The response body length in bytes.", + "page": "http-body_len.html" + }, + "http-header": { + "id": "http-header", + "name": "Http.header", + "sig": "Http.header(handle, name) -> str", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "A response header value (case-insensitive).", + "page": "http-header.html" + }, + "http-free": { + "id": "http-free", + "name": "Http.free", + "sig": "Http.free(handle) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Release a request's resources.", + "page": "http-free.html" + }, + "http-parse": { + "id": "http-parse", + "name": "Http.parse", + "sig": "Http.parse(bytes, len) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Parse a raw HTTP response into a handle.", + "page": "http-parse.html" + }, + "http-save_to": { + "id": "http-save_to", + "name": "Http.save_to", + "sig": "Http.save_to(handle, path) -> void", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Stream the response body to a file.", + "page": "http-save_to.html" + }, + "http-received": { + "id": "http-received", + "name": "Http.received", + "sig": "Http.received(handle) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "Body bytes received so far.", + "page": "http-received.html" + }, + "http-expected": { + "id": "http-expected", + "name": "Http.expected", + "sig": "Http.expected(handle) -> int", + "kind": "namespace-method", + "category": "http", + "section": "Http", + "tip": "The body length the server announced, or -1.", + "page": "http-expected.html" + }, + "ivec2-make": { + "id": "ivec2-make", + "name": "IVec2.make", + "sig": "IVec2.make(x, y) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Build an integer vector from x and y components.", + "page": "ivec2-make.html" + }, + "ivec2-zero": { + "id": "ivec2-zero", + "name": "IVec2.zero", + "sig": "IVec2.zero() -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The origin cell, (0, 0).", + "page": "ivec2-zero.html" + }, + "ivec2-x": { + "id": "ivec2-x", + "name": "IVec2.x", + "sig": "IVec2.x(v) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The x component of an integer vector.", + "page": "ivec2-x.html" + }, + "ivec2-y": { + "id": "ivec2-y", + "name": "IVec2.y", + "sig": "IVec2.y(v) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The y component of an integer vector.", + "page": "ivec2-y.html" + }, + "ivec2-add": { + "id": "ivec2-add", + "name": "IVec2.add", + "sig": "IVec2.add(a, b) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Component-wise sum of two integer vectors.", + "page": "ivec2-add.html" + }, + "ivec2-sub": { + "id": "ivec2-sub", + "name": "IVec2.sub", + "sig": "IVec2.sub(a, b) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Component-wise difference of two integer vectors.", + "page": "ivec2-sub.html" + }, + "ivec2-scale": { + "id": "ivec2-scale", + "name": "IVec2.scale", + "sig": "IVec2.scale(v, s) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Multiply both components by an integer.", + "page": "ivec2-scale.html" + }, + "ivec2-dot": { + "id": "ivec2-dot", + "name": "IVec2.dot", + "sig": "IVec2.dot(a, b) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The dot product ax*bx + ay*by.", + "page": "ivec2-dot.html" + }, + "ivec2-equal": { + "id": "ivec2-equal", + "name": "IVec2.equal", + "sig": "IVec2.equal(a, b) -> bool", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "True when both components match.", + "page": "ivec2-equal.html" + }, + "ivec2-manhattan": { + "id": "ivec2-manhattan", + "name": "IVec2.manhattan", + "sig": "IVec2.manhattan(a, b) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Grid distance |dx| + |dy|.", + "page": "ivec2-manhattan.html" + }, + "ivec2-to_vector": { + "id": "ivec2-to_vector", + "name": "IVec2.to_vector", + "sig": "IVec2.to_vector(v) -> Vector", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Widen to a fixed-point Vector.", + "page": "ivec2-to_vector.html" + }, + "ivec2-distance2": { + "id": "ivec2-distance2", + "name": "IVec2.distance2", + "sig": "IVec2.distance2(a, b) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The squared distance between two points.", + "page": "ivec2-distance2.html" + }, + "ivec2-within": { + "id": "ivec2-within", + "name": "IVec2.within", + "sig": "IVec2.within(a, b, radius) -> bool", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "Are two points within a radius of each other?", + "page": "ivec2-within.html" + }, + "ivec2-heading": { + "id": "ivec2-heading", + "name": "IVec2.heading", + "sig": "IVec2.heading(from, to) -> int", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The direction from one point to another, in degrees.", + "page": "ivec2-heading.html" + }, + "ivec2-along": { + "id": "ivec2-along", + "name": "IVec2.along", + "sig": "IVec2.along(origin, degrees, distance) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "The point a distance along a heading.", + "page": "ivec2-along.html" + }, + "ivec2-step": { + "id": "ivec2-step", + "name": "IVec2.step", + "sig": "IVec2.step(degrees) -> IVec2", + "kind": "namespace-method", + "category": "ivec2", + "section": "IVec2", + "tip": "A -1/0/1 unit step along a heading.", + "page": "ivec2-step.html" + }, + "map-size": { + "id": "map-size", + "name": "Map.size", + "sig": "Map.size(width, height)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Set the tilemap dimensions in cells before filling rows.", + "page": "map-size.html" + }, + "map-row": { + "id": "map-row", + "name": "Map.row", + "sig": "Map.row(y, cells)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Fill one row of the tilemap from a string of tile characters.", + "page": "map-row.html" + }, + "map-tile": { + "id": "map-tile", + "name": "Map.tile", + "sig": "Map.tile(x, y) -> int", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Read the tile character at a cell (out-of-bounds reads answer '#').", + "page": "map-tile.html" + }, + "map-get": { + "id": "map-get", + "name": "Map.get", + "sig": "Map.get(x: tx, y: ty) -> int", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "The glyph at a cell ('#' outside the map).", + "page": "map-get.html" + }, + "map-set": { + "id": "map-set", + "name": "Map.set", + "sig": "Map.set(x: tx, y: ty, glyph: g)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Write one cell in place.", + "page": "map-set.html" + }, + "map-fill": { + "id": "map-fill", + "name": "Map.fill", + "sig": "Map.fill(glyph: g)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Every cell becomes the glyph.", + "page": "map-fill.html" + }, + "map-rect": { + "id": "map-rect", + "name": "Map.rect", + "sig": "Map.rect(x: tx, y: ty, width: w, height: h, glyph: g)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Fill a rectangle of cells.", + "page": "map-rect.html" + }, + "map-border": { + "id": "map-border", + "name": "Map.border", + "sig": "Map.border(glyph: g)", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "The outermost ring of cells.", + "page": "map-border.html" + }, + "map-random_cell": { + "id": "map-random_cell", + "name": "Map.random_cell", + "sig": "Map.random_cell(glyph: g) -> IVec2", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "A random cell holding the glyph (seeded RNG).", + "page": "map-random_cell.html" + }, + "map-is_solid": { + "id": "map-is_solid", + "name": "Map.is_solid", + "sig": "Map.is_solid(x: tx, y: ty) -> bool", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Is a cell solid, per the Solids config?", + "page": "map-is_solid.html" + }, + "map-is_solid_at": { + "id": "map-is_solid_at", + "name": "Map.is_solid_at", + "sig": "Map.is_solid_at(x: px, y: py) -> bool", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "Is the cell under a pixel position solid?", + "page": "map-is_solid_at.html" + }, + "map-width": { + "id": "map-width", + "name": "Map.width", + "sig": "Map.width() -> int", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "The map's width in cells.", + "page": "map-width.html" + }, + "map-height": { + "id": "map-height", + "name": "Map.height", + "sig": "Map.height() -> int", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "The map's height in cells.", + "page": "map-height.html" + }, + "map-random_cell_far": { + "id": "map-random_cell_far", + "name": "Map.random_cell_far", + "sig": "Map.random_cell_far(glyph:, from:, min_tiles:) -> IVec2", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "A random cell with the glyph, at least a distance from a point.", + "page": "map-random_cell_far.html" + }, + "map-to_tile": { + "id": "map-to_tile", + "name": "Map.to_tile", + "sig": "Map.to_tile(pixel: IVec2) -> IVec2", + "kind": "namespace-method", + "category": "map", + "section": "Map — tilemap", + "tip": "The tile under a pixel position.", + "page": "map-to_tile.html" + }, + "rect-make": { + "id": "rect-make", + "name": "Rect.make", + "sig": "Rect.make(x, y, w, h) -> Rect", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "Build a rectangle from a corner and a size.", + "page": "rect-make.html" + }, + "rect-x": { + "id": "rect-x", + "name": "Rect.x", + "sig": "Rect.x(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The left edge (x position).", + "page": "rect-x.html" + }, + "rect-y": { + "id": "rect-y", + "name": "Rect.y", + "sig": "Rect.y(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The top edge (y position).", + "page": "rect-y.html" + }, + "rect-w": { + "id": "rect-w", + "name": "Rect.w", + "sig": "Rect.w(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The width.", + "page": "rect-w.html" + }, + "rect-h": { + "id": "rect-h", + "name": "Rect.h", + "sig": "Rect.h(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The height.", + "page": "rect-h.html" + }, + "rect-right": { + "id": "rect-right", + "name": "Rect.right", + "sig": "Rect.right(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The right edge, x + w.", + "page": "rect-right.html" + }, + "rect-bottom": { + "id": "rect-bottom", + "name": "Rect.bottom", + "sig": "Rect.bottom(r) -> fixed", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The bottom edge, y + h.", + "page": "rect-bottom.html" + }, + "rect-center": { + "id": "rect-center", + "name": "Rect.center", + "sig": "Rect.center(r) -> Vector", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "The center point as a Vector.", + "page": "rect-center.html" + }, + "rect-contains": { + "id": "rect-contains", + "name": "Rect.contains", + "sig": "Rect.contains(r, px, py) -> bool", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "True when the point is inside.", + "page": "rect-contains.html" + }, + "rect-intersects": { + "id": "rect-intersects", + "name": "Rect.intersects", + "sig": "Rect.intersects(a, b) -> bool", + "kind": "namespace-method", + "category": "rect", + "section": "Rect", + "tip": "True when two rectangles overlap.", + "page": "rect-intersects.html" + }, + "udp-open": { + "id": "udp-open", + "name": "Udp.open", + "sig": "Udp.open(port) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Open a socket on a port.", + "page": "udp-open.html" + }, + "udp-port": { + "id": "udp-port", + "name": "Udp.port", + "sig": "Udp.port(socket) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "The port a socket is bound to.", + "page": "udp-port.html" + }, + "udp-send": { + "id": "udp-send", + "name": "Udp.send", + "sig": "Udp.send(socket, ip, port, bytes, len) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Send one datagram.", + "page": "udp-send.html" + }, + "udp-recv": { + "id": "udp-recv", + "name": "Udp.recv", + "sig": "Udp.recv(socket, bytes, cap) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Read the next waiting datagram.", + "page": "udp-recv.html" + }, + "udp-from_ip": { + "id": "udp-from_ip", + "name": "Udp.from_ip", + "sig": "Udp.from_ip(socket) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "The sender of the last datagram read.", + "page": "udp-from_ip.html" + }, + "udp-from_port": { + "id": "udp-from_port", + "name": "Udp.from_port", + "sig": "Udp.from_port(socket) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "The sender's port.", + "page": "udp-from_port.html" + }, + "udp-close": { + "id": "udp-close", + "name": "Udp.close", + "sig": "Udp.close(socket) -> void", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Close a socket.", + "page": "udp-close.html" + }, + "udp-resolve": { + "id": "udp-resolve", + "name": "Udp.resolve", + "sig": "Udp.resolve(name) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Look a host name up.", + "page": "udp-resolve.html" + }, + "udp-local_ip": { + "id": "udp-local_ip", + "name": "Udp.local_ip", + "sig": "Udp.local_ip() -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "This machine's address.", + "page": "udp-local_ip.html" + }, + "udp-ip": { + "id": "udp-ip", + "name": "Udp.ip", + "sig": "Udp.ip(text) -> int", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Parse a dotted address.", + "page": "udp-ip.html" + }, + "udp-ip_text": { + "id": "udp-ip_text", + "name": "Udp.ip_text", + "sig": "Udp.ip_text(ip) -> string", + "kind": "namespace-method", + "category": "udp", + "section": "Udp", + "tip": "Format an address.", + "page": "udp-ip_text.html" + }, + "bigint-from": { + "id": "bigint-from", + "name": "BigInt.from", + "sig": "BigInt.from(value) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Turn a plain int into a BigInt.", + "page": "bigint-from.html" + }, + "bigint-parse": { + "id": "bigint-parse", + "name": "BigInt.parse", + "sig": "BigInt.parse(text) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Parse decimal text into a BigInt.", + "page": "bigint-parse.html" + }, + "bigint-add": { + "id": "bigint-add", + "name": "BigInt.add", + "sig": "BigInt.add(a, b) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Exact sum of two big integers.", + "page": "bigint-add.html" + }, + "bigint-sub": { + "id": "bigint-sub", + "name": "BigInt.sub", + "sig": "BigInt.sub(a, b) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Exact difference of two big integers.", + "page": "bigint-sub.html" + }, + "bigint-mul": { + "id": "bigint-mul", + "name": "BigInt.mul", + "sig": "BigInt.mul(a, b) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Exact product of two big integers.", + "page": "bigint-mul.html" + }, + "bigint-neg": { + "id": "bigint-neg", + "name": "BigInt.neg", + "sig": "BigInt.neg(a) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Negate a big integer.", + "page": "bigint-neg.html" + }, + "bigint-pow": { + "id": "bigint-pow", + "name": "BigInt.pow", + "sig": "BigInt.pow(a, exp) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Raise a big integer to an int power.", + "page": "bigint-pow.html" + }, + "bigint-div": { + "id": "bigint-div", + "name": "BigInt.div", + "sig": "BigInt.div(a, d) -> BigInt", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Divide a big integer by an int (toward zero).", + "page": "bigint-div.html" + }, + "bigint-mod": { + "id": "bigint-mod", + "name": "BigInt.mod", + "sig": "BigInt.mod(a, d) -> int", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Remainder of a big integer divided by an int.", + "page": "bigint-mod.html" + }, + "bigint-cmp": { + "id": "bigint-cmp", + "name": "BigInt.cmp", + "sig": "BigInt.cmp(a, b) -> int", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Compare two big integers: -1, 0, or 1.", + "page": "bigint-cmp.html" + }, + "bigint-eq": { + "id": "bigint-eq", + "name": "BigInt.eq", + "sig": "BigInt.eq(a, b) -> bool", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "True when two big integers are equal.", + "page": "bigint-eq.html" + }, + "bigint-is_zero": { + "id": "bigint-is_zero", + "name": "BigInt.is_zero", + "sig": "BigInt.is_zero(a) -> bool", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "True when a big integer is zero.", + "page": "bigint-is_zero.html" + }, + "bigint-to_int": { + "id": "bigint-to_int", + "name": "BigInt.to_int", + "sig": "BigInt.to_int(a) -> int", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Narrow a big integer to a plain int (clamped).", + "page": "bigint-to_int.html" + }, + "bigint-str": { + "id": "bigint-str", + "name": "BigInt.str", + "sig": "BigInt.str(a) -> string", + "kind": "namespace-method", + "category": "bigint", + "section": "BigInt", + "tip": "Render a big integer as decimal text.", + "page": "bigint-str.html" + }, + "process-spawn": { + "id": "process-spawn", + "name": "Process.spawn", + "sig": "Process.spawn(path, args) -> int", + "kind": "namespace-method", + "category": "process", + "section": "Process", + "tip": "Start a program; never blocks.", + "page": "process-spawn.html" + }, + "process-poll": { + "id": "process-poll", + "name": "Process.poll", + "sig": "Process.poll(handle) -> int", + "kind": "namespace-method", + "category": "process", + "section": "Process", + "tip": "-1 while running, else the exit code.", + "page": "process-poll.html" + }, + "process-kill": { + "id": "process-kill", + "name": "Process.kill", + "sig": "Process.kill(handle) -> void", + "kind": "namespace-method", + "category": "process", + "section": "Process", + "tip": "End a running child.", + "page": "process-kill.html" + }, + "process-free": { + "id": "process-free", + "name": "Process.free", + "sig": "Process.free(handle) -> void", + "kind": "namespace-method", + "category": "process", + "section": "Process", + "tip": "Let a process handle go.", + "page": "process-free.html" + }, + "decimal-from": { + "id": "decimal-from", + "name": "Decimal.from", + "sig": "Decimal.from(value) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Turn a whole int into a Decimal.", + "page": "decimal-from.html" + }, + "decimal-parse": { + "id": "decimal-parse", + "name": "Decimal.parse", + "sig": "Decimal.parse(text) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Parse decimal text like \"19.99\".", + "page": "decimal-parse.html" + }, + "decimal-add": { + "id": "decimal-add", + "name": "Decimal.add", + "sig": "Decimal.add(a, b) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Exact sum of two decimals.", + "page": "decimal-add.html" + }, + "decimal-sub": { + "id": "decimal-sub", + "name": "Decimal.sub", + "sig": "Decimal.sub(a, b) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Exact difference of two decimals.", + "page": "decimal-sub.html" + }, + "decimal-mul": { + "id": "decimal-mul", + "name": "Decimal.mul", + "sig": "Decimal.mul(a, b) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Exact product of two decimals.", + "page": "decimal-mul.html" + }, + "decimal-neg": { + "id": "decimal-neg", + "name": "Decimal.neg", + "sig": "Decimal.neg(a) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Negate a decimal.", + "page": "decimal-neg.html" + }, + "decimal-cmp": { + "id": "decimal-cmp", + "name": "Decimal.cmp", + "sig": "Decimal.cmp(a, b) -> int", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Compare two decimals: -1, 0, or 1.", + "page": "decimal-cmp.html" + }, + "decimal-eq": { + "id": "decimal-eq", + "name": "Decimal.eq", + "sig": "Decimal.eq(a, b) -> bool", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "True when two decimals are equal in value.", + "page": "decimal-eq.html" + }, + "decimal-scale": { + "id": "decimal-scale", + "name": "Decimal.scale", + "sig": "Decimal.scale(d) -> int", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "The number of digits after the point.", + "page": "decimal-scale.html" + }, + "decimal-rescale": { + "id": "decimal-rescale", + "name": "Decimal.rescale", + "sig": "Decimal.rescale(d, places) -> Decimal", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Change precision (truncates toward zero).", + "page": "decimal-rescale.html" + }, + "decimal-str": { + "id": "decimal-str", + "name": "Decimal.str", + "sig": "Decimal.str(d) -> string", + "kind": "namespace-method", + "category": "decimal", + "section": "Decimal", + "tip": "Render a decimal as text.", + "page": "decimal-str.html" + }, + "type-int": { + "id": "type-int", + "name": "int", + "sig": "int", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "The default 32-bit signed integer — also how colors, keys, and tiles are carried.", + "page": "type-int.html" + }, + "type-fixed": { + "id": "type-fixed", + "name": "fixed", + "sig": "fixed", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "Q16.16 fixed-point for deterministic fractional math — no floats.", + "page": "type-fixed.html" + }, + "type-long": { + "id": "type-long", + "name": "long", + "sig": "long", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A 64-bit signed integer for values that overflow a 32-bit int.", + "page": "type-long.html" + }, + "type-bool": { + "id": "type-bool", + "name": "bool", + "sig": "bool", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A truth value — the result of comparisons and of and / or / not.", + "page": "type-bool.html" + }, + "type-float": { + "id": "type-float", + "name": "float", + "sig": "float", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A 32-bit IEEE floating-point number — what the GPU uses.", + "page": "type-float.html" + }, + "type-string": { + "id": "type-string", + "name": "string", + "sig": "string", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "An immutable string — compared by content, sliceable, and interpolatable.", + "page": "type-string.html" + }, + "type-double": { + "id": "type-double", + "name": "double", + "sig": "double", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A 64-bit IEEE floating-point number for precise math.", + "page": "type-double.html" + }, + "type-entity": { + "id": "type-entity", + "name": "entity", + "sig": "entity", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "The id/handle of a spawned model instance, as returned by self().", + "page": "type-entity.html" + }, + "type-pointer": { + "id": "type-pointer", + "name": "pointer", + "sig": "pointer", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A raw address into memory — a byte buffer from bytes(n), or an FFI handle.", + "page": "type-pointer.html" + }, + "type-words": { + "id": "type-words", + "name": "words", + "sig": "words", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A raw buffer indexed as 32-bit words — each buffer[i] reads or writes an int.", + "page": "type-words.html" + }, + "type-byte": { + "id": "type-byte", + "name": "byte", + "sig": "byte", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A raw buffer indexed one byte at a time — each buffer[i] reads or writes a byte.", + "page": "type-byte.html" + }, + "type-slices": { + "id": "type-slices", + "name": "[]T (slices)", + "sig": "[]T", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A growable slice of T — new []T makes one, push appends, len counts, s[i] indexes.", + "page": "type-slices.html" + }, + "type-fixeds": { + "id": "type-fixeds", + "name": "fixeds", + "sig": "fixeds", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A buffer of fixed-point values — f[i] reads and writes a fixed.", + "page": "type-fixeds.html" + }, + "type-countdown": { + "id": "type-countdown", + "name": "countdown", + "sig": "countdown", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "An int component field the engine steps toward 0 once per Update.", + "page": "type-countdown.html" + }, + "type-floats": { + "id": "type-floats", + "name": "floats", + "sig": "floats / doubles", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A buffer of floats (or doubles) — v[i] reads and writes one.", + "page": "type-floats.html" + }, + "type-pointers": { + "id": "type-pointers", + "name": "pointers", + "sig": "pointers", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A buffer of pointers — p[i] reads and writes a pointer.", + "page": "type-pointers.html" + }, + "type-vector": { + "id": "type-vector", + "name": "Vector", + "sig": "Vector", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A 2D vector — two fixed components (x, y), copied by value.", + "page": "type-vector.html" + }, + "type-ivec2": { + "id": "type-ivec2", + "name": "IVec2", + "sig": "IVec2", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "An integer 2D vector — two int components (x, y), copied by value.", + "page": "type-ivec2.html" + }, + "type-rect": { + "id": "type-rect", + "name": "Rect", + "sig": "Rect", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "A rectangle — position (x, y) and size (w, h), copied by value.", + "page": "type-rect.html" + }, + "type-void": { + "id": "type-void", + "name": "void", + "sig": "void", + "kind": "type", + "category": "types", + "section": "Types", + "tip": "The absence of a value — the return type of a function that returns nothing.", + "page": "type-void.html" + }, + "dict-new": { + "id": "dict-new", + "name": "Dict.new", + "sig": "Dict.new() -> Dict", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Create an empty string-keyed map.", + "page": "dict-new.html" + }, + "dict-set": { + "id": "dict-set", + "name": "Dict.set", + "sig": "Dict.set(d, key, value)", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Insert or update a key.", + "page": "dict-set.html" + }, + "dict-get": { + "id": "dict-get", + "name": "Dict.get", + "sig": "Dict.get(d, key) -> int", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Look up a key (0 if absent).", + "page": "dict-get.html" + }, + "dict-get_or": { + "id": "dict-get_or", + "name": "Dict.get_or", + "sig": "Dict.get_or(d, key, fallback) -> int", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Look up a key with an explicit default.", + "page": "dict-get_or.html" + }, + "dict-has": { + "id": "dict-has", + "name": "Dict.has", + "sig": "Dict.has(d, key) -> bool", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Is a key present?", + "page": "dict-has.html" + }, + "dict-remove": { + "id": "dict-remove", + "name": "Dict.remove", + "sig": "Dict.remove(d, key)", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Delete a key.", + "page": "dict-remove.html" + }, + "dict-size": { + "id": "dict-size", + "name": "Dict.size", + "sig": "Dict.size(d) -> int", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "How many keys are stored.", + "page": "dict-size.html" + }, + "dict-clear": { + "id": "dict-clear", + "name": "Dict.clear", + "sig": "Dict.clear(d)", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Remove every key.", + "page": "dict-clear.html" + }, + "dict-keys": { + "id": "dict-keys", + "name": "Dict.keys", + "sig": "Dict.keys(d) -> []pointer", + "kind": "namespace-method", + "category": "dict", + "section": "Dict", + "tip": "Every key, as a slice of strings.", + "page": "dict-keys.html" + }, + "op-arith": { + "id": "op-arith", + "name": "Arithmetic", + "sig": "+ - * / %", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Add, subtract, multiply, integer-divide, and remainder — on int and fixed.", + "page": "op-arith.html" + }, + "op-compare": { + "id": "op-compare", + "name": "Comparison", + "sig": "== != < <= > >=", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Compare two values and yield a bool; on strings == compares contents.", + "page": "op-compare.html" + }, + "op-logical": { + "id": "op-logical", + "name": "Logical", + "sig": "and or not", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "The boolean combinators, spelled as words — never && or || or a bare !.", + "page": "op-logical.html" + }, + "op-bitwise": { + "id": "op-bitwise", + "name": "Bitwise", + "sig": "& | ^ ~ << >>", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Bit-level and, or, xor, not, and shifts — with Go-style precedence.", + "page": "op-bitwise.html" + }, + "op-range": { + "id": "op-range", + "name": "Range", + "sig": "a .. b", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "A half-open range for numeric for loops — from a up to but not including b.", + "page": "op-range.html" + }, + "op-assign": { + "id": "op-assign", + "name": "Assignment", + "sig": "name = value", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Store into a var, a field, or an element — a statement, not an expression.", + "page": "op-assign.html" + }, + "op-access": { + "id": "op-access", + "name": "Member & index", + "sig": "x.field buffer[i] text[a..b]", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Field access, element index, and string slice — usable as value or target.", + "page": "op-access.html" + }, + "op-interp": { + "id": "op-interp", + "name": "String interpolation", + "sig": "`text {expr} more`", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "A backtick string with {expr} holes, each stringified and concatenated.", + "page": "op-interp.html" + }, + "op-literals": { + "id": "op-literals", + "name": "Literals", + "sig": "42 0x1E90FF 'w' \"text\" true null", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Integer, hex, character, string, boolean, and null-pointer literals.", + "page": "op-literals.html" + }, + "op-comment": { + "id": "op-comment", + "name": "Comment", + "sig": "# to end of line", + "kind": "operator", + "category": "operators", + "section": "Operators & tokens", + "tip": "Everything after # on a line is a comment, ignored by the compiler.", + "page": "op-comment.html" + }, + "fn-print": { + "id": "fn-print", + "name": "print", + "sig": "print(x)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Print an int or string followed by a newline — for headless tests and debugging.", + "page": "fn-print.html" + }, + "fn-string": { + "id": "fn-string", + "name": "string", + "sig": "string(x) -> string", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Convert an int, bool, or fixed value to text; a string passes through unchanged.", + "page": "fn-string.html" + }, + "fn-quit": { + "id": "fn-quit", + "name": "quit", + "sig": "quit()", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Stop the game loop cleanly after the current frame finishes.", + "page": "fn-quit.html" + }, + "fn-save": { + "id": "fn-save", + "name": "save", + "sig": "save()", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Serialize the whole world — every model instance, property, and program var — in one call.", + "page": "fn-save.html" + }, + "fn-load": { + "id": "fn-load", + "name": "load", + "sig": "load() -> bool", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Restore the world from a snapshot previously written by save().", + "page": "fn-load.html" + }, + "fn-len": { + "id": "fn-len", + "name": "len", + "sig": "len(x) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The number of elements in a slice, or the byte length of a string.", + "page": "fn-len.html" + }, + "fn-push": { + "id": "fn-push", + "name": "push", + "sig": "push(slice, x)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Append one element to the end of a growable slice.", + "page": "fn-push.html" + }, + "fn-abs": { + "id": "fn-abs", + "name": "abs", + "sig": "abs(a) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The absolute value of an integer (its magnitude, never negative).", + "page": "fn-abs.html" + }, + "fn-clamp": { + "id": "fn-clamp", + "name": "clamp", + "sig": "clamp(v, lo, hi) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Constrain a value to the inclusive range [lo, hi].", + "page": "fn-clamp.html" + }, + "fn-panic": { + "id": "fn-panic", + "name": "panic", + "sig": "panic(msg: string)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Abort with a located error message instead of crashing.", + "page": "fn-panic.html" + }, + "fn-assert": { + "id": "fn-assert", + "name": "assert", + "sig": "assert(cond: bool, msg: string)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Abort with a located message when an invariant is false.", + "page": "fn-assert.html" + }, + "fn-ok": { + "id": "fn-ok", + "name": "ok", + "sig": "ok(v) -> result", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Wrap a success payload in a result — the happy half of try/else.", + "page": "fn-ok.html" + }, + "fn-err": { + "id": "fn-err", + "name": "err", + "sig": "err(msg: string) -> result", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Wrap a failure message in a result — the sad half, recovered by try/else.", + "page": "fn-err.html" + }, + "fn-is_ok": { + "id": "fn-is_ok", + "name": "is_ok", + "sig": "is_ok(r: result) -> bool", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "True when a result carries a success payload.", + "page": "fn-is_ok.html" + }, + "fn-is_err": { + "id": "fn-is_err", + "name": "is_err", + "sig": "is_err(r: result) -> bool", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "True when a result carries a failure.", + "page": "fn-is_err.html" + }, + "fn-some": { + "id": "fn-some", + "name": "some", + "sig": "some(v) -> option", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Wrap a present value in an option.", + "page": "fn-some.html" + }, + "fn-none": { + "id": "fn-none", + "name": "none", + "sig": "none() -> option", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The empty option — a missing value.", + "page": "fn-none.html" + }, + "fn-is_some": { + "id": "fn-is_some", + "name": "is_some", + "sig": "is_some(o: option) -> bool", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "True when an option holds a value.", + "page": "fn-is_some.html" + }, + "fn-is_none": { + "id": "fn-is_none", + "name": "is_none", + "sig": "is_none(o: option) -> bool", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "True when an option is empty.", + "page": "fn-is_none.html" + }, + "fn-unwrap_or": { + "id": "fn-unwrap_or", + "name": "unwrap_or", + "sig": "unwrap_or(o: option, fallback) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The option value, or a fallback if empty.", + "page": "fn-unwrap_or.html" + }, + "fn-exit": { + "id": "fn-exit", + "name": "exit", + "sig": "exit(code)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Terminate the process immediately with a status code.", + "page": "fn-exit.html" + }, + "fn-run": { + "id": "fn-run", + "name": "run", + "sig": "run(cmd)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Run a string as a shell command.", + "page": "fn-run.html" + }, + "fn-getenv": { + "id": "fn-getenv", + "name": "getenv", + "sig": "getenv(name) -> string", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Read an environment variable, returning empty when it is unset.", + "page": "fn-getenv.html" + }, + "fn-read_char": { + "id": "fn-read_char", + "name": "read_char", + "sig": "read_char() -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Read one byte from standard input, or -1 at end of input.", + "page": "fn-read_char.html" + }, + "fn-arg": { + "id": "fn-arg", + "name": "arg", + "sig": "arg(i) -> string", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The i-th command-line argument as a string.", + "page": "fn-arg.html" + }, + "fn-arg_count": { + "id": "fn-arg_count", + "name": "arg_count", + "sig": "arg_count() -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The number of command-line arguments, counting the program name.", + "page": "fn-arg_count.html" + }, + "fn-bytes": { + "id": "fn-bytes", + "name": "bytes", + "sig": "bytes(n) -> pointer", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Allocate a raw buffer of n bytes and return a pointer to it.", + "page": "fn-bytes.html" + }, + "fn-file_stderr": { + "id": "fn-file_stderr", + "name": "file_stderr", + "sig": "file_stderr() -> pointer", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The standard-error stream handle for use with file_write.", + "page": "fn-file_stderr.html" + }, + "fn-file_stdout": { + "id": "fn-file_stdout", + "name": "file_stdout", + "sig": "file_stdout() -> pointer", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The standard-output stream handle for use with file_write.", + "page": "fn-file_stdout.html" + }, + "fn-file_write": { + "id": "fn-file_write", + "name": "file_write", + "sig": "file_write(handle, buf, len)", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Write a run of raw bytes to a stream handle.", + "page": "fn-file_write.html" + }, + "fn-fixed": { + "id": "fn-fixed", + "name": "fixed", + "sig": "fixed(x) -> fixed", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Convert an int (or a float) into a Q16.16 fixed-point value.", + "page": "fn-fixed.html" + }, + "fn-floor": { + "id": "fn-floor", + "name": "floor", + "sig": "floor(x) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Floor a fixed-point value down to the nearest integer.", + "page": "fn-floor.html" + }, + "fn-max": { + "id": "fn-max", + "name": "max", + "sig": "max(a, b) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The larger of two integers.", + "page": "fn-max.html" + }, + "fn-min": { + "id": "fn-min", + "name": "min", + "sig": "min(a, b) -> int", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "The smaller of two integers.", + "page": "fn-min.html" + }, + "fn-ui_build": { + "id": "fn-ui_build", + "name": "ui_build", + "sig": "ui_build()", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Build the declared UI tree so it can be opened and rendered.", + "page": "fn-ui_build.html" + }, + "fn-words": { + "id": "fn-words", + "name": "words", + "sig": "words(n) -> words", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Allocate a raw buffer of n 32-bit words, indexable with [i].", + "page": "fn-words.html" + }, + "fn-float": { + "id": "fn-float", + "name": "float", + "sig": "float(x) -> float / double(x) -> double", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Convert any number to a float (or double).", + "page": "fn-float.html" + }, + "fn-int": { + "id": "fn-int", + "name": "int", + "sig": "int(x) -> int / long(x) -> long", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "Convert a number to a whole number, truncating toward zero.", + "page": "fn-int.html" + }, + "fn-float_bits": { + "id": "fn-float_bits", + "name": "float_bits", + "sig": "float_bits(x) -> int / float_from_bits(bits) -> float", + "kind": "builtin", + "category": "builtins", + "section": "Builtin functions", + "tip": "A float's IEEE bit pattern as an int, and back.", + "page": "fn-float_bits.html" + }, + "set-new": { + "id": "set-new", + "name": "Set.new", + "sig": "Set.new() -> Set", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Create an empty string set.", + "page": "set-new.html" + }, + "set-add": { + "id": "set-add", + "name": "Set.add", + "sig": "Set.add(s, key)", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Add a member (duplicates ignored).", + "page": "set-add.html" + }, + "set-has": { + "id": "set-has", + "name": "Set.has", + "sig": "Set.has(s, key) -> bool", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Is a member present?", + "page": "set-has.html" + }, + "set-remove": { + "id": "set-remove", + "name": "Set.remove", + "sig": "Set.remove(s, key)", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Remove a member.", + "page": "set-remove.html" + }, + "set-size": { + "id": "set-size", + "name": "Set.size", + "sig": "Set.size(s) -> int", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "How many members.", + "page": "set-size.html" + }, + "set-clear": { + "id": "set-clear", + "name": "Set.clear", + "sig": "Set.clear(s)", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Remove every member.", + "page": "set-clear.html" + }, + "set-members": { + "id": "set-members", + "name": "Set.members", + "sig": "Set.members(s) -> []pointer", + "kind": "namespace-method", + "category": "set", + "section": "Set", + "tip": "Every member, as a slice of strings.", + "page": "set-members.html" + }, + "annot-queries": { + "id": "annot-queries", + "name": "@Queries", + "sig": "@Queries(these: [Position, Velocity])", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Declare the properties a handler operates on, binding their fields by name in the body.", + "page": "annot-queries.html" + }, + "annot-computed": { + "id": "annot-computed", + "name": "@Computed", + "sig": "@Computed on a property field", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "A derived field expanded inline wherever it is read, never stored.", + "page": "annot-computed.html" + }, + "annot-on": { + "id": "annot-on", + "name": "@On", + "sig": "@On(Event) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Register a compile-time listener that runs whenever an event is emitted.", + "page": "annot-on.html" + }, + "annot-export": { + "id": "annot-export", + "name": "@export", + "sig": "@export function name(…) -> R", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Expose a function as a native symbol so a host can call it.", + "page": "annot-export.html" + }, + "annot-sync": { + "id": "annot-sync", + "name": "@Sync", + "sig": "@Sync property … / @Owned", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Mark fields as replicated and models as owned so the compiler generates networking.", + "page": "annot-sync.html" + }, + "annot-handles": { + "id": "annot-handles", + "name": "@Handles", + "sig": "@Handles(Event) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Declarative hint naming the event or subsystem a handler is responsible for.", + "page": "annot-handles.html" + }, + "annot-onattach": { + "id": "annot-onattach", + "name": "@OnAttach", + "sig": "@OnAttach(Property) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler when a property is structurally attached to a live instance.", + "page": "annot-onattach.html" + }, + "annot-ondespawn": { + "id": "annot-ondespawn", + "name": "@OnDespawn", + "sig": "@OnDespawn(Model, reason: r) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler when a model instance is torn down, optionally knowing why.", + "page": "annot-ondespawn.html" + }, + "annot-ondetach": { + "id": "annot-ondetach", + "name": "@OnDetach", + "sig": "@OnDetach(Property) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler when a property is structurally detached from a live instance.", + "page": "annot-ondetach.html" + }, + "annot-ondisable": { + "id": "annot-ondisable", + "name": "@OnDisable", + "sig": "@OnDisable(Property) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler when a property is paused (disabled) on an instance.", + "page": "annot-ondisable.html" + }, + "annot-onenable": { + "id": "annot-onenable", + "name": "@OnEnable", + "sig": "@OnEnable(Property) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler when a paused property is re-enabled on an instance.", + "page": "annot-onenable.html" + }, + "annot-onquit": { + "id": "annot-onquit", + "name": "@OnQuit", + "sig": "@OnQuit handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler once at shutdown, after the last frame.", + "page": "annot-onquit.html" + }, + "annot-onspawn": { + "id": "annot-onspawn", + "name": "@OnSpawn", + "sig": "@OnSpawn(Model) handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler once each time an instance of a model is spawned.", + "page": "annot-onspawn.html" + }, + "annot-onstart": { + "id": "annot-onstart", + "name": "@OnStart", + "sig": "@OnStart handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler once at boot instead of assigning it a frame phase.", + "page": "annot-onstart.html" + }, + "annot-owned": { + "id": "annot-owned", + "name": "@Owned", + "sig": "@Owned model Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Give a model a network owner slot so its instances can be assigned to a peer.", + "page": "annot-owned.html" + }, + "annot-predicted": { + "id": "annot-predicted", + "name": "@Predicted", + "sig": "@Predicted handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a control handler on the owning client speculatively and on the server authoritatively.", + "page": "annot-predicted.html" + }, + "annot-public": { + "id": "annot-public", + "name": "@Public", + "sig": "@Public @OnSpawn(Model) handler … { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Promote a lifecycle hook to a public event other modules can listen for.", + "page": "annot-public.html" + }, + "annot-reads": { + "id": "annot-reads", + "name": "@Reads", + "sig": "@Reads(Property)", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Declare that a handler reads a property — an analysis and scheduling hint.", + "page": "annot-reads.html" + }, + "annot-server": { + "id": "annot-server", + "name": "@Server", + "sig": "@Server handler Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Run a handler only on the authority; clients receive the result via @Sync.", + "page": "annot-server.html" + }, + "annot-toclients": { + "id": "annot-toclients", + "name": "@ToClients", + "sig": "@ToClients event Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "A remote event broadcast from the server to clients — a server→clients notification.", + "page": "annot-toclients.html" + }, + "annot-toserver": { + "id": "annot-toserver", + "name": "@ToServer", + "sig": "@ToServer event Name { … }", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "A remote event sent from a client to the server — a client→server request.", + "page": "annot-toserver.html" + }, + "annot-writes": { + "id": "annot-writes", + "name": "@Writes", + "sig": "@Writes(Property)", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Declare that a handler writes a property — an analysis and scheduling hint.", + "page": "annot-writes.html" + }, + "annot-enginesystem": { + "id": "annot-enginesystem", + "name": "@EngineSystem", + "sig": "\"@EngineSystem(Component, Phase) function esys_name() { … }\"", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Register a package's engine-owned system so the frame loop runs it each phase.", + "page": "annot-enginesystem.html" + }, + "annot-namespace": { + "id": "annot-namespace", + "name": "@Namespace", + "sig": "\"@Namespace(Name) function name_method(…) -> T { … }\"", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Let a package provide a Name.method(…) namespace that dispatches to name_method.", + "page": "annot-namespace.html" + }, + "annot-clearcolor": { + "id": "annot-clearcolor", + "name": "\"@ClearColor\"", + "sig": "\"@ClearColor(0xRRGGBB) handler Draw phase Render { … }\"", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Declare a clear colour so the Render phase auto-clears + auto-presents for you.", + "page": "annot-clearcolor.html" + }, + "annot-system": { + "id": "annot-system", + "name": "@System", + "sig": "\"@System(Phase) function name() { … }\"", + "kind": "annotation", + "category": "annotations", + "section": "Annotations", + "tip": "Register a prebuilt binary module's system with the host at load, per phase.", + "page": "annot-system.html" + }, + "huge-from": { + "id": "huge-from", + "name": "Huge.from", + "sig": "Huge.from(value) -> Huge", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Turn an int into a Huge.", + "page": "huge-from.html" + }, + "huge-add": { + "id": "huge-add", + "name": "Huge.add", + "sig": "Huge.add(a, b) -> Huge", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Sum of two big numbers.", + "page": "huge-add.html" + }, + "huge-sub": { + "id": "huge-sub", + "name": "Huge.sub", + "sig": "Huge.sub(a, b) -> Huge", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Difference of two big numbers.", + "page": "huge-sub.html" + }, + "huge-mul": { + "id": "huge-mul", + "name": "Huge.mul", + "sig": "Huge.mul(a, b) -> Huge", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Product of two big numbers.", + "page": "huge-mul.html" + }, + "huge-neg": { + "id": "huge-neg", + "name": "Huge.neg", + "sig": "Huge.neg(a) -> Huge", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Negate a big number.", + "page": "huge-neg.html" + }, + "huge-cmp": { + "id": "huge-cmp", + "name": "Huge.cmp", + "sig": "Huge.cmp(a, b) -> int", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Compare two big numbers: -1, 0, or 1.", + "page": "huge-cmp.html" + }, + "huge-sign": { + "id": "huge-sign", + "name": "Huge.sign", + "sig": "Huge.sign(a) -> int", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "The sign: -1, 0, or 1.", + "page": "huge-sign.html" + }, + "huge-mantissa": { + "id": "huge-mantissa", + "name": "Huge.mantissa", + "sig": "Huge.mantissa(a) -> fixed", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "The mantissa in [1, 10).", + "page": "huge-mantissa.html" + }, + "huge-exp": { + "id": "huge-exp", + "name": "Huge.exp", + "sig": "Huge.exp(a) -> int", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "The base-10 exponent.", + "page": "huge-exp.html" + }, + "huge-str": { + "id": "huge-str", + "name": "Huge.str", + "sig": "Huge.str(a) -> string", + "kind": "namespace-method", + "category": "huge", + "section": "Huge", + "tip": "Render as scientific text like 1.23e45.", + "page": "huge-str.html" + }, + "angle-from_degrees": { + "id": "angle-from_degrees", + "name": "Angle.from_degrees", + "sig": "Angle.from_degrees(d) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Degrees to a wrapped radian angle.", + "page": "angle-from_degrees.html" + }, + "angle-to_degrees": { + "id": "angle-to_degrees", + "name": "Angle.to_degrees", + "sig": "Angle.to_degrees(a) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Radians back to degrees.", + "page": "angle-to_degrees.html" + }, + "angle-wrap": { + "id": "angle-wrap", + "name": "Angle.wrap", + "sig": "Angle.wrap(a) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Normalize any radian value to [-pi, pi).", + "page": "angle-wrap.html" + }, + "angle-sin": { + "id": "angle-sin", + "name": "Angle.sin", + "sig": "Angle.sin(a) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Sine of an angle.", + "page": "angle-sin.html" + }, + "angle-cos": { + "id": "angle-cos", + "name": "Angle.cos", + "sig": "Angle.cos(a) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Cosine of an angle.", + "page": "angle-cos.html" + }, + "angle-add": { + "id": "angle-add", + "name": "Angle.add", + "sig": "Angle.add(a, b) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Add two angles (wrapped).", + "page": "angle-add.html" + }, + "angle-diff": { + "id": "angle-diff", + "name": "Angle.diff", + "sig": "Angle.diff(a, b) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Shortest signed rotation from a to b.", + "page": "angle-diff.html" + }, + "angle-lerp": { + "id": "angle-lerp", + "name": "Angle.lerp", + "sig": "Angle.lerp(a, b, t) -> fixed", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "Interpolate along the shortest arc.", + "page": "angle-lerp.html" + }, + "angle-diff_degrees": { + "id": "angle-diff_degrees", + "name": "Angle.diff_degrees", + "sig": "Angle.diff_degrees(a: int, b: int) -> int", + "kind": "namespace-method", + "category": "angle", + "section": "Angle", + "tip": "The signed difference between two headings in whole degrees.", + "page": "angle-diff_degrees.html" + }, + "percent-clamp": { + "id": "percent-clamp", + "name": "Percent.clamp", + "sig": "Percent.clamp(v) -> fixed", + "kind": "namespace-method", + "category": "percent", + "section": "Percent", + "tip": "Clamp any fixed into [0, 1].", + "page": "percent-clamp.html" + }, + "percent-of": { + "id": "percent-of", + "name": "Percent.of", + "sig": "Percent.of(num, den) -> fixed", + "kind": "namespace-method", + "category": "percent", + "section": "Percent", + "tip": "Clamped ratio num / den.", + "page": "percent-of.html" + }, + "percent-lerp": { + "id": "percent-lerp", + "name": "Percent.lerp", + "sig": "Percent.lerp(a, b, t) -> fixed", + "kind": "namespace-method", + "category": "percent", + "section": "Percent", + "tip": "Interpolate a..b by a clamped t.", + "page": "percent-lerp.html" + }, + "percent-apply": { + "id": "percent-apply", + "name": "Percent.apply", + "sig": "Percent.apply(value, p) -> fixed", + "kind": "namespace-method", + "category": "percent", + "section": "Percent", + "tip": "Scale a value by a clamped percent.", + "page": "percent-apply.html" + }, + "regex-compile": { + "id": "regex-compile", + "name": "Regex.compile", + "sig": "Regex.compile(pattern) -> Regex", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "Compile a pattern once for reuse; returns an error value (null) if it is invalid.", + "page": "regex-compile.html" + }, + "regex-valid": { + "id": "regex-valid", + "name": "Regex.valid", + "sig": "Regex.valid(pattern) -> bool", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "True if the pattern is well-formed (compiles without error).", + "page": "regex-valid.html" + }, + "regex-matches": { + "id": "regex-matches", + "name": "Regex.matches", + "sig": "Regex.matches(text, pattern) -> bool", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "True if the pattern matches anywhere in the text (a search).", + "page": "regex-matches.html" + }, + "regex-test": { + "id": "regex-test", + "name": "Regex.test", + "sig": "Regex.test(text, re) -> bool", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "Like matches, but reuses an already-compiled Regex.", + "page": "regex-test.html" + }, + "regex-find": { + "id": "regex-find", + "name": "Regex.find", + "sig": "Regex.find(text, pattern) -> Match", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "The first match of the pattern in the text (a Match, or null if none).", + "page": "regex-find.html" + }, + "regex-exec": { + "id": "regex-exec", + "name": "Regex.exec", + "sig": "Regex.exec(text, re) -> Match", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "Like find, but with an already-compiled Regex.", + "page": "regex-exec.html" + }, + "regex-next": { + "id": "regex-next", + "name": "Regex.next", + "sig": "Regex.next(text, re, from) -> Match", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "The next match at or after byte offset from — the basis of a find-all loop.", + "page": "regex-next.html" + }, + "regex-replace": { + "id": "regex-replace", + "name": "Regex.replace", + "sig": "Regex.replace(text, pattern, replacement) -> string", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "Replace every match; the replacement expands \\0..\\9 group references.", + "page": "regex-replace.html" + }, + "regex-group": { + "id": "regex-group", + "name": "Regex.group", + "sig": "Regex.group(match, n) -> string", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "The text of capture group n (group 0 is the whole match); empty if unset.", + "page": "regex-group.html" + }, + "regex-group_count": { + "id": "regex-group_count", + "name": "Regex.group_count", + "sig": "Regex.group_count(match) -> int", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "How many capture groups the match's pattern has.", + "page": "regex-group_count.html" + }, + "regex-start": { + "id": "regex-start", + "name": "Regex.start", + "sig": "Regex.start(match, n) -> int", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "The start byte offset of group n, or -1 if it did not participate.", + "page": "regex-start.html" + }, + "regex-end": { + "id": "regex-end", + "name": "Regex.end", + "sig": "Regex.end(match, n) -> int", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "The end byte offset of group n, or -1 if it did not participate.", + "page": "regex-end.html" + }, + "regex-ok": { + "id": "regex-ok", + "name": "Regex.ok", + "sig": "Regex.ok(match) -> bool", + "kind": "namespace-method", + "category": "regex", + "section": "Regex", + "tip": "True if the match succeeded (the Match is non-null).", + "page": "regex-ok.html" + }, + "grid-line": { + "id": "grid-line", + "name": "Grid.line", + "sig": "Grid.line(x0, y0, x1, y1) -> []Cell", + "kind": "namespace-method", + "category": "grid", + "section": "Grid", + "tip": "Every cell a straight line from (x0,y0) to (x1,y1) crosses (Bresenham).", + "page": "grid-line.html" + }, + "grid-blocked": { + "id": "grid-blocked", + "name": "Grid.blocked", + "sig": "Grid.blocked(x, y, wall) -> bool", + "kind": "namespace-method", + "category": "grid", + "section": "Grid", + "tip": "True if the cell is out of bounds or holds the wall tile.", + "page": "grid-blocked.html" + }, + "grid-line_of_sight": { + "id": "grid-line_of_sight", + "name": "Grid.line_of_sight", + "sig": "Grid.line_of_sight(x0, y0, x1, y1, wall) -> bool", + "kind": "namespace-method", + "category": "grid", + "section": "Grid", + "tip": "True if the straight line between two cells crosses no wall.", + "page": "grid-line_of_sight.html" + }, + "grid-flood": { + "id": "grid-flood", + "name": "Grid.flood", + "sig": "Grid.flood(x, y, wall) -> []Cell", + "kind": "namespace-method", + "category": "grid", + "section": "Grid", + "tip": "Every passable cell reachable from (x,y), 4-connected, in BFS order.", + "page": "grid-flood.html" + }, + "grid-a_star": { + "id": "grid-a_star", + "name": "Grid.a_star", + "sig": "Grid.a_star(x0, y0, x1, y1, wall) -> []Cell", + "kind": "namespace-method", + "category": "grid", + "section": "Grid", + "tip": "The shortest 4-connected path between two cells (A*), or an empty list.", + "page": "grid-a_star.html" + }, + "anim-frame": { + "id": "anim-frame", + "name": "Anim.frame", + "sig": "Anim.frame(timer, fps, count) -> int", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "The looping frame index for an elapsed timer at a given fps.", + "page": "anim-frame.html" + }, + "anim-once": { + "id": "anim-once", + "name": "Anim.once", + "sig": "Anim.once(timer, fps, count) -> int", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "A non-looping frame index that clamps on the last frame.", + "page": "anim-once.html" + }, + "anim-pingpong": { + "id": "anim-pingpong", + "name": "Anim.pingpong", + "sig": "Anim.pingpong(timer, fps, count) -> int", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "A frame index that bounces 0..count-1..0 and repeats.", + "page": "anim-pingpong.html" + }, + "anim-finished": { + "id": "anim-finished", + "name": "Anim.finished", + "sig": "Anim.finished(timer, fps, count) -> bool", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "True once a one-shot clip has run past its last frame.", + "page": "anim-finished.html" + }, + "anim-duration": { + "id": "anim-duration", + "name": "Anim.duration", + "sig": "Anim.duration(fps, count) -> fixed", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "Seconds for one full cycle of a clip: count / fps.", + "page": "anim-duration.html" + }, + "anim-cell_x": { + "id": "anim-cell_x", + "name": "Anim.cell_x", + "sig": "Anim.cell_x(frame, cols, cell_w) -> int", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "The source x (pixels) of a frame on a grid spritesheet.", + "page": "anim-cell_x.html" + }, + "anim-cell_y": { + "id": "anim-cell_y", + "name": "Anim.cell_y", + "sig": "Anim.cell_y(frame, cols, cell_h) -> int", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "The source y (pixels) of a frame on a grid spritesheet.", + "page": "anim-cell_y.html" + }, + "anim-play": { + "id": "anim-play", + "name": "Anim.play", + "sig": "Anim.play(entity, clip) | Anim.play(entity, fps, frames, mode)", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "Start (or restart) a spritesheet clip on an entity in one call.", + "page": "anim-play.html" + }, + "anim-clip": { + "id": "anim-clip", + "name": "Anim.clip", + "sig": "Anim.clip(name, frames, fps, mode)", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "Register a named spritesheet clip so Anim.play can play it by name.", + "page": "anim-clip.html" + }, + "anim-on_frame": { + "id": "anim-on_frame", + "name": "Anim.on_frame", + "sig": "Anim.on_frame(entity, frame)", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "Arm a frame event — the engine flags the tick a clip lands on this frame.", + "page": "anim-on_frame.html" + }, + "anim-fired": { + "id": "anim-fired", + "name": "Anim.fired", + "sig": "Anim.fired(entity) -> bool", + "kind": "namespace-method", + "category": "anim", + "section": "Anim", + "tip": "Did the entity's clip land on its armed frame event this tick?", + "page": "anim-fired.html" + }, + "motion-to": { + "id": "motion-to", + "name": "Motion.to", + "sig": "Motion.to(entity, from, to, dur, ease)", + "kind": "namespace-method", + "category": "motion", + "section": "Motion", + "tip": "Start a value tween on an entity's Motion component in one call.", + "page": "motion-to.html" + }, + "tween-progress": { + "id": "tween-progress", + "name": "Tween.progress", + "sig": "Tween.progress(timer, duration) -> fixed", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "A one-shot 0..1 amount, clamped, for timer over duration.", + "page": "tween-progress.html" + }, + "tween-loop": { + "id": "tween-loop", + "name": "Tween.loop", + "sig": "Tween.loop(timer, duration) -> fixed", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "A repeating 0..1 sawtooth amount over duration.", + "page": "tween-loop.html" + }, + "tween-yoyo": { + "id": "tween-yoyo", + "name": "Tween.yoyo", + "sig": "Tween.yoyo(timer, duration) -> fixed", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "A repeating 0..1..0 triangle amount over duration.", + "page": "tween-yoyo.html" + }, + "tween-done": { + "id": "tween-done", + "name": "Tween.done", + "sig": "Tween.done(timer, duration) -> bool", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "True once a one-shot tween's timer reaches its duration.", + "page": "tween-done.html" + }, + "tween-ease": { + "id": "tween-ease", + "name": "Tween.ease", + "sig": "Tween.ease(t, mode) -> fixed", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Shape a 0..1 amount through an easing curve chosen by a literal mode.", + "page": "tween-ease.html" + }, + "tween-number": { + "id": "tween-number", + "name": "Tween.number", + "sig": "Tween.number(from, to, t) -> fixed", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Linear blend of two fixeds by amount t.", + "page": "tween-number.html" + }, + "tween-round": { + "id": "tween-round", + "name": "Tween.round", + "sig": "Tween.round(from, to, t) -> int", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Linear blend of two ints by amount t, rounded to the nearest int.", + "page": "tween-round.html" + }, + "tween-point": { + "id": "tween-point", + "name": "Tween.point", + "sig": "Tween.point(from, to, t) -> Vector", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Component-wise blend of two Vectors by amount t.", + "page": "tween-point.html" + }, + "tween-tint": { + "id": "tween-tint", + "name": "Tween.tint", + "sig": "Tween.tint(from, to, t) -> int", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Per-channel blend of two colors by amount t.", + "page": "tween-tint.html" + }, + "tween-to": { + "id": "tween-to", + "name": "Tween.to", + "sig": "Tween.to(from, to, dur, ease) -> handle", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Start a fluent, engine-advanced tween and return a handle.", + "page": "tween-to.html" + }, + "tween-chain": { + "id": "tween-chain", + "name": "Tween.chain", + "sig": "Tween.chain(handle, to, dur, ease) -> handle", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Append a tween segment that runs after the handle's current queue.", + "page": "tween-chain.html" + }, + "tween-delay": { + "id": "tween-delay", + "name": "Tween.delay", + "sig": "Tween.delay(handle, ticks) -> handle", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Append a pause to a tween handle's sequence.", + "page": "tween-delay.html" + }, + "tween-value": { + "id": "tween-value", + "name": "Tween.value", + "sig": "Tween.value(handle) -> int", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "The current value of a fluent tween handle this frame.", + "page": "tween-value.html" + }, + "tween-stop": { + "id": "tween-stop", + "name": "Tween.stop", + "sig": "Tween.stop(handle)", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Stop and dispose a tween handle, freezing its value.", + "page": "tween-stop.html" + }, + "tween-parallel": { + "id": "tween-parallel", + "name": "Tween.parallel", + "sig": "Tween.parallel(a, b) -> bool", + "kind": "namespace-method", + "category": "tween", + "section": "Tween", + "tip": "Are two tween handles both finished? — a parallel completion query.", + "page": "tween-parallel.html" + }, + "query-count": { + "id": "query-count", + "name": "Query.count", + "sig": "Query.count(prop) -> int", + "kind": "namespace-method", + "category": "query", + "section": "Query", + "tip": "How many live entities carry a property.", + "page": "query-count.html" + }, + "query-first": { + "id": "query-first", + "name": "Query.first", + "sig": "Query.first(prop) -> int", + "kind": "namespace-method", + "category": "query", + "section": "Query", + "tip": "The lowest-id live entity carrying a property, or -1.", + "page": "query-first.html" + }, + "query-nearest": { + "id": "query-nearest", + "name": "Query.nearest", + "sig": "Query.nearest(prop, pos, x_field, y_field, x, y) -> int", + "kind": "namespace-method", + "category": "query", + "section": "Query", + "tip": "The bearer of prop closest to a point, by squared distance.", + "page": "query-nearest.html" + }, + "query-within": { + "id": "query-within", + "name": "Query.within", + "sig": "Query.within(prop, pos, x, y, radius, x_field, y_field) -> []int", + "kind": "namespace-method", + "category": "query", + "section": "Query", + "tip": "Every bearer of prop inside a radius of a point.", + "page": "query-within.html" + }, + "reflect-prop": { + "id": "reflect-prop", + "name": "Reflect.prop", + "sig": "Reflect.prop(name) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The property id for a property name, or -1 if unknown.", + "page": "reflect-prop.html" + }, + "reflect-field": { + "id": "reflect-field", + "name": "Reflect.field", + "sig": "Reflect.field(prop, name) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The field id of a named field within a property, or -1.", + "page": "reflect-field.html" + }, + "reflect-prop_count": { + "id": "reflect-prop_count", + "name": "Reflect.prop_count", + "sig": "Reflect.prop_count() -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "How many properties the world schema defines.", + "page": "reflect-prop_count.html" + }, + "reflect-prop_name": { + "id": "reflect-prop_name", + "name": "Reflect.prop_name", + "sig": "Reflect.prop_name(index) -> string", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The name of the property at an index (or \"\").", + "page": "reflect-prop_name.html" + }, + "reflect-field_count": { + "id": "reflect-field_count", + "name": "Reflect.field_count", + "sig": "Reflect.field_count(prop) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "How many fields a property has.", + "page": "reflect-field_count.html" + }, + "reflect-field_name": { + "id": "reflect-field_name", + "name": "Reflect.field_name", + "sig": "Reflect.field_name(prop, index) -> string", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The name of the field at an index within a property.", + "page": "reflect-field_name.html" + }, + "reflect-field_type": { + "id": "reflect-field_type", + "name": "Reflect.field_type", + "sig": "Reflect.field_type(prop, index) -> string", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The type name of the field at an index within a property.", + "page": "reflect-field_type.html" + }, + "reflect-get": { + "id": "reflect-get", + "name": "Reflect.get", + "sig": "Reflect.get(entity, prop, field) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "Read one field of an entity by (prop, field) id.", + "page": "reflect-get.html" + }, + "reflect-set": { + "id": "reflect-set", + "name": "Reflect.set", + "sig": "Reflect.set(entity, prop, field, value)", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "Write one field of an entity by (prop, field) id.", + "page": "reflect-set.html" + }, + "reflect-has": { + "id": "reflect-has", + "name": "Reflect.has", + "sig": "Reflect.has(entity, prop) -> bool", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "Does an entity carry a property?", + "page": "reflect-has.html" + }, + "reflect-kind": { + "id": "reflect-kind", + "name": "Reflect.kind", + "sig": "Reflect.kind(entity) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The model id of an entity.", + "page": "reflect-kind.html" + }, + "reflect-model": { + "id": "reflect-model", + "name": "Reflect.model", + "sig": "Reflect.model(name) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "The model id for a model name, or -1 if unknown.", + "page": "reflect-model.html" + }, + "reflect-serialize": { + "id": "reflect-serialize", + "name": "Reflect.serialize", + "sig": "Reflect.serialize(entity) -> value", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "Walk an entity's components into a value tree.", + "page": "reflect-serialize.html" + }, + "reflect-apply": { + "id": "reflect-apply", + "name": "Reflect.apply", + "sig": "Reflect.apply(entity, value) -> int", + "kind": "namespace-method", + "category": "reflect", + "section": "Reflect", + "tip": "Write a value tree's fields back into an entity.", + "page": "reflect-apply.html" + }, + "camera-set": { + "id": "camera-set", + "name": "Camera.set", + "sig": "Camera.set(x, y)", + "kind": "namespace-method", + "category": "camera", + "section": "Camera", + "tip": "Place the camera at a world-space offset.", + "page": "camera-set.html" + }, + "camera-follow": { + "id": "camera-follow", + "name": "Camera.follow", + "sig": "Camera.follow(x, y, lerp)", + "kind": "namespace-method", + "category": "camera", + "section": "Camera", + "tip": "Ease the camera toward centring a target point.", + "page": "camera-follow.html" + }, + "camera-shake": { + "id": "camera-shake", + "name": "Camera.shake", + "sig": "Camera.shake(amount)", + "kind": "namespace-method", + "category": "camera", + "section": "Camera", + "tip": "Jitter the camera by up to +/- amount pixels (seeded RNG).", + "page": "camera-shake.html" + }, + "camera-zoom": { + "id": "camera-zoom", + "name": "Camera.zoom", + "sig": "Camera.zoom(scale)", + "kind": "namespace-method", + "category": "camera", + "section": "Camera", + "tip": "Scale the whole view about the screen centre by a Q16.16 factor (1.0 = none).", + "page": "camera-zoom.html" + }, + "camera-shake_for": { + "id": "camera-shake_for", + "name": "Camera.shake_for", + "sig": "Camera.shake_for(amount: px, frames: n)", + "kind": "namespace-method", + "category": "camera", + "section": "Camera", + "tip": "Shake for a number of frames, then stop, with no bookkeeping.", + "page": "camera-shake_for.html" + }, + "light-ambient": { + "id": "light-ambient", + "name": "Light.ambient", + "sig": "Light.ambient(color)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Multiply the whole scene by a tint — the night/cave modulate.", + "page": "light-ambient.html" + }, + "light-point": { + "id": "light-point", + "name": "Light.point", + "sig": "Light.point(x, y, radius, color, energy)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Add a radial glow that falls off with distance.", + "page": "light-point.html" + }, + "light-occlude": { + "id": "light-occlude", + "name": "Light.occlude", + "sig": "Light.occlude(x, y, width, height)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Register a rectangle that blocks light — a hard shadow caster.", + "page": "light-occlude.html" + }, + "light-clear_occluders": { + "id": "light-clear_occluders", + "name": "Light.clear_occluders", + "sig": "Light.clear_occluders()", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Forget every occluder — call once per frame before re-registering.", + "page": "light-clear_occluders.html" + }, + "light-spot": { + "id": "light-spot", + "name": "Light.spot", + "sig": "Light.spot(x, y, radius, color, energy, direction, spread)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "A cone / flashlight light aimed at a direction with a half-angle spread.", + "page": "light-spot.html" + }, + "light-falloff": { + "id": "light-falloff", + "name": "Light.falloff", + "sig": "Light.falloff(exponent)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Set the brightness-ramp exponent for later lights (1 linear, 2 quadratic…).", + "page": "light-falloff.html" + }, + "light-soft": { + "id": "light-soft", + "name": "Light.soft", + "sig": "Light.soft(radius)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Soft shadows — an occluder edge fades through a penumbra of this radius.", + "page": "light-soft.html" + }, + "light-gel": { + "id": "light-gel", + "name": "Light.gel", + "sig": "Light.gel(color)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "A colour cookie — later lights gel from their centre colour to this rim colour.", + "page": "light-gel.html" + }, + "light-clear_gel": { + "id": "light-clear_gel", + "name": "Light.clear_gel", + "sig": "Light.clear_gel()", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Clear the gel — later lights are a flat single colour again.", + "page": "light-clear_gel.html" + }, + "light-normal": { + "id": "light-normal", + "name": "Light.normal", + "sig": "Light.normal(x, y, width, height, nx, ny)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Stamp a surface normal over a rectangle so lights shade it by facing (N·L).", + "page": "light-normal.html" + }, + "light-clear_normals": { + "id": "light-clear_normals", + "name": "Light.clear_normals", + "sig": "Light.clear_normals()", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Forget every stamped normal — call once per frame before re-stamping.", + "page": "light-clear_normals.html" + }, + "light-height": { + "id": "light-height", + "name": "Light.height", + "sig": "Light.height(height)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Set the virtual height of lights above the surface, for normal-map shading.", + "page": "light-height.html" + }, + "light-time_of_day": { + "id": "light-time_of_day", + "name": "Light.time_of_day", + "sig": "Light.time_of_day(t)", + "kind": "namespace-method", + "category": "light", + "section": "Light", + "tip": "Set the ambient tint from a 0..1 time-of-day — a day/night cycle in one value.", + "page": "light-time_of_day.html" + }, + "sprite-sheet": { + "id": "sprite-sheet", + "name": "Sprite.sheet", + "sig": "Sprite.sheet(path, cellw, cellh) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "Load a spritesheet and remember its cell grid; returns a sheet handle.", + "page": "sprite-sheet.html" + }, + "sprite-cell": { + "id": "sprite-cell", + "name": "Sprite.cell", + "sig": "Sprite.cell(sheet, col, row) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "One cell of a sheet, by grid coords; returns a sprite id.", + "page": "sprite-cell.html" + }, + "sprite-cell_span": { + "id": "sprite-cell_span", + "name": "Sprite.cell_span", + "sig": "Sprite.cell_span(sheet, col, row, cols, rows) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "A sprite spanning cols x rows cells (tall/wide art).", + "page": "sprite-cell_span.html" + }, + "sprite-define": { + "id": "sprite-define", + "name": "Sprite.define", + "sig": "Sprite.define(name, sheet, col, row) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "Name a cell for later lookup; returns its sprite id.", + "page": "sprite-define.html" + }, + "sprite-named": { + "id": "sprite-named", + "name": "Sprite.named", + "sig": "Sprite.named(name) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "Look up a named sprite's id, or -1.", + "page": "sprite-named.html" + }, + "sprite-draw": { + "id": "sprite-draw", + "name": "Sprite.draw", + "sig": "Sprite.draw(id, x, y)", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "Blit an atlas sprite at (x, y) through the camera/zoom/clip.", + "page": "sprite-draw.html" + }, + "sprite-draw_scaled": { + "id": "sprite-draw_scaled", + "name": "Sprite.draw_scaled", + "sig": "Sprite.draw_scaled(id, x, y, scale)", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "Blit an atlas sprite scaled by an integer factor.", + "page": "sprite-draw_scaled.html" + }, + "sprite-width": { + "id": "sprite-width", + "name": "Sprite.width", + "sig": "Sprite.width(id) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "The atlas sprite's width in pixels.", + "page": "sprite-width.html" + }, + "sprite-height": { + "id": "sprite-height", + "name": "Sprite.height", + "sig": "Sprite.height(id) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "The atlas sprite's height in pixels.", + "page": "sprite-height.html" + }, + "sprite-strip": { + "id": "sprite-strip", + "name": "Sprite.strip", + "sig": "Sprite.strip(sheet, col, row, count, rows) -> int", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "A run of animation frames as consecutive sprite ids.", + "page": "sprite-strip.html" + }, + "sprite-draw_meter": { + "id": "sprite-draw_meter", + "name": "Sprite.draw_meter", + "sig": "Sprite.draw_meter(x:, y:, value:, max:, per_icon:, spacing:, full:, half:, empty:)", + "kind": "namespace-method", + "category": "sprite", + "section": "Sprite", + "tip": "A value as a row of full / half / empty icons.", + "page": "sprite-draw_meter.html" + }, + "assets-image": { + "id": "assets-image", + "name": "Assets.image", + "sig": "Assets.image(path) -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Load a whole image file as one sprite; returns its id.", + "page": "assets-image.html" + }, + "assets-load": { + "id": "assets-load", + "name": "Assets.load", + "sig": "Assets.load(path) -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Alias of Assets.image.", + "page": "assets-load.html" + }, + "assets-get": { + "id": "assets-get", + "name": "Assets.get", + "sig": "Assets.get(name) -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Look up a named sprite's id (alias of Sprite.named).", + "page": "assets-get.html" + }, + "assets-enqueue": { + "id": "assets-enqueue", + "name": "Assets.enqueue", + "sig": "Assets.enqueue(name, path)", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Queue a named image file to preload later.", + "page": "assets-enqueue.html" + }, + "assets-pump": { + "id": "assets-pump", + "name": "Assets.pump", + "sig": "Assets.pump(max) -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Load up to max queued assets this frame; returns how many it loaded.", + "page": "assets-pump.html" + }, + "assets-total": { + "id": "assets-total", + "name": "Assets.total", + "sig": "Assets.total() -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "How many assets are enqueued.", + "page": "assets-total.html" + }, + "assets-loaded": { + "id": "assets-loaded", + "name": "Assets.loaded", + "sig": "Assets.loaded() -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "How many enqueued assets have loaded so far.", + "page": "assets-loaded.html" + }, + "assets-ready": { + "id": "assets-ready", + "name": "Assets.ready", + "sig": "Assets.ready() -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "1 once every enqueued asset has loaded.", + "page": "assets-ready.html" + }, + "assets-font": { + "id": "assets-font", + "name": "Assets.font", + "sig": "Assets.font(name: s) -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "A font loaded through the preload queue, by name.", + "page": "assets-font.html" + }, + "assets-progress": { + "id": "assets-progress", + "name": "Assets.progress", + "sig": "Assets.progress() -> int", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Loading progress as a 0..100 percent.", + "page": "assets-progress.html" + }, + "assets-enqueue_dir": { + "id": "assets-enqueue_dir", + "name": "Assets.enqueue_dir", + "sig": "Assets.enqueue_dir(path: dir)", + "kind": "namespace-method", + "category": "assets", + "section": "Assets", + "tip": "Enqueue every file of a directory, named by its file name.", + "page": "assets-enqueue_dir.html" + }, + "kw-test": { + "id": "kw-test", + "name": "test", + "sig": "test \"name\" { … }", + "kind": "keyword", + "category": "testing", + "section": "Testing", + "tip": "A named test block, run automatically with pass/fail reporting.", + "page": "kw-test.html" + }, + "pool-live": { + "id": "pool-live", + "name": "Pool.live", + "sig": "Pool.live() -> int", + "kind": "namespace-method", + "category": "pool", + "section": "Pool", + "tip": "How many entities are currently alive.", + "page": "pool-live.html" + }, + "pool-free": { + "id": "pool-free", + "name": "Pool.free", + "sig": "Pool.free() -> int", + "kind": "namespace-method", + "category": "pool", + "section": "Pool", + "tip": "How many freed slots are waiting to be reused.", + "page": "pool-free.html" + }, + "pool-reserved": { + "id": "pool-reserved", + "name": "Pool.reserved", + "sig": "Pool.reserved() -> int", + "kind": "namespace-method", + "category": "pool", + "section": "Pool", + "tip": "High-water: how many slots have ever been allocated.", + "page": "pool-reserved.html" + }, + "pool-capacity": { + "id": "pool-capacity", + "name": "Pool.capacity", + "sig": "Pool.capacity() -> int", + "kind": "namespace-method", + "category": "pool", + "section": "Pool", + "tip": "The maximum number of entities.", + "page": "pool-capacity.html" + }, + "value-null": { + "id": "value-null", + "name": "Value.null", + "sig": "Value.null() -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "An empty null node.", + "page": "value-null.html" + }, + "value-int": { + "id": "value-int", + "name": "Value.int", + "sig": "Value.int(n: int) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Wrap an int in a value node.", + "page": "value-int.html" + }, + "value-fixed": { + "id": "value-fixed", + "name": "Value.fixed", + "sig": "Value.fixed(f: fixed) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Wrap a fixed in a value node.", + "page": "value-fixed.html" + }, + "value-bool": { + "id": "value-bool", + "name": "Value.bool", + "sig": "Value.bool(b: bool) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Wrap a bool in a value node.", + "page": "value-bool.html" + }, + "value-str": { + "id": "value-str", + "name": "Value.str", + "sig": "Value.str(s: string) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Wrap a string in a value node.", + "page": "value-str.html" + }, + "value-list": { + "id": "value-list", + "name": "Value.list", + "sig": "Value.list() -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "An empty list node.", + "page": "value-list.html" + }, + "value-object": { + "id": "value-object", + "name": "Value.object", + "sig": "Value.object() -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "An empty object node.", + "page": "value-object.html" + }, + "value-add": { + "id": "value-add", + "name": "Value.add", + "sig": "Value.add(list, item) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Append an item to a list; returns the list.", + "page": "value-add.html" + }, + "value-put": { + "id": "value-put", + "name": "Value.put", + "sig": "Value.put(obj, key: string, item) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Set a key on an object; returns the object.", + "page": "value-put.html" + }, + "value-get": { + "id": "value-get", + "name": "Value.get", + "sig": "Value.get(obj, key: string) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Read a member of an object by key.", + "page": "value-get.html" + }, + "value-has": { + "id": "value-has", + "name": "Value.has", + "sig": "Value.has(obj, key: string) -> bool", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Whether an object has a member.", + "page": "value-has.html" + }, + "value-at": { + "id": "value-at", + "name": "Value.at", + "sig": "Value.at(list, index: int) -> value", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Read a list item by index.", + "page": "value-at.html" + }, + "value-key_at": { + "id": "value-key_at", + "name": "Value.key_at", + "sig": "Value.key_at(obj, index: int) -> string", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "The key of an object member by position.", + "page": "value-key_at.html" + }, + "value-count": { + "id": "value-count", + "name": "Value.count", + "sig": "Value.count(value) -> int", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Number of items/members in a list or object.", + "page": "value-count.html" + }, + "value-kind": { + "id": "value-kind", + "name": "Value.kind", + "sig": "Value.kind(value) -> int", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "The node's kind tag.", + "page": "value-kind.html" + }, + "value-as_int": { + "id": "value-as_int", + "name": "Value.as_int", + "sig": "Value.as_int(value) -> int", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Read a scalar node as an int.", + "page": "value-as_int.html" + }, + "value-as_str": { + "id": "value-as_str", + "name": "Value.as_str", + "sig": "Value.as_str(value) -> string", + "kind": "namespace-method", + "category": "value", + "section": "Value", + "tip": "Read a str node's text.", + "page": "value-as_str.html" + }, + "json-encode": { + "id": "json-encode", + "name": "Json.encode", + "sig": "Json.encode(value) -> string", + "kind": "namespace-method", + "category": "json", + "section": "Json", + "tip": "Serialize a value tree to compact JSON text.", + "page": "json-encode.html" + }, + "json-parse": { + "id": "json-parse", + "name": "Json.parse", + "sig": "Json.parse(text: string) -> value", + "kind": "namespace-method", + "category": "json", + "section": "Json", + "tip": "Parse JSON text into a value tree.", + "page": "json-parse.html" + }, + "job-defer": { + "id": "job-defer", + "name": "Job.defer", + "sig": "Job.defer() -> Job", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "A future you resolve yourself later.", + "page": "job-defer.html" + }, + "job-run": { + "id": "job-run", + "name": "Job.run", + "sig": "Job.run(kind, arg) -> Job", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Start a background compute job.", + "page": "job-run.html" + }, + "job-fulfill": { + "id": "job-fulfill", + "name": "Job.fulfill", + "sig": "Job.fulfill(handle, value) -> void", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Resolve a pending job with a value.", + "page": "job-fulfill.html" + }, + "job-fail": { + "id": "job-fail", + "name": "Job.fail", + "sig": "Job.fail(handle, error) -> void", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Resolve a pending job as failed.", + "page": "job-fail.html" + }, + "job-cancel": { + "id": "job-cancel", + "name": "Job.cancel", + "sig": "Job.cancel(handle) -> void", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Cancel a job before it finishes.", + "page": "job-cancel.html" + }, + "job-pump": { + "id": "job-pump", + "name": "Job.pump", + "sig": "Job.pump(budget) -> int", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Advance background jobs; collect results.", + "page": "job-pump.html" + }, + "job-done": { + "id": "job-done", + "name": "Job.done", + "sig": "Job.done(handle) -> bool", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Has the job resolved (any outcome)?", + "page": "job-done.html" + }, + "job-ok": { + "id": "job-ok", + "name": "Job.ok", + "sig": "Job.ok(handle) -> bool", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Did the job succeed?", + "page": "job-ok.html" + }, + "job-failed": { + "id": "job-failed", + "name": "Job.failed", + "sig": "Job.failed(handle) -> bool", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Did the job fail?", + "page": "job-failed.html" + }, + "job-cancelled": { + "id": "job-cancelled", + "name": "Job.cancelled", + "sig": "Job.cancelled(handle) -> bool", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Was the job cancelled?", + "page": "job-cancelled.html" + }, + "job-result": { + "id": "job-result", + "name": "Job.result", + "sig": "Job.result(handle) -> int", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "The success value of a done job.", + "page": "job-result.html" + }, + "job-error": { + "id": "job-error", + "name": "Job.error", + "sig": "Job.error(handle) -> int", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "The error code of a failed job.", + "page": "job-error.html" + }, + "job-pending": { + "id": "job-pending", + "name": "Job.pending", + "sig": "Job.pending() -> int", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "How many jobs are still unresolved.", + "page": "job-pending.html" + }, + "job-free": { + "id": "job-free", + "name": "Job.free", + "sig": "Job.free(handle) -> void", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Release a job slot back to the pool.", + "page": "job-free.html" + }, + "job-parallel_for": { + "id": "job-parallel_for", + "name": "Job.parallel_for", + "sig": "Job.parallel_for(count, work, ctx) -> void", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "Run work(i, ctx) for every i in [0, count) across all cores; returns when every call is done.", + "page": "job-parallel_for.html" + }, + "job-is_worker": { + "id": "job-is_worker", + "name": "Job.is_worker", + "sig": "Job.is_worker() -> bool", + "kind": "namespace-method", + "category": "job", + "section": "Job", + "tip": "True on a Job.parallel_for pool thread, false on the main thread.", + "page": "job-is_worker.html" + }, + "promise-all": { + "id": "promise-all", + "name": "Promise.all", + "sig": "Promise.all(handles) -> Job", + "kind": "namespace-method", + "category": "promise", + "section": "Promise", + "tip": "Succeeds when every member succeeds.", + "page": "promise-all.html" + }, + "promise-race": { + "id": "promise-race", + "name": "Promise.race", + "sig": "Promise.race(handles) -> Job", + "kind": "namespace-method", + "category": "promise", + "section": "Promise", + "tip": "Succeeds when the first member does.", + "page": "promise-race.html" + }, + "promise-count_done": { + "id": "promise-count_done", + "name": "Promise.count_done", + "sig": "Promise.count_done(handles) -> int", + "kind": "namespace-method", + "category": "promise", + "section": "Promise", + "tip": "How many members have resolved.", + "page": "promise-count_done.html" + }, + "promise-all_done": { + "id": "promise-all_done", + "name": "Promise.all_done", + "sig": "Promise.all_done(handles) -> bool", + "kind": "namespace-method", + "category": "promise", + "section": "Promise", + "tip": "Have all members resolved?", + "page": "promise-all_done.html" + }, + "sync-mutex": { + "id": "sync-mutex", + "name": "Sync.mutex", + "sig": "Sync.mutex() -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Create a cooperative lock.", + "page": "sync-mutex.html" + }, + "sync-lock": { + "id": "sync-lock", + "name": "Sync.lock", + "sig": "Sync.lock(mutex) -> void", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Take the lock.", + "page": "sync-lock.html" + }, + "sync-unlock": { + "id": "sync-unlock", + "name": "Sync.unlock", + "sig": "Sync.unlock(mutex) -> void", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Release the lock.", + "page": "sync-unlock.html" + }, + "sync-try_lock": { + "id": "sync-try_lock", + "name": "Sync.try_lock", + "sig": "Sync.try_lock(mutex) -> bool", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Take the lock only if it is free.", + "page": "sync-try_lock.html" + }, + "sync-atomic": { + "id": "sync-atomic", + "name": "Sync.atomic", + "sig": "Sync.atomic() -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Create an atomic counter (starts at 0).", + "page": "sync-atomic.html" + }, + "sync-get": { + "id": "sync-get", + "name": "Sync.get", + "sig": "Sync.get(atomic) -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Read the counter.", + "page": "sync-get.html" + }, + "sync-set": { + "id": "sync-set", + "name": "Sync.set", + "sig": "Sync.set(atomic, value) -> void", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Store a value in the counter.", + "page": "sync-set.html" + }, + "sync-add": { + "id": "sync-add", + "name": "Sync.add", + "sig": "Sync.add(atomic, delta) -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Add to the counter; return the new value.", + "page": "sync-add.html" + }, + "sync-cas": { + "id": "sync-cas", + "name": "Sync.cas", + "sig": "Sync.cas(atomic, expect, next) -> bool", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Compare-and-set the counter.", + "page": "sync-cas.html" + }, + "sync-channel": { + "id": "sync-channel", + "name": "Sync.channel", + "sig": "Sync.channel() -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Create a bounded int FIFO channel.", + "page": "sync-channel.html" + }, + "sync-send": { + "id": "sync-send", + "name": "Sync.send", + "sig": "Sync.send(channel, value) -> bool", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Enqueue a value (false if full).", + "page": "sync-send.html" + }, + "sync-recv": { + "id": "sync-recv", + "name": "Sync.recv", + "sig": "Sync.recv(channel) -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Dequeue the oldest value.", + "page": "sync-recv.html" + }, + "sync-can_recv": { + "id": "sync-can_recv", + "name": "Sync.can_recv", + "sig": "Sync.can_recv(channel) -> bool", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "Is there a value waiting?", + "page": "sync-can_recv.html" + }, + "sync-len": { + "id": "sync-len", + "name": "Sync.len", + "sig": "Sync.len(channel) -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "How many values are queued.", + "page": "sync-len.html" + }, + "sync-cpu_count": { + "id": "sync-cpu_count", + "name": "Sync.cpu_count", + "sig": "Sync.cpu_count() -> int", + "kind": "namespace-method", + "category": "sync", + "section": "Sync", + "tip": "The machine's logical cores (1 to 64); Job.parallel_for uses one thread per core.", + "page": "sync-cpu_count.html" + }, + "xml-parse": { + "id": "xml-parse", + "name": "Xml.parse", + "sig": "Xml.parse(text: string) -> xml", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "Parse XML text into an element tree.", + "page": "xml-parse.html" + }, + "xml-tag": { + "id": "xml-tag", + "name": "Xml.tag", + "sig": "Xml.tag(node) -> string", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "The element's tag name.", + "page": "xml-tag.html" + }, + "xml-text": { + "id": "xml-text", + "name": "Xml.text", + "sig": "Xml.text(node) -> string", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "The element's character data.", + "page": "xml-text.html" + }, + "xml-attr": { + "id": "xml-attr", + "name": "Xml.attr", + "sig": "Xml.attr(node, key: string) -> string", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "An attribute's value by name.", + "page": "xml-attr.html" + }, + "xml-attr_int": { + "id": "xml-attr_int", + "name": "Xml.attr_int", + "sig": "Xml.attr_int(node, key: string, dflt: int) -> int", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "An attribute parsed as an integer.", + "page": "xml-attr_int.html" + }, + "xml-has": { + "id": "xml-has", + "name": "Xml.has", + "sig": "Xml.has(node, key: string) -> int", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "Whether an attribute is present.", + "page": "xml-has.html" + }, + "xml-attr_count": { + "id": "xml-attr_count", + "name": "Xml.attr_count", + "sig": "Xml.attr_count(node) -> int", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "How many attributes the element has.", + "page": "xml-attr_count.html" + }, + "xml-child_count": { + "id": "xml-child_count", + "name": "Xml.child_count", + "sig": "Xml.child_count(node) -> int", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "How many child elements.", + "page": "xml-child_count.html" + }, + "xml-child": { + "id": "xml-child", + "name": "Xml.child", + "sig": "Xml.child(node, index: int) -> xml", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "A child element by index.", + "page": "xml-child.html" + }, + "xml-find": { + "id": "xml-find", + "name": "Xml.find", + "sig": "Xml.find(node, tag: string) -> xml", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "The first child with a given tag.", + "page": "xml-find.html" + }, + "xml-count": { + "id": "xml-count", + "name": "Xml.count", + "sig": "Xml.count(node, tag: string) -> int", + "kind": "namespace-method", + "category": "xml", + "section": "Xml", + "tip": "How many children have a given tag.", + "page": "xml-count.html" + }, + "base64-decode": { + "id": "base64-decode", + "name": "Base64.decode", + "sig": "Base64.decode(src: string) -> string", + "kind": "namespace-method", + "category": "base64", + "section": "Base64", + "tip": "Decode standard base64 text to bytes.", + "page": "base64-decode.html" + }, + "base64-encode": { + "id": "base64-encode", + "name": "Base64.encode", + "sig": "Base64.encode(src: string) -> string", + "kind": "namespace-method", + "category": "base64", + "section": "Base64", + "tip": "Encode bytes as standard base64 text.", + "page": "base64-encode.html" + }, + "tiled-read": { + "id": "tiled-read", + "name": "Tiled.read", + "sig": "Tiled.read(path: string) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Read a map file into the intermediate value tree.", + "page": "tiled-read.html" + }, + "tiled-read_tsx": { + "id": "tiled-read_tsx", + "name": "Tiled.read_tsx", + "sig": "Tiled.read_tsx(path: string) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Read a tileset file into a value object.", + "page": "tiled-read_tsx.html" + }, + "tiled-load": { + "id": "tiled-load", + "name": "Tiled.load", + "sig": "Tiled.load(path: string) -> map", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Load a map file into the runtime map model.", + "page": "tiled-load.html" + }, + "tiled-gid": { + "id": "tiled-gid", + "name": "Tiled.gid", + "sig": "Tiled.gid(map, layer: int, x: int, y: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The raw GID at a cell in a tile layer.", + "page": "tiled-gid.html" + }, + "tiled-resolve": { + "id": "tiled-resolve", + "name": "Tiled.resolve", + "sig": "Tiled.resolve(map, gid: int) -> gidinfo", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Decode a GID into tileset, local id and flip flags.", + "page": "tiled-resolve.html" + }, + "tiled-width": { + "id": "tiled-width", + "name": "Tiled.width", + "sig": "Tiled.width(map) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The map width in tiles.", + "page": "tiled-width.html" + }, + "tiled-height": { + "id": "tiled-height", + "name": "Tiled.height", + "sig": "Tiled.height(map) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The map height in tiles.", + "page": "tiled-height.html" + }, + "tiled-layer_count": { + "id": "tiled-layer_count", + "name": "Tiled.layer_count", + "sig": "Tiled.layer_count(map) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "How many layers the map has.", + "page": "tiled-layer_count.html" + }, + "tiled-layer_name": { + "id": "tiled-layer_name", + "name": "Tiled.layer_name", + "sig": "Tiled.layer_name(map, index: int) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's name by index.", + "page": "tiled-layer_name.html" + }, + "tiled-draw": { + "id": "tiled-draw", + "name": "Tiled.draw", + "sig": "Tiled.draw(map, camx: int, camy: int) -> void", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Draw every visible tile layer.", + "page": "tiled-draw.html" + }, + "tiled-project": { + "id": "tiled-project", + "name": "Tiled.project", + "sig": "Tiled.project(map, layer: int) -> void", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Project a collision layer to the byte tilemap.", + "page": "tiled-project.html" + }, + "tiled-tree": { + "id": "tiled-tree", + "name": "Tiled.tree", + "sig": "Tiled.tree(map) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The underlying intermediate value tree.", + "page": "tiled-tree.html" + }, + "tiled-tile_prop": { + "id": "tiled-tile_prop", + "name": "Tiled.tile_prop", + "sig": "Tiled.tile_prop(map, gid: int, name: string) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Whether a GID's tile carries a bool property.", + "page": "tiled-tile_prop.html" + }, + "tiled-collide": { + "id": "tiled-collide", + "name": "Tiled.collide", + "sig": "Tiled.collide(map, layer: int) -> void", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Drive collision from per-tile metadata.", + "page": "tiled-collide.html" + }, + "tiled-collision_kind": { + "id": "tiled-collision_kind", + "name": "Tiled.collision_kind", + "sig": "Tiled.collision_kind(map, gid: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Classify a GID's collision from its tile metadata.", + "page": "tiled-collision_kind.html" + }, + "tiled-tile_shapes": { + "id": "tiled-tile_shapes", + "name": "Tiled.tile_shapes", + "sig": "Tiled.tile_shapes(map, gid: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Whether a GID's tile has a collision shape.", + "page": "tiled-tile_shapes.html" + }, + "tiled-draw_anim": { + "id": "tiled-draw_anim", + "name": "Tiled.draw_anim", + "sig": "Tiled.draw_anim(map, camx: int, camy: int, frame: int) -> void", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Draw with animated tiles advanced to a frame.", + "page": "tiled-draw_anim.html" + }, + "tiled-frame_gid": { + "id": "tiled-frame_gid", + "name": "Tiled.frame_gid", + "sig": "Tiled.frame_gid(map, gid: int, frame: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The current GID of an animated tile at a frame.", + "page": "tiled-frame_gid.html" + }, + "tiled-animated": { + "id": "tiled-animated", + "name": "Tiled.animated", + "sig": "Tiled.animated(map, gid: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Whether a GID's tile is animated.", + "page": "tiled-animated.html" + }, + "tiled-object_count": { + "id": "tiled-object_count", + "name": "Tiled.object_count", + "sig": "Tiled.object_count(map, layer: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "How many objects an object layer holds.", + "page": "tiled-object_count.html" + }, + "tiled-object": { + "id": "tiled-object", + "name": "Tiled.object", + "sig": "Tiled.object(map, layer: int, index: int) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "An object on an object layer by index.", + "page": "tiled-object.html" + }, + "tiled-object_shape": { + "id": "tiled-object_shape", + "name": "Tiled.object_shape", + "sig": "Tiled.object_shape(object) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Classify an object's shape.", + "page": "tiled-object_shape.html" + }, + "tiled-prop": { + "id": "tiled-prop", + "name": "Tiled.prop", + "sig": "Tiled.prop(container, name: string) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A custom property's value, with class-default fallback.", + "page": "tiled-prop.html" + }, + "tiled-prop_int": { + "id": "tiled-prop_int", + "name": "Tiled.prop_int", + "sig": "Tiled.prop_int(container, name: string) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A custom property parsed as an integer.", + "page": "tiled-prop_int.html" + }, + "tiled-prop_type": { + "id": "tiled-prop_type", + "name": "Tiled.prop_type", + "sig": "Tiled.prop_type(container, name: string) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A custom property's declared type.", + "page": "tiled-prop_type.html" + }, + "tiled-load_types": { + "id": "tiled-load_types", + "name": "Tiled.load_types", + "sig": "Tiled.load_types(path: string) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Load a project custom-type table.", + "page": "tiled-load_types.html" + }, + "tiled-template": { + "id": "tiled-template", + "name": "Tiled.template", + "sig": "Tiled.template(path: string) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Read a template file into an object value.", + "page": "tiled-template.html" + }, + "tiled-spawn": { + "id": "tiled-spawn", + "name": "Tiled.spawn", + "sig": "Tiled.spawn(map, object, model: string) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Spawn a Ludic entity from an object.", + "page": "tiled-spawn.html" + }, + "tiled-spawn_layer": { + "id": "tiled-spawn_layer", + "name": "Tiled.spawn_layer", + "sig": "Tiled.spawn_layer(map, layer: int, model: string) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Spawn every object on a layer.", + "page": "tiled-spawn_layer.html" + }, + "tiled-cell_x": { + "id": "tiled-cell_x", + "name": "Tiled.cell_x", + "sig": "Tiled.cell_x(map, x: int, y: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The screen x of a tile cell for the map orientation.", + "page": "tiled-cell_x.html" + }, + "tiled-cell_y": { + "id": "tiled-cell_y", + "name": "Tiled.cell_y", + "sig": "Tiled.cell_y(map, x: int, y: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "The screen y of a tile cell for the map orientation.", + "page": "tiled-cell_y.html" + }, + "tiled-layer_kind": { + "id": "tiled-layer_kind", + "name": "Tiled.layer_kind", + "sig": "Tiled.layer_kind(map, layer: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's kind.", + "page": "tiled-layer_kind.html" + }, + "tiled-layer_opacity": { + "id": "tiled-layer_opacity", + "name": "Tiled.layer_opacity", + "sig": "Tiled.layer_opacity(map, layer: int) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's opacity.", + "page": "tiled-layer_opacity.html" + }, + "tiled-layer_tint": { + "id": "tiled-layer_tint", + "name": "Tiled.layer_tint", + "sig": "Tiled.layer_tint(map, layer: int) -> string", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's tint colour.", + "page": "tiled-layer_tint.html" + }, + "tiled-layer_offsetx": { + "id": "tiled-layer_offsetx", + "name": "Tiled.layer_offsetx", + "sig": "Tiled.layer_offsetx(map, layer: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's horizontal offset.", + "page": "tiled-layer_offsetx.html" + }, + "tiled-layer_offsety": { + "id": "tiled-layer_offsety", + "name": "Tiled.layer_offsety", + "sig": "Tiled.layer_offsety(map, layer: int) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A layer's vertical offset.", + "page": "tiled-layer_offsety.html" + }, + "tiled-world": { + "id": "tiled-world", + "name": "Tiled.world", + "sig": "Tiled.world(path: string) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "Read a .world file.", + "page": "tiled-world.html" + }, + "tiled-world_count": { + "id": "tiled-world_count", + "name": "Tiled.world_count", + "sig": "Tiled.world_count(world) -> int", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "How many maps a world stitches.", + "page": "tiled-world_count.html" + }, + "tiled-world_map": { + "id": "tiled-world_map", + "name": "Tiled.world_map", + "sig": "Tiled.world_map(world, index: int) -> value", + "kind": "namespace-method", + "category": "tiled", + "section": "Tiled", + "tip": "A world member map by index.", + "page": "tiled-world_map.html" + }, + "ui-build": { + "id": "ui-build", + "name": "Ui.build", + "sig": "Ui.build()", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Construct every declared ui block (loads skins, measures fonts).", + "page": "ui-build.html" + }, + "ui-open": { + "id": "ui-open", + "name": "Ui.open", + "sig": "Ui.open(id: UI_Name)", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Make one menu active and focus its first button.", + "page": "ui-open.html" + }, + "ui-close": { + "id": "ui-close", + "name": "Ui.close", + "sig": "Ui.close()", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Deactivate the menu: no menu is open.", + "page": "ui-close.html" + }, + "ui-tick": { + "id": "ui-tick", + "name": "Ui.tick", + "sig": "Ui.tick(key: k)", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Advance navigation by one key (the frame loop does this for you).", + "page": "ui-tick.html" + }, + "ui-clicked": { + "id": "ui-clicked", + "name": "Ui.clicked", + "sig": "Ui.clicked(id: UI_Name) -> bool", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Was this control activated this frame? (polled form of UiClicked)", + "page": "ui-clicked.html" + }, + "ui-set_text": { + "id": "ui-set_text", + "name": "Ui.set_text", + "sig": "Ui.set_text(id: UI_Name, text: s)", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Replace a label's or button's text.", + "page": "ui-set_text.html" + }, + "ui-render": { + "id": "ui-render", + "name": "Ui.render", + "sig": "Ui.render()", + "kind": "namespace-method", + "category": "ui", + "section": "Ui", + "tip": "Draw the active menu.", + "page": "ui-render.html" + }, + "file-open": { + "id": "file-open", + "name": "File.open", + "sig": "File.open(path: p, mode: m) -> pointer", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "Open a file (fopen); null when it cannot be opened.", + "page": "file-open.html" + }, + "file-read": { + "id": "file-read", + "name": "File.read", + "sig": "File.read(file: f, buffer: b, count: n) -> int", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "Read up to n bytes into a buffer; returns the bytes read.", + "page": "file-read.html" + }, + "file-write": { + "id": "file-write", + "name": "File.write", + "sig": "File.write(file: f, buffer: b, count: n) -> int", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "Write n bytes from a buffer; returns the bytes written.", + "page": "file-write.html" + }, + "file-seek": { + "id": "file-seek", + "name": "File.seek", + "sig": "File.seek(file: f, offset: o, whence: w)", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "Move the file cursor (0 start, 1 current, 2 end).", + "page": "file-seek.html" + }, + "file-tell": { + "id": "file-tell", + "name": "File.tell", + "sig": "File.tell(file: f) -> int", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "The cursor position in bytes.", + "page": "file-tell.html" + }, + "file-close": { + "id": "file-close", + "name": "File.close", + "sig": "File.close(file: f)", + "kind": "namespace-method", + "category": "file", + "section": "File", + "tip": "Close a handle from File.open.", + "page": "file-close.html" + }, + "font-load": { + "id": "font-load", + "name": "Font.load", + "sig": "Font.load(path: p) -> int", + "kind": "namespace-method", + "category": "font", + "section": "Font", + "tip": "Load a TrueType font; returns a font handle.", + "page": "font-load.html" + }, + "fx-sparks": { + "id": "fx-sparks", + "name": "Fx.sparks", + "sig": "Fx.sparks(x: px, y: px, color: c, count: n)", + "kind": "namespace-method", + "category": "fx", + "section": "Fx", + "tip": "A burst of sparks flying out of a point.", + "page": "fx-sparks.html" + }, + "fx-number": { + "id": "fx-number", + "name": "Fx.number", + "sig": "Fx.number(x: px, y: px, value: n, color: c)", + "kind": "namespace-method", + "category": "fx", + "section": "Fx", + "tip": "A number that floats up from a point and fades.", + "page": "fx-number.html" + }, + "fx-clear": { + "id": "fx-clear", + "name": "Fx.clear", + "sig": "Fx.clear()", + "kind": "namespace-method", + "category": "fx", + "section": "Fx", + "tip": "Drop every spark and number at once.", + "page": "fx-clear.html" + }, + "prefab-spawn": { + "id": "prefab-spawn", + "name": "Prefab.spawn", + "sig": "Prefab.spawn(name: s) -> entity", + "kind": "namespace-method", + "category": "prefab", + "section": "Prefab", + "tip": "Spawn a prefab chosen by name at runtime; -1 if none has that name.", + "page": "prefab-spawn.html" + }, + "prefab-spawn_at": { + "id": "prefab-spawn_at", + "name": "Prefab.spawn_at", + "sig": "Prefab.spawn_at(name: s, at: IVec2) -> entity", + "kind": "namespace-method", + "category": "prefab", + "section": "Prefab", + "tip": "Spawn a prefab by name and place it.", + "page": "prefab-spawn_at.html" + }, + "app-splash_hide": { + "id": "app-splash_hide", + "name": "App.splash_hide", + "sig": "App.splash_hide() -> void", + "kind": "namespace-method", + "category": "app", + "section": "App", + "tip": "Dismiss the boot splash.", + "page": "app-splash_hide.html" + }, + "app-set_icon": { + "id": "app-set_icon", + "name": "App.set_icon", + "sig": "App.set_icon(path) -> void", + "kind": "namespace-method", + "category": "app", + "section": "App", + "tip": "Set the Dock icon from an image the game ships.", + "page": "app-set_icon.html" + }, + "app-window_hide": { + "id": "app-window_hide", + "name": "App.window_hide", + "sig": "App.window_hide() -> void", + "kind": "namespace-method", + "category": "app", + "section": "App", + "tip": "Take the game's window off the screen.", + "page": "app-window_hide.html" + }, + "app-window_show": { + "id": "app-window_show", + "name": "App.window_show", + "sig": "App.window_show() -> void", + "kind": "namespace-method", + "category": "app", + "section": "App", + "tip": "Bring the game's window back.", + "page": "app-window_show.html" + }, + "phase-start": { + "id": "phase-start", + "name": "Start", + "sig": "phase Start", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "Runs once at boot, before the game loop — the place to set up initial state.", + "page": "phase-start.html" + }, + "phase-input": { + "id": "phase-input", + "name": "Input", + "sig": "phase Input", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "The first per-frame phase — read the keyboard and record the player's intent.", + "page": "phase-input.html" + }, + "phase-fixedupdate": { + "id": "phase-fixedupdate", + "name": "FixedUpdate", + "sig": "phase FixedUpdate", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "The deterministic simulation step — physics and gameplay meant to be reproducible.", + "page": "phase-fixedupdate.html" + }, + "phase-update": { + "id": "phase-update", + "name": "Update", + "sig": "phase Update", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "The ordinary per-frame game-logic step, run after Input and FixedUpdate.", + "page": "phase-update.html" + }, + "phase-lateupdate": { + "id": "phase-lateupdate", + "name": "LateUpdate", + "sig": "phase LateUpdate", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "Runs each frame after Update and before Render.", + "page": "phase-lateupdate.html" + }, + "phase-render": { + "id": "phase-render", + "name": "Render", + "sig": "phase Render", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "The last per-frame phase — draw the world, then call Screen.show() once.", + "page": "phase-render.html" + }, + "phase-overlay": { + "id": "phase-overlay", + "name": "Overlay", + "sig": "phase Overlay", + "kind": "phase", + "category": "phases", + "section": "Phases", + "tip": "The HUD pass — runs after Render and after every engine-owned drawing system.", + "page": "phase-overlay.html" + } + }, + "highlight": { + "keywords": { + "program": "kw-program", + "property": "kw-property", + "model": "kw-model", + "handler": "kw-handler", + "phase": "kw-phase", + "const": "kw-const", + "var": "kw-var", + "let": "kw-let", + "function": "kw-function", + "fn": "kw-fn", + "prefab": "kw-prefab", + "return": "kw-return", + "entry": "kw-entry", + "import": "kw-import", + "public": "kw-public", + "extern": "kw-extern", + "new": "kw-new", + "namespace": "kw-namespace", + "enum": "kw-enum", + "ui": "kw-ui", + "spawn": "kw-spawn", + "despawn": "kw-despawn", + "query": "kw-query", + "self": "kw-self", + "enable": "kw-enable", + "disable": "kw-disable", + "attach": "kw-attach", + "detach": "kw-detach", + "if": "kw-if", + "else": "kw-if", + "while": "kw-while", + "for": "kw-for", + "in": "kw-in", + "match": "kw-match", + "machine": "kw-machine", + "state": "kw-state", + "become": "kw-become", + "break": "kw-break", + "continue": "kw-continue", + "where": "kw-where", + "try": "kw-try", + "scene": "kw-scene", + "layer": "kw-layer", + "on": "kw-on", + "enter": "kw-enter", + "exit": "kw-exit", + "start": "kw-start", + "event": "kw-event", + "emit": "kw-emit", + "cancel": "kw-cancel", + "cancellable": "kw-cancellable", + "test": "kw-test" + }, + "types": { + "int": "type-int", + "fixed": "type-fixed", + "long": "type-long", + "bool": "type-bool", + "float": "type-float", + "string": "type-string", + "double": "type-double", + "entity": "type-entity", + "pointer": "type-pointer", + "words": "type-words", + "byte": "type-byte", + "fixeds": "type-fixeds", + "countdown": "type-countdown", + "floats": "type-floats", + "doubles": "type-floats", + "pointers": "type-pointers", + "Vector": "type-vector", + "IVec2": "type-ivec2", + "Rect": "type-rect", + "void": "type-void" + }, + "phases": { + "Start": "phase-start", + "Input": "phase-input", + "FixedUpdate": "phase-fixedupdate", + "Update": "phase-update", + "LateUpdate": "phase-lateupdate", + "Render": "phase-render", + "Overlay": "phase-overlay" + }, + "builtins": { + "world_attach_dyn": { + "id": "fn-world_attach_dyn", + "params": [ + "target", + "property" + ] + }, + "world_count": { + "id": "fn-world_count", + "params": [] + }, + "world_detach_dyn": { + "id": "fn-world_detach_dyn", + "params": [ + "target", + "property" + ] + }, + "world_field_id": { + "id": "fn-world_field_id", + "params": [ + "property", + "name" + ] + }, + "world_get": { + "id": "fn-world_get", + "params": [ + "target", + "property", + "field" + ] + }, + "world_has": { + "id": "fn-world_has", + "params": [ + "target", + "property" + ] + }, + "world_kind": { + "id": "fn-world_kind", + "params": [ + "target" + ] + }, + "world_load": { + "id": "fn-world_load", + "params": [ + "buf", + "len" + ] + }, + "world_model_id": { + "id": "fn-world_model_id", + "params": [ + "name" + ] + }, + "world_prop_id": { + "id": "fn-world_prop_id", + "params": [ + "name" + ] + }, + "world_query_next": { + "id": "fn-world_query_next", + "params": [ + "property", + "cursor" + ] + }, + "world_register_prop": { + "id": "fn-world_register_prop", + "params": [ + "name", + "fields" + ] + }, + "world_save": { + "id": "fn-world_save", + "params": [ + "buf" + ] + }, + "world_set": { + "id": "fn-world_set", + "params": [ + "target", + "property", + "field", + "value" + ] + }, + "world_size": { + "id": "fn-world_size", + "params": [] + }, + "world_spawn": { + "id": "fn-world_spawn", + "params": [ + "model" + ] + }, + "print": { + "id": "fn-print", + "params": [ + "x" + ] + }, + "string": { + "id": "fn-string", + "params": [ + "x" + ] + }, + "quit": { + "id": "fn-quit", + "params": [] + }, + "save": { + "id": "fn-save", + "params": [] + }, + "load": { + "id": "fn-load", + "params": [] + }, + "len": { + "id": "fn-len", + "params": [ + "x" + ] + }, + "push": { + "id": "fn-push", + "params": [ + "slice", + "x" + ] + }, + "abs": { + "id": "fn-abs", + "params": [ + "a" + ] + }, + "clamp": { + "id": "fn-clamp", + "params": [ + "v", + "lo", + "hi" + ] + }, + "panic": { + "id": "fn-panic", + "params": [ + "msg" + ] + }, + "assert": { + "id": "fn-assert", + "params": [ + "cond", + "msg" + ] + }, + "ok": { + "id": "fn-ok", + "params": [ + "v" + ] + }, + "err": { + "id": "fn-err", + "params": [ + "msg" + ] + }, + "is_ok": { + "id": "fn-is_ok", + "params": [ + "r" + ] + }, + "is_err": { + "id": "fn-is_err", + "params": [ + "r" + ] + }, + "some": { + "id": "fn-some", + "params": [ + "v" + ] + }, + "none": { + "id": "fn-none", + "params": [] + }, + "is_some": { + "id": "fn-is_some", + "params": [ + "o" + ] + }, + "is_none": { + "id": "fn-is_none", + "params": [ + "o" + ] + }, + "unwrap_or": { + "id": "fn-unwrap_or", + "params": [ + "o", + "fallback" + ] + }, + "exit": { + "id": "fn-exit", + "params": [ + "code" + ] + }, + "run": { + "id": "fn-run", + "params": [ + "cmd" + ] + }, + "getenv": { + "id": "fn-getenv", + "params": [ + "name" + ] + }, + "read_char": { + "id": "fn-read_char", + "params": [] + }, + "arg": { + "id": "fn-arg", + "params": [ + "i" + ] + }, + "arg_count": { + "id": "fn-arg_count", + "params": [] + }, + "bytes": { + "id": "fn-bytes", + "params": [ + "n" + ] + }, + "file_stderr": { + "id": "fn-file_stderr", + "params": [] + }, + "file_stdout": { + "id": "fn-file_stdout", + "params": [] + }, + "file_write": { + "id": "fn-file_write", + "params": [ + "handle", + "buf", + "len" + ] + }, + "fixed": { + "id": "fn-fixed", + "params": [ + "x" + ] + }, + "floor": { + "id": "fn-floor", + "params": [ + "x" + ] + }, + "max": { + "id": "fn-max", + "params": [ + "a", + "b" + ] + }, + "min": { + "id": "fn-min", + "params": [ + "a", + "b" + ] + }, + "ui_build": { + "id": "fn-ui_build", + "params": [] + }, + "words": { + "id": "fn-words", + "params": [ + "n" + ] + }, + "float": { + "id": "fn-float", + "params": [ + "x" + ] + }, + "double": { + "id": "fn-float", + "params": [ + "x" + ] + }, + "int": { + "id": "fn-int", + "params": [ + "x" + ] + }, + "long": { + "id": "fn-int", + "params": [ + "x" + ] + }, + "float_bits": { + "id": "fn-float_bits", + "params": [ + "x" + ] + }, + "float_from_bits": { + "id": "fn-float_bits", + "params": [ + "x" + ] + }, + "double_bits": { + "id": "fn-float_bits", + "params": [ + "x" + ] + }, + "double_from_bits": { + "id": "fn-float_bits", + "params": [ + "x" + ] + } + }, + "nsmethods": { + "Screen.clear": { + "id": "screen-clear", + "params": [ + "color" + ] + }, + "Screen.fill_rectangle": { + "id": "screen-fill_rectangle", + "params": [ + "x", + "y", + "width", + "height", + "color" + ] + }, + "Screen.draw_rectangle": { + "id": "screen-draw_rectangle", + "params": [ + "x", + "y", + "width", + "height", + "color" + ] + }, + "Screen.put_pixel": { + "id": "screen-put_pixel", + "params": [ + "x", + "y", + "color" + ] + }, + "Screen.draw_text": { + "id": "screen-draw_text", + "params": [ + "x", + "y", + "text", + "color", + "scale" + ] + }, + "Screen.draw_number": { + "id": "screen-draw_number", + "params": [ + "x", + "y", + "value", + "color", + "scale" + ] + }, + "Screen.show": { + "id": "screen-show", + "params": [] + }, + "Screen.width": { + "id": "screen-width", + "params": [] + }, + "Screen.height": { + "id": "screen-height", + "params": [] + }, + "Screen.status": { + "id": "screen-status", + "params": [ + "text" + ] + }, + "Screen.line": { + "id": "screen-line", + "params": [] + }, + "Screen.circle": { + "id": "screen-circle", + "params": [] + }, + "Screen.fill_circle": { + "id": "screen-fill_circle", + "params": [] + }, + "Screen.triangle": { + "id": "screen-triangle", + "params": [] + }, + "Screen.fill_triangle": { + "id": "screen-fill_triangle", + "params": [] + }, + "Screen.sprite": { + "id": "screen-sprite", + "params": [] + }, + "Screen.sprite_scaled": { + "id": "screen-sprite_scaled", + "params": [] + }, + "Screen.oval": { + "id": "screen-oval", + "params": [] + }, + "Screen.camera": { + "id": "screen-camera", + "params": [] + }, + "Screen.clip": { + "id": "screen-clip", + "params": [] + }, + "Screen.clip_reset": { + "id": "screen-clip_reset", + "params": [] + }, + "Screen.blend_mode": { + "id": "screen-blend_mode", + "params": [] + }, + "Screen.measure_text": { + "id": "screen-measure_text", + "params": [] + }, + "Screen.pixel": { + "id": "screen-pixel", + "params": [] + }, + "Screen.bar": { + "id": "screen-bar", + "params": [] + }, + "Clock.now": { + "id": "clock-now", + "params": [] + }, + "Clock.set": { + "id": "clock-set", + "params": [] + }, + "Clock.advance": { + "id": "clock-advance", + "params": [] + }, + "Clock.reset": { + "id": "clock-reset", + "params": [] + }, + "Collision.rects": { + "id": "collision-rects", + "params": [] + }, + "Collision.point_rect": { + "id": "collision-point_rect", + "params": [] + }, + "Collision.circles": { + "id": "collision-circles", + "params": [] + }, + "Collision.rect_circle": { + "id": "collision-rect_circle", + "params": [] + }, + "Color.rgb": { + "id": "color-rgb", + "params": [] + }, + "Color.rgba": { + "id": "color-rgba", + "params": [] + }, + "Color.lerp": { + "id": "color-lerp", + "params": [] + }, + "Color.darken": { + "id": "color-darken", + "params": [] + }, + "Color.lighten": { + "id": "color-lighten", + "params": [] + }, + "Color.with_alpha": { + "id": "color-with_alpha", + "params": [] + }, + "Crypto.sha256": { + "id": "crypto-sha256", + "params": [ + "s" + ] + }, + "Crypto.hmac_sha256": { + "id": "crypto-hmac_sha256", + "params": [ + "key", + "msg" + ] + }, + "Crypto.verify_hmac": { + "id": "crypto-verify_hmac", + "params": [ + "key", + "msg", + "mac" + ] + }, + "Crypto.hex": { + "id": "crypto-hex", + "params": [ + "s" + ] + }, + "Crypto.ct_equal": { + "id": "crypto-ct_equal", + "params": [ + "a", + "b" + ] + }, + "Crypto.random_bytes": { + "id": "crypto-random_bytes", + "params": [ + "n" + ] + }, + "Crypto.random_hex": { + "id": "crypto-random_hex", + "params": [ + "n" + ] + }, + "Crypto.random_u32": { + "id": "crypto-random_u32", + "params": [] + }, + "Crypto.base64": { + "id": "crypto-base64", + "params": [ + "s" + ] + }, + "Date.new": { + "id": "date-new", + "params": [] + }, + "Date.year": { + "id": "date-year", + "params": [] + }, + "Date.month": { + "id": "date-month", + "params": [] + }, + "Date.day": { + "id": "date-day", + "params": [] + }, + "Date.weekday": { + "id": "date-weekday", + "params": [] + }, + "Date.is_leap": { + "id": "date-is_leap", + "params": [] + }, + "Date.days_in_month": { + "id": "date-days_in_month", + "params": [] + }, + "Date.to_epoch": { + "id": "date-to_epoch", + "params": [] + }, + "Date.add_days": { + "id": "date-add_days", + "params": [] + }, + "Date.diff_days": { + "id": "date-diff_days", + "params": [] + }, + "DateTime.from": { + "id": "datetime-from", + "params": [] + }, + "DateTime.date": { + "id": "datetime-date", + "params": [] + }, + "DateTime.add": { + "id": "datetime-add", + "params": [] + }, + "DateTime.year": { + "id": "datetime-year", + "params": [] + }, + "DateTime.month": { + "id": "datetime-month", + "params": [] + }, + "DateTime.day": { + "id": "datetime-day", + "params": [] + }, + "DateTime.weekday": { + "id": "datetime-weekday", + "params": [] + }, + "DateTime.hour": { + "id": "datetime-hour", + "params": [] + }, + "DateTime.minute": { + "id": "datetime-minute", + "params": [] + }, + "DateTime.second": { + "id": "datetime-second", + "params": [] + }, + "DateTime.format": { + "id": "datetime-format", + "params": [] + }, + "DateTime.parse": { + "id": "datetime-parse", + "params": [] + }, + "Duration.seconds": { + "id": "duration-seconds", + "params": [] + }, + "Duration.minutes": { + "id": "duration-minutes", + "params": [] + }, + "Duration.hours": { + "id": "duration-hours", + "params": [] + }, + "Duration.days": { + "id": "duration-days", + "params": [] + }, + "Duration.as_seconds": { + "id": "duration-as_seconds", + "params": [] + }, + "Duration.as_minutes": { + "id": "duration-as_minutes", + "params": [] + }, + "Duration.as_hours": { + "id": "duration-as_hours", + "params": [] + }, + "Duration.as_days": { + "id": "duration-as_days", + "params": [] + }, + "Ease.in": { + "id": "ease-in", + "params": [] + }, + "Ease.out": { + "id": "ease-out", + "params": [] + }, + "Ease.in_out": { + "id": "ease-in_out", + "params": [] + }, + "Ease.back": { + "id": "ease-back", + "params": [] + }, + "Ease.bounce": { + "id": "ease-bounce", + "params": [] + }, + "Ease.elastic": { + "id": "ease-elastic", + "params": [] + }, + "Fs.exists": { + "id": "fs-exists", + "params": [ + "p" + ] + }, + "Fs.is_dir": { + "id": "fs-is_dir", + "params": [ + "p" + ] + }, + "Fs.read_text": { + "id": "fs-read_text", + "params": [ + "p" + ] + }, + "Fs.write_text": { + "id": "fs-write_text", + "params": [ + "p", + "s" + ] + }, + "Fs.append_text": { + "id": "fs-append_text", + "params": [ + "p", + "s" + ] + }, + "Fs.remove": { + "id": "fs-remove", + "params": [ + "p" + ] + }, + "Fs.size": { + "id": "fs-size", + "params": [ + "p" + ] + }, + "Fs.mkdir": { + "id": "fs-mkdir", + "params": [ + "p" + ] + }, + "Fs.copy": { + "id": "fs-copy", + "params": [ + "src", + "dst" + ] + }, + "Fs.list": { + "id": "fs-list", + "params": [ + "dir" + ] + }, + "Hash.of": { + "id": "hash-of", + "params": [ + "s" + ] + }, + "Hash.fnv1a": { + "id": "hash-fnv1a", + "params": [ + "s" + ] + }, + "Hash.crc32": { + "id": "hash-crc32", + "params": [ + "s" + ] + }, + "Hash.mix": { + "id": "hash-mix", + "params": [ + "x" + ] + }, + "Hash.combine": { + "id": "hash-combine", + "params": [] + }, + "Hash.of64": { + "id": "hash-of64", + "params": [ + "s" + ] + }, + "Hash.fnv1a_64": { + "id": "hash-fnv1a_64", + "params": [ + "s" + ] + }, + "Hash.mix64": { + "id": "hash-mix64", + "params": [ + "x" + ] + }, + "Input.key": { + "id": "input-key", + "params": [] + }, + "Input.bind": { + "id": "input-bind", + "params": [] + }, + "Input.rebind": { + "id": "input-rebind", + "params": [] + }, + "Input.poll": { + "id": "input-poll", + "params": [] + }, + "Input.down": { + "id": "input-down", + "params": [] + }, + "Input.pressed": { + "id": "input-pressed", + "params": [] + }, + "Input.record": { + "id": "input-record", + "params": [] + }, + "Input.replay": { + "id": "input-replay", + "params": [] + }, + "Input.key_down": { + "id": "input-key_down", + "params": [] + }, + "Input.key_pressed": { + "id": "input-key_pressed", + "params": [] + }, + "Input.key_released": { + "id": "input-key_released", + "params": [] + }, + "Input.key_label": { + "id": "input-key_label", + "params": [] + }, + "Input.press": { + "id": "input-press", + "params": [] + }, + "Input.release": { + "id": "input-release", + "params": [] + }, + "Input.axis": { + "id": "input-axis", + "params": [] + }, + "Input.axis_i": { + "id": "input-axis_i", + "params": [] + }, + "Input.vector": { + "id": "input-vector", + "params": [] + }, + "Input.strength": { + "id": "input-strength", + "params": [] + }, + "Input.mouse_x": { + "id": "input-mouse_x", + "params": [] + }, + "Input.mouse_y": { + "id": "input-mouse_y", + "params": [] + }, + "Input.mouse_dx": { + "id": "input-mouse_dx", + "params": [] + }, + "Input.mouse_dy": { + "id": "input-mouse_dy", + "params": [] + }, + "Input.mouse_down": { + "id": "input-mouse_down", + "params": [] + }, + "Input.wheel": { + "id": "input-wheel", + "params": [] + }, + "Input.set_mouse": { + "id": "input-set_mouse", + "params": [] + }, + "Input.pad_connected": { + "id": "input-pad_connected", + "params": [] + }, + "Input.pad_button": { + "id": "input-pad_button", + "params": [] + }, + "Input.pad_axis": { + "id": "input-pad_axis", + "params": [] + }, + "Input.set_pad": { + "id": "input-set_pad", + "params": [] + }, + "Input.touch_count": { + "id": "input-touch_count", + "params": [] + }, + "Input.touch_x": { + "id": "input-touch_x", + "params": [] + }, + "Input.touch_y": { + "id": "input-touch_y", + "params": [] + }, + "Input.set_touch": { + "id": "input-set_touch", + "params": [] + }, + "Input.action": { + "id": "input-action", + "params": [] + }, + "Input.bind_pad": { + "id": "input-bind_pad", + "params": [] + }, + "Input.active": { + "id": "input-active", + "params": [] + }, + "Input.just_pressed": { + "id": "input-just_pressed", + "params": [] + }, + "Input.just_released": { + "id": "input-just_released", + "params": [] + }, + "Input.cursor_mode": { + "id": "input-cursor_mode", + "params": [] + }, + "Input.move_i": { + "id": "input-move_i", + "params": [] + }, + "List.len": { + "id": "list-len", + "params": [ + "s" + ] + }, + "List.push": { + "id": "list-push", + "params": [ + "s", + "v" + ] + }, + "List.clear": { + "id": "list-clear", + "params": [ + "s" + ] + }, + "List.first": { + "id": "list-first", + "params": [ + "s" + ] + }, + "List.last": { + "id": "list-last", + "params": [ + "s" + ] + }, + "List.pop": { + "id": "list-pop", + "params": [ + "s" + ] + }, + "List.swap": { + "id": "list-swap", + "params": [ + "s", + "i", + "j" + ] + }, + "List.contains": { + "id": "list-contains", + "params": [ + "s", + "v" + ] + }, + "List.index_of": { + "id": "list-index_of", + "params": [ + "s", + "v" + ] + }, + "List.reverse": { + "id": "list-reverse", + "params": [ + "s" + ] + }, + "List.insert": { + "id": "list-insert", + "params": [] + }, + "List.remove_at": { + "id": "list-remove_at", + "params": [] + }, + "List.remove": { + "id": "list-remove", + "params": [] + }, + "List.sort": { + "id": "list-sort", + "params": [] + }, + "List.sort_by": { + "id": "list-sort_by", + "params": [] + }, + "List.sort_desc_by": { + "id": "list-sort_desc_by", + "params": [] + }, + "List.sort_with": { + "id": "list-sort_with", + "params": [] + }, + "List.sample": { + "id": "list-sample", + "params": [] + }, + "Log.trace": { + "id": "log-trace", + "params": [ + "msg" + ] + }, + "Log.debug": { + "id": "log-debug", + "params": [ + "msg" + ] + }, + "Log.info": { + "id": "log-info", + "params": [ + "msg" + ] + }, + "Log.warn": { + "id": "log-warn", + "params": [ + "msg" + ] + }, + "Log.error": { + "id": "log-error", + "params": [ + "msg" + ] + }, + "Log.set_level": { + "id": "log-set_level", + "params": [ + "n" + ] + }, + "Log.level": { + "id": "log-level", + "params": [] + }, + "Math.min": { + "id": "math-min", + "params": [ + "a", + "b" + ] + }, + "Math.max": { + "id": "math-max", + "params": [ + "a", + "b" + ] + }, + "Math.abs": { + "id": "math-abs", + "params": [ + "x" + ] + }, + "Math.clamp": { + "id": "math-clamp", + "params": [ + "v", + "lo", + "hi" + ] + }, + "Math.sign": { + "id": "math-sign", + "params": [ + "x" + ] + }, + "Math.floor": { + "id": "math-floor", + "params": [ + "x" + ] + }, + "Math.ceil": { + "id": "math-ceil", + "params": [ + "x" + ] + }, + "Math.round": { + "id": "math-round", + "params": [ + "x" + ] + }, + "Math.lerp": { + "id": "math-lerp", + "params": [ + "a", + "b", + "t" + ] + }, + "Math.inverse_lerp": { + "id": "math-inverse_lerp", + "params": [ + "a", + "b", + "v" + ] + }, + "Math.remap": { + "id": "math-remap", + "params": [ + "v", + "in0", + "in1", + "out0", + "out1" + ] + }, + "Math.sqrt": { + "id": "math-sqrt", + "params": [] + }, + "Math.sin": { + "id": "math-sin", + "params": [] + }, + "Math.cos": { + "id": "math-cos", + "params": [] + }, + "Math.tan": { + "id": "math-tan", + "params": [] + }, + "Math.hypot": { + "id": "math-hypot", + "params": [] + }, + "Math.dist": { + "id": "math-dist", + "params": [] + }, + "Math.dist2": { + "id": "math-dist2", + "params": [] + }, + "Math.deg_to_rad": { + "id": "math-deg_to_rad", + "params": [] + }, + "Math.rad_to_deg": { + "id": "math-rad_to_deg", + "params": [] + }, + "Math.posmod": { + "id": "math-posmod", + "params": [] + }, + "Math.wrap": { + "id": "math-wrap", + "params": [] + }, + "Math.ping_pong": { + "id": "math-ping_pong", + "params": [] + }, + "Math.snapped": { + "id": "math-snapped", + "params": [] + }, + "Math.move_toward": { + "id": "math-move_toward", + "params": [] + }, + "Math.smoothstep": { + "id": "math-smoothstep", + "params": [] + }, + "Math.atan2": { + "id": "math-atan2", + "params": [] + }, + "Math.asin": { + "id": "math-asin", + "params": [] + }, + "Math.acos": { + "id": "math-acos", + "params": [] + }, + "Math.exp": { + "id": "math-exp", + "params": [] + }, + "Math.log": { + "id": "math-log", + "params": [] + }, + "Math.pow": { + "id": "math-pow", + "params": [] + }, + "Memory.bytes": { + "id": "memory-bytes", + "params": [] + }, + "Memory.words": { + "id": "memory-words", + "params": [] + }, + "Memory.copy": { + "id": "memory-copy", + "params": [] + }, + "Memory.fill": { + "id": "memory-fill", + "params": [] + }, + "Memory.peek": { + "id": "memory-peek", + "params": [] + }, + "Memory.poke": { + "id": "memory-poke", + "params": [] + }, + "Mime.of": { + "id": "mime-of", + "params": [ + "path" + ] + }, + "Mime.sniff": { + "id": "mime-sniff", + "params": [ + "path" + ] + }, + "Network.send": { + "id": "network-send", + "params": [] + }, + "Network.poll": { + "id": "network-poll", + "params": [] + }, + "Network.serialize": { + "id": "network-serialize", + "params": [] + }, + "Network.apply": { + "id": "network-apply", + "params": [] + }, + "Network.owner": { + "id": "network-owner", + "params": [] + }, + "Network.set_owner": { + "id": "network-set_owner", + "params": [] + }, + "Network.is_server": { + "id": "network-is_server", + "params": [] + }, + "Network.is_owner": { + "id": "network-is_owner", + "params": [] + }, + "Network.local_id": { + "id": "network-local_id", + "params": [] + }, + "Noise.value2": { + "id": "noise-value2", + "params": [ + "seed" + ] + }, + "Noise.perlin2": { + "id": "noise-perlin2", + "params": [ + "seed" + ] + }, + "Noise.simplex2": { + "id": "noise-simplex2", + "params": [ + "seed" + ] + }, + "Noise.fbm2": { + "id": "noise-fbm2", + "params": [ + "seed", + "octaves" + ] + }, + "Noise.cellular2": { + "id": "noise-cellular2", + "params": [ + "seed" + ] + }, + "Noise.cellular2_id": { + "id": "noise-cellular2_id", + "params": [ + "seed" + ] + }, + "Noise.unit": { + "id": "noise-unit", + "params": [ + "n" + ] + }, + "Os.args": { + "id": "os-args", + "params": [] + }, + "Os.arg_count": { + "id": "os-arg_count", + "params": [] + }, + "Os.arg": { + "id": "os-arg", + "params": [ + "i" + ] + }, + "Os.env": { + "id": "os-env", + "params": [ + "name" + ] + }, + "Os.env_or": { + "id": "os-env_or", + "params": [ + "name", + "fallback" + ] + }, + "Os.has_env": { + "id": "os-has_env", + "params": [ + "name" + ] + }, + "Os.set_env": { + "id": "os-set_env", + "params": [ + "name", + "value" + ] + }, + "Os.unset_env": { + "id": "os-unset_env", + "params": [ + "name" + ] + }, + "Os.exit": { + "id": "os-exit", + "params": [ + "code" + ] + }, + "Os.platform": { + "id": "os-platform", + "params": [] + }, + "Os.arch": { + "id": "os-arch", + "params": [] + }, + "Os.stdout_write": { + "id": "os-stdout_write", + "params": [ + "s" + ] + }, + "Os.stderr_write": { + "id": "os-stderr_write", + "params": [ + "s" + ] + }, + "Os.save_dir": { + "id": "os-save_dir", + "params": [ + "app" + ] + }, + "Os.config_dir": { + "id": "os-config_dir", + "params": [ + "app" + ] + }, + "Os.cache_dir": { + "id": "os-cache_dir", + "params": [ + "app" + ] + }, + "Os.temp_dir": { + "id": "os-temp_dir", + "params": [] + }, + "Os.pid": { + "id": "os-pid", + "params": [] + }, + "Path.join": { + "id": "path-join", + "params": [ + "a", + "b" + ] + }, + "Path.dir": { + "id": "path-dir", + "params": [ + "p" + ] + }, + "Path.base": { + "id": "path-base", + "params": [ + "p" + ] + }, + "Path.ext": { + "id": "path-ext", + "params": [ + "p" + ] + }, + "Path.stem": { + "id": "path-stem", + "params": [ + "p" + ] + }, + "Path.normalize": { + "id": "path-normalize", + "params": [ + "p" + ] + }, + "Save.write": { + "id": "save-write", + "params": [] + }, + "Save.read": { + "id": "save-read", + "params": [] + }, + "System.run": { + "id": "system-run", + "params": [] + }, + "System.read_char": { + "id": "system-read_char", + "params": [] + }, + "System.file_open": { + "id": "system-file_open", + "params": [] + }, + "System.file_read": { + "id": "system-file_read", + "params": [] + }, + "System.file_write": { + "id": "system-file_write", + "params": [] + }, + "System.file_seek": { + "id": "system-file_seek", + "params": [] + }, + "System.file_tell": { + "id": "system-file_tell", + "params": [] + }, + "System.file_close": { + "id": "system-file_close", + "params": [] + }, + "Text.length": { + "id": "text-length", + "params": [ + "s" + ] + }, + "Text.char_at": { + "id": "text-char_at", + "params": [ + "s", + "i" + ] + }, + "Text.slice": { + "id": "text-slice", + "params": [ + "s", + "a", + "b" + ] + }, + "Text.starts_with": { + "id": "text-starts_with", + "params": [ + "s", + "prefix" + ] + }, + "Text.ends_with": { + "id": "text-ends_with", + "params": [ + "s", + "suffix" + ] + }, + "Text.contains": { + "id": "text-contains", + "params": [ + "s", + "sub" + ] + }, + "Text.index_of": { + "id": "text-index_of", + "params": [ + "s", + "sub" + ] + }, + "Text.equals": { + "id": "text-equals", + "params": [ + "a", + "b" + ] + }, + "Text.concat": { + "id": "text-concat", + "params": [ + "a", + "b" + ] + }, + "Text.to_int": { + "id": "text-to_int", + "params": [ + "s" + ] + }, + "Text.from_int": { + "id": "text-from_int", + "params": [ + "n" + ] + }, + "Text.upper": { + "id": "text-upper", + "params": [] + }, + "Text.lower": { + "id": "text-lower", + "params": [] + }, + "Text.trim": { + "id": "text-trim", + "params": [] + }, + "Text.repeat": { + "id": "text-repeat", + "params": [] + }, + "Text.pad_left": { + "id": "text-pad_left", + "params": [] + }, + "Text.pad_right": { + "id": "text-pad_right", + "params": [] + }, + "Text.split": { + "id": "text-split", + "params": [] + }, + "Text.join": { + "id": "text-join", + "params": [] + }, + "Text.replace": { + "id": "text-replace", + "params": [] + }, + "Time.frame": { + "id": "time-frame", + "params": [] + }, + "Time.delta": { + "id": "time-delta", + "params": [] + }, + "Time.elapsed": { + "id": "time-elapsed", + "params": [] + }, + "Time.now": { + "id": "time-now", + "params": [] + }, + "Time.since": { + "id": "time-since", + "params": [] + }, + "Unicode.len": { + "id": "unicode-len", + "params": [ + "s" + ] + }, + "Unicode.byte_len": { + "id": "unicode-byte_len", + "params": [ + "s" + ] + }, + "Unicode.is_valid_utf8": { + "id": "unicode-is_valid_utf8", + "params": [ + "s" + ] + }, + "Unicode.char_at": { + "id": "unicode-char_at", + "params": [ + "s", + "i" + ] + }, + "Unicode.chars": { + "id": "unicode-chars", + "params": [ + "s" + ] + }, + "Unicode.upper": { + "id": "unicode-upper", + "params": [ + "s" + ] + }, + "Unicode.lower": { + "id": "unicode-lower", + "params": [ + "s" + ] + }, + "Unicode.truncate": { + "id": "unicode-truncate", + "params": [ + "s", + "n" + ] + }, + "Unicode.grapheme_len": { + "id": "unicode-grapheme_len", + "params": [ + "s" + ] + }, + "Uuid.new": { + "id": "uuid-new", + "params": [] + }, + "Uuid.v4": { + "id": "uuid-v4", + "params": [] + }, + "Uuid.new_v7": { + "id": "uuid-new_v7", + "params": [] + }, + "Uuid.v7": { + "id": "uuid-v7", + "params": [] + }, + "Uuid.parse": { + "id": "uuid-parse", + "params": [ + "s" + ] + }, + "Uuid.is_valid": { + "id": "uuid-is_valid", + "params": [ + "s" + ] + }, + "Uuid.to_text": { + "id": "uuid-to_text", + "params": [ + "id" + ] + }, + "Uuid.equals": { + "id": "uuid-equals", + "params": [] + }, + "Uuid.nil": { + "id": "uuid-nil", + "params": [] + }, + "World.get": { + "id": "world-get", + "params": [] + }, + "World.set": { + "id": "world-set", + "params": [] + }, + "World.has": { + "id": "world-has", + "params": [] + }, + "World.count": { + "id": "world-count", + "params": [] + }, + "World.size": { + "id": "world-size", + "params": [] + }, + "World.spawn": { + "id": "world-spawn", + "params": [] + }, + "World.save": { + "id": "world-save", + "params": [] + }, + "World.load": { + "id": "world-load", + "params": [] + }, + "World.prop_id": { + "id": "world-prop_id", + "params": [] + }, + "World.field_id": { + "id": "world-field_id", + "params": [] + }, + "World.model_id": { + "id": "world-model_id", + "params": [] + }, + "World.kind": { + "id": "world-kind", + "params": [] + }, + "World.register_prop": { + "id": "world-register_prop", + "params": [] + }, + "World.attach": { + "id": "world-attach", + "params": [] + }, + "World.detach": { + "id": "world-detach", + "params": [] + }, + "World.query_next": { + "id": "world-query_next", + "params": [] + }, + "World.despawn": { + "id": "world-despawn", + "params": [] + }, + "Audio.load": { + "id": "audio-load", + "params": [] + }, + "Audio.play": { + "id": "audio-play", + "params": [] + }, + "Audio.play_at": { + "id": "audio-play_at", + "params": [] + }, + "Audio.play_sound": { + "id": "audio-play_sound", + "params": [] + }, + "Audio.play_music": { + "id": "audio-play_music", + "params": [] + }, + "Audio.stop": { + "id": "audio-stop", + "params": [] + }, + "Audio.stop_music": { + "id": "audio-stop_music", + "params": [] + }, + "Audio.stop_all": { + "id": "audio-stop_all", + "params": [] + }, + "Audio.volume": { + "id": "audio-volume", + "params": [] + }, + "Audio.pitch": { + "id": "audio-pitch", + "params": [] + }, + "Audio.is_playing": { + "id": "audio-is_playing", + "params": [] + }, + "Audio.define": { + "id": "audio-define", + "params": [] + }, + "Audio.named": { + "id": "audio-named", + "params": [] + }, + "Random.range": { + "id": "random-range", + "params": [ + "low", + "high" + ] + }, + "Random.chance": { + "id": "random-chance", + "params": [ + "percent" + ] + }, + "Random.seed": { + "id": "random-seed", + "params": [ + "value" + ] + }, + "Random.value": { + "id": "random-value", + "params": [] + }, + "Random.int": { + "id": "random-int", + "params": [] + }, + "Random.sign": { + "id": "random-sign", + "params": [] + }, + "Random.weighted": { + "id": "random-weighted", + "params": [] + }, + "Vector.make": { + "id": "vector-make", + "params": [] + }, + "Vector.zero": { + "id": "vector-zero", + "params": [] + }, + "Vector.x": { + "id": "vector-x", + "params": [] + }, + "Vector.y": { + "id": "vector-y", + "params": [] + }, + "Vector.add": { + "id": "vector-add", + "params": [] + }, + "Vector.sub": { + "id": "vector-sub", + "params": [] + }, + "Vector.scale": { + "id": "vector-scale", + "params": [] + }, + "Vector.dot": { + "id": "vector-dot", + "params": [] + }, + "Vector.length": { + "id": "vector-length", + "params": [] + }, + "Vector.distance": { + "id": "vector-distance", + "params": [] + }, + "Vector.normalize": { + "id": "vector-normalize", + "params": [] + }, + "Vector.lerp": { + "id": "vector-lerp", + "params": [] + }, + "Vector.rotate": { + "id": "vector-rotate", + "params": [] + }, + "Vector.angle": { + "id": "vector-angle", + "params": [] + }, + "Vector.from_angle": { + "id": "vector-from_angle", + "params": [] + }, + "Http.get": { + "id": "http-get", + "params": [] + }, + "Http.post": { + "id": "http-post", + "params": [] + }, + "Http.request": { + "id": "http-request", + "params": [] + }, + "Http.open": { + "id": "http-open", + "params": [] + }, + "Http.set": { + "id": "http-set", + "params": [] + }, + "Http.body": { + "id": "http-body", + "params": [] + }, + "Http.body_bytes": { + "id": "http-body_bytes", + "params": [] + }, + "Http.send": { + "id": "http-send", + "params": [] + }, + "Http.poll": { + "id": "http-poll", + "params": [] + }, + "Http.status": { + "id": "http-status", + "params": [] + }, + "Http.ok": { + "id": "http-ok", + "params": [] + }, + "Http.text": { + "id": "http-text", + "params": [] + }, + "Http.body_len": { + "id": "http-body_len", + "params": [] + }, + "Http.header": { + "id": "http-header", + "params": [] + }, + "Http.free": { + "id": "http-free", + "params": [] + }, + "Http.parse": { + "id": "http-parse", + "params": [] + }, + "Http.save_to": { + "id": "http-save_to", + "params": [] + }, + "Http.received": { + "id": "http-received", + "params": [] + }, + "Http.expected": { + "id": "http-expected", + "params": [] + }, + "IVec2.make": { + "id": "ivec2-make", + "params": [] + }, + "IVec2.zero": { + "id": "ivec2-zero", + "params": [] + }, + "IVec2.x": { + "id": "ivec2-x", + "params": [] + }, + "IVec2.y": { + "id": "ivec2-y", + "params": [] + }, + "IVec2.add": { + "id": "ivec2-add", + "params": [] + }, + "IVec2.sub": { + "id": "ivec2-sub", + "params": [] + }, + "IVec2.scale": { + "id": "ivec2-scale", + "params": [] + }, + "IVec2.dot": { + "id": "ivec2-dot", + "params": [] + }, + "IVec2.equal": { + "id": "ivec2-equal", + "params": [] + }, + "IVec2.manhattan": { + "id": "ivec2-manhattan", + "params": [] + }, + "IVec2.to_vector": { + "id": "ivec2-to_vector", + "params": [] + }, + "IVec2.distance2": { + "id": "ivec2-distance2", + "params": [] + }, + "IVec2.within": { + "id": "ivec2-within", + "params": [] + }, + "IVec2.heading": { + "id": "ivec2-heading", + "params": [] + }, + "IVec2.along": { + "id": "ivec2-along", + "params": [] + }, + "IVec2.step": { + "id": "ivec2-step", + "params": [] + }, + "Map.size": { + "id": "map-size", + "params": [ + "width", + "height" + ] + }, + "Map.row": { + "id": "map-row", + "params": [ + "y", + "cells" + ] + }, + "Map.tile": { + "id": "map-tile", + "params": [ + "x", + "y" + ] + }, + "Map.get": { + "id": "map-get", + "params": [] + }, + "Map.set": { + "id": "map-set", + "params": [] + }, + "Map.fill": { + "id": "map-fill", + "params": [] + }, + "Map.rect": { + "id": "map-rect", + "params": [] + }, + "Map.border": { + "id": "map-border", + "params": [] + }, + "Map.random_cell": { + "id": "map-random_cell", + "params": [] + }, + "Map.is_solid": { + "id": "map-is_solid", + "params": [] + }, + "Map.is_solid_at": { + "id": "map-is_solid_at", + "params": [] + }, + "Map.width": { + "id": "map-width", + "params": [] + }, + "Map.height": { + "id": "map-height", + "params": [] + }, + "Map.random_cell_far": { + "id": "map-random_cell_far", + "params": [] + }, + "Map.to_tile": { + "id": "map-to_tile", + "params": [] + }, + "Rect.make": { + "id": "rect-make", + "params": [] + }, + "Rect.x": { + "id": "rect-x", + "params": [] + }, + "Rect.y": { + "id": "rect-y", + "params": [] + }, + "Rect.w": { + "id": "rect-w", + "params": [] + }, + "Rect.h": { + "id": "rect-h", + "params": [] + }, + "Rect.right": { + "id": "rect-right", + "params": [] + }, + "Rect.bottom": { + "id": "rect-bottom", + "params": [] + }, + "Rect.center": { + "id": "rect-center", + "params": [] + }, + "Rect.contains": { + "id": "rect-contains", + "params": [] + }, + "Rect.intersects": { + "id": "rect-intersects", + "params": [] + }, + "Udp.open": { + "id": "udp-open", + "params": [] + }, + "Udp.port": { + "id": "udp-port", + "params": [] + }, + "Udp.send": { + "id": "udp-send", + "params": [] + }, + "Udp.recv": { + "id": "udp-recv", + "params": [] + }, + "Udp.from_ip": { + "id": "udp-from_ip", + "params": [] + }, + "Udp.from_port": { + "id": "udp-from_port", + "params": [] + }, + "Udp.close": { + "id": "udp-close", + "params": [] + }, + "Udp.resolve": { + "id": "udp-resolve", + "params": [] + }, + "Udp.local_ip": { + "id": "udp-local_ip", + "params": [] + }, + "Udp.ip": { + "id": "udp-ip", + "params": [] + }, + "Udp.ip_text": { + "id": "udp-ip_text", + "params": [] + }, + "BigInt.from": { + "id": "bigint-from", + "params": [] + }, + "BigInt.parse": { + "id": "bigint-parse", + "params": [] + }, + "BigInt.add": { + "id": "bigint-add", + "params": [] + }, + "BigInt.sub": { + "id": "bigint-sub", + "params": [] + }, + "BigInt.mul": { + "id": "bigint-mul", + "params": [] + }, + "BigInt.neg": { + "id": "bigint-neg", + "params": [] + }, + "BigInt.pow": { + "id": "bigint-pow", + "params": [] + }, + "BigInt.div": { + "id": "bigint-div", + "params": [] + }, + "BigInt.mod": { + "id": "bigint-mod", + "params": [] + }, + "BigInt.cmp": { + "id": "bigint-cmp", + "params": [] + }, + "BigInt.eq": { + "id": "bigint-eq", + "params": [] + }, + "BigInt.is_zero": { + "id": "bigint-is_zero", + "params": [] + }, + "BigInt.to_int": { + "id": "bigint-to_int", + "params": [] + }, + "BigInt.str": { + "id": "bigint-str", + "params": [] + }, + "Process.spawn": { + "id": "process-spawn", + "params": [] + }, + "Process.poll": { + "id": "process-poll", + "params": [] + }, + "Process.kill": { + "id": "process-kill", + "params": [] + }, + "Process.free": { + "id": "process-free", + "params": [] + }, + "Decimal.from": { + "id": "decimal-from", + "params": [] + }, + "Decimal.parse": { + "id": "decimal-parse", + "params": [] + }, + "Decimal.add": { + "id": "decimal-add", + "params": [] + }, + "Decimal.sub": { + "id": "decimal-sub", + "params": [] + }, + "Decimal.mul": { + "id": "decimal-mul", + "params": [] + }, + "Decimal.neg": { + "id": "decimal-neg", + "params": [] + }, + "Decimal.cmp": { + "id": "decimal-cmp", + "params": [] + }, + "Decimal.eq": { + "id": "decimal-eq", + "params": [] + }, + "Decimal.scale": { + "id": "decimal-scale", + "params": [] + }, + "Decimal.rescale": { + "id": "decimal-rescale", + "params": [] + }, + "Decimal.str": { + "id": "decimal-str", + "params": [] + }, + "Dict.new": { + "id": "dict-new", + "params": [] + }, + "Dict.set": { + "id": "dict-set", + "params": [] + }, + "Dict.get": { + "id": "dict-get", + "params": [] + }, + "Dict.get_or": { + "id": "dict-get_or", + "params": [] + }, + "Dict.has": { + "id": "dict-has", + "params": [] + }, + "Dict.remove": { + "id": "dict-remove", + "params": [] + }, + "Dict.size": { + "id": "dict-size", + "params": [] + }, + "Dict.clear": { + "id": "dict-clear", + "params": [] + }, + "Dict.keys": { + "id": "dict-keys", + "params": [] + }, + "Set.new": { + "id": "set-new", + "params": [] + }, + "Set.add": { + "id": "set-add", + "params": [] + }, + "Set.has": { + "id": "set-has", + "params": [] + }, + "Set.remove": { + "id": "set-remove", + "params": [] + }, + "Set.size": { + "id": "set-size", + "params": [] + }, + "Set.clear": { + "id": "set-clear", + "params": [] + }, + "Set.members": { + "id": "set-members", + "params": [] + }, + "Huge.from": { + "id": "huge-from", + "params": [] + }, + "Huge.add": { + "id": "huge-add", + "params": [] + }, + "Huge.sub": { + "id": "huge-sub", + "params": [] + }, + "Huge.mul": { + "id": "huge-mul", + "params": [] + }, + "Huge.neg": { + "id": "huge-neg", + "params": [] + }, + "Huge.cmp": { + "id": "huge-cmp", + "params": [] + }, + "Huge.sign": { + "id": "huge-sign", + "params": [] + }, + "Huge.mantissa": { + "id": "huge-mantissa", + "params": [] + }, + "Huge.exp": { + "id": "huge-exp", + "params": [] + }, + "Huge.str": { + "id": "huge-str", + "params": [] + }, + "Angle.from_degrees": { + "id": "angle-from_degrees", + "params": [] + }, + "Angle.to_degrees": { + "id": "angle-to_degrees", + "params": [] + }, + "Angle.wrap": { + "id": "angle-wrap", + "params": [] + }, + "Angle.sin": { + "id": "angle-sin", + "params": [] + }, + "Angle.cos": { + "id": "angle-cos", + "params": [] + }, + "Angle.add": { + "id": "angle-add", + "params": [] + }, + "Angle.diff": { + "id": "angle-diff", + "params": [] + }, + "Angle.lerp": { + "id": "angle-lerp", + "params": [] + }, + "Angle.diff_degrees": { + "id": "angle-diff_degrees", + "params": [] + }, + "Percent.clamp": { + "id": "percent-clamp", + "params": [] + }, + "Percent.of": { + "id": "percent-of", + "params": [] + }, + "Percent.lerp": { + "id": "percent-lerp", + "params": [] + }, + "Percent.apply": { + "id": "percent-apply", + "params": [] + }, + "Regex.compile": { + "id": "regex-compile", + "params": [ + "pattern" + ] + }, + "Regex.valid": { + "id": "regex-valid", + "params": [ + "pattern" + ] + }, + "Regex.matches": { + "id": "regex-matches", + "params": [ + "text", + "pattern" + ] + }, + "Regex.test": { + "id": "regex-test", + "params": [ + "text", + "re" + ] + }, + "Regex.find": { + "id": "regex-find", + "params": [ + "text", + "pattern" + ] + }, + "Regex.exec": { + "id": "regex-exec", + "params": [ + "text", + "re" + ] + }, + "Regex.next": { + "id": "regex-next", + "params": [ + "text", + "re", + "from" + ] + }, + "Regex.replace": { + "id": "regex-replace", + "params": [ + "text", + "pattern", + "replacement" + ] + }, + "Regex.group": { + "id": "regex-group", + "params": [ + "match", + "n" + ] + }, + "Regex.group_count": { + "id": "regex-group_count", + "params": [ + "match" + ] + }, + "Regex.start": { + "id": "regex-start", + "params": [ + "match", + "n" + ] + }, + "Regex.end": { + "id": "regex-end", + "params": [ + "match", + "n" + ] + }, + "Regex.ok": { + "id": "regex-ok", + "params": [ + "match" + ] + }, + "Grid.line": { + "id": "grid-line", + "params": [] + }, + "Grid.blocked": { + "id": "grid-blocked", + "params": [ + "wall" + ] + }, + "Grid.line_of_sight": { + "id": "grid-line_of_sight", + "params": [ + "wall" + ] + }, + "Grid.flood": { + "id": "grid-flood", + "params": [ + "wall" + ] + }, + "Grid.a_star": { + "id": "grid-a_star", + "params": [ + "wall" + ] + }, + "Anim.frame": { + "id": "anim-frame", + "params": [ + "timer", + "fps", + "count" + ] + }, + "Anim.once": { + "id": "anim-once", + "params": [ + "timer", + "fps", + "count" + ] + }, + "Anim.pingpong": { + "id": "anim-pingpong", + "params": [ + "timer", + "fps", + "count" + ] + }, + "Anim.finished": { + "id": "anim-finished", + "params": [ + "timer", + "fps", + "count" + ] + }, + "Anim.duration": { + "id": "anim-duration", + "params": [ + "fps", + "count" + ] + }, + "Anim.cell_x": { + "id": "anim-cell_x", + "params": [ + "frame", + "cols", + "cell_w" + ] + }, + "Anim.cell_y": { + "id": "anim-cell_y", + "params": [ + "frame", + "cols", + "cell_h" + ] + }, + "Anim.play": { + "id": "anim-play", + "params": [ + "entity", + "clip" + ] + }, + "Anim.clip": { + "id": "anim-clip", + "params": [ + "name", + "frames", + "fps", + "mode" + ] + }, + "Anim.on_frame": { + "id": "anim-on_frame", + "params": [ + "entity", + "frame" + ] + }, + "Anim.fired": { + "id": "anim-fired", + "params": [ + "entity" + ] + }, + "Motion.to": { + "id": "motion-to", + "params": [ + "entity", + "dur", + "ease" + ] + }, + "Tween.progress": { + "id": "tween-progress", + "params": [ + "timer", + "duration" + ] + }, + "Tween.loop": { + "id": "tween-loop", + "params": [ + "timer", + "duration" + ] + }, + "Tween.yoyo": { + "id": "tween-yoyo", + "params": [ + "timer", + "duration" + ] + }, + "Tween.done": { + "id": "tween-done", + "params": [ + "timer", + "duration" + ] + }, + "Tween.ease": { + "id": "tween-ease", + "params": [ + "t", + "mode" + ] + }, + "Tween.number": { + "id": "tween-number", + "params": [ + "from", + "to", + "t" + ] + }, + "Tween.round": { + "id": "tween-round", + "params": [ + "from", + "to", + "t" + ] + }, + "Tween.point": { + "id": "tween-point", + "params": [ + "from", + "to", + "t" + ] + }, + "Tween.tint": { + "id": "tween-tint", + "params": [ + "from", + "to", + "t" + ] + }, + "Tween.to": { + "id": "tween-to", + "params": [ + "dur", + "ease" + ] + }, + "Tween.chain": { + "id": "tween-chain", + "params": [ + "handle", + "to", + "dur", + "ease" + ] + }, + "Tween.delay": { + "id": "tween-delay", + "params": [ + "handle", + "ticks" + ] + }, + "Tween.value": { + "id": "tween-value", + "params": [ + "handle" + ] + }, + "Tween.stop": { + "id": "tween-stop", + "params": [ + "handle" + ] + }, + "Tween.parallel": { + "id": "tween-parallel", + "params": [] + }, + "Query.count": { + "id": "query-count", + "params": [ + "prop" + ] + }, + "Query.first": { + "id": "query-first", + "params": [ + "prop" + ] + }, + "Query.nearest": { + "id": "query-nearest", + "params": [ + "prop", + "pos" + ] + }, + "Query.within": { + "id": "query-within", + "params": [ + "prop", + "pos", + "radius" + ] + }, + "Reflect.prop": { + "id": "reflect-prop", + "params": [ + "name" + ] + }, + "Reflect.field": { + "id": "reflect-field", + "params": [ + "prop", + "name" + ] + }, + "Reflect.prop_count": { + "id": "reflect-prop_count", + "params": [] + }, + "Reflect.prop_name": { + "id": "reflect-prop_name", + "params": [ + "index" + ] + }, + "Reflect.field_count": { + "id": "reflect-field_count", + "params": [ + "prop" + ] + }, + "Reflect.field_name": { + "id": "reflect-field_name", + "params": [ + "prop", + "index" + ] + }, + "Reflect.field_type": { + "id": "reflect-field_type", + "params": [ + "prop", + "index" + ] + }, + "Reflect.get": { + "id": "reflect-get", + "params": [ + "entity", + "prop", + "field" + ] + }, + "Reflect.set": { + "id": "reflect-set", + "params": [ + "entity", + "prop", + "field", + "value" + ] + }, + "Reflect.has": { + "id": "reflect-has", + "params": [ + "entity", + "prop" + ] + }, + "Reflect.kind": { + "id": "reflect-kind", + "params": [ + "entity" + ] + }, + "Reflect.model": { + "id": "reflect-model", + "params": [ + "name" + ] + }, + "Reflect.serialize": { + "id": "reflect-serialize", + "params": [] + }, + "Reflect.apply": { + "id": "reflect-apply", + "params": [] + }, + "Camera.set": { + "id": "camera-set", + "params": [] + }, + "Camera.follow": { + "id": "camera-follow", + "params": [ + "lerp" + ] + }, + "Camera.shake": { + "id": "camera-shake", + "params": [ + "amount" + ] + }, + "Camera.zoom": { + "id": "camera-zoom", + "params": [ + "scale" + ] + }, + "Camera.shake_for": { + "id": "camera-shake_for", + "params": [] + }, + "Light.ambient": { + "id": "light-ambient", + "params": [ + "color" + ] + }, + "Light.point": { + "id": "light-point", + "params": [ + "radius", + "color", + "energy" + ] + }, + "Light.occlude": { + "id": "light-occlude", + "params": [] + }, + "Light.clear_occluders": { + "id": "light-clear_occluders", + "params": [] + }, + "Light.spot": { + "id": "light-spot", + "params": [ + "radius", + "color", + "energy", + "direction", + "spread" + ] + }, + "Light.falloff": { + "id": "light-falloff", + "params": [ + "exponent" + ] + }, + "Light.soft": { + "id": "light-soft", + "params": [ + "radius" + ] + }, + "Light.gel": { + "id": "light-gel", + "params": [ + "color" + ] + }, + "Light.clear_gel": { + "id": "light-clear_gel", + "params": [] + }, + "Light.normal": { + "id": "light-normal", + "params": [] + }, + "Light.clear_normals": { + "id": "light-clear_normals", + "params": [] + }, + "Light.height": { + "id": "light-height", + "params": [ + "height" + ] + }, + "Light.time_of_day": { + "id": "light-time_of_day", + "params": [ + "t" + ] + }, + "Sprite.sheet": { + "id": "sprite-sheet", + "params": [] + }, + "Sprite.cell": { + "id": "sprite-cell", + "params": [] + }, + "Sprite.cell_span": { + "id": "sprite-cell_span", + "params": [] + }, + "Sprite.define": { + "id": "sprite-define", + "params": [] + }, + "Sprite.named": { + "id": "sprite-named", + "params": [] + }, + "Sprite.draw": { + "id": "sprite-draw", + "params": [] + }, + "Sprite.draw_scaled": { + "id": "sprite-draw_scaled", + "params": [] + }, + "Sprite.width": { + "id": "sprite-width", + "params": [] + }, + "Sprite.height": { + "id": "sprite-height", + "params": [] + }, + "Sprite.strip": { + "id": "sprite-strip", + "params": [] + }, + "Sprite.draw_meter": { + "id": "sprite-draw_meter", + "params": [] + }, + "Assets.image": { + "id": "assets-image", + "params": [] + }, + "Assets.load": { + "id": "assets-load", + "params": [] + }, + "Assets.get": { + "id": "assets-get", + "params": [] + }, + "Assets.enqueue": { + "id": "assets-enqueue", + "params": [] + }, + "Assets.pump": { + "id": "assets-pump", + "params": [] + }, + "Assets.total": { + "id": "assets-total", + "params": [] + }, + "Assets.loaded": { + "id": "assets-loaded", + "params": [] + }, + "Assets.ready": { + "id": "assets-ready", + "params": [] + }, + "Assets.font": { + "id": "assets-font", + "params": [] + }, + "Assets.progress": { + "id": "assets-progress", + "params": [] + }, + "Assets.enqueue_dir": { + "id": "assets-enqueue_dir", + "params": [] + }, + "Pool.live": { + "id": "pool-live", + "params": [] + }, + "Pool.free": { + "id": "pool-free", + "params": [] + }, + "Pool.reserved": { + "id": "pool-reserved", + "params": [] + }, + "Pool.capacity": { + "id": "pool-capacity", + "params": [] + }, + "Value.null": { + "id": "value-null", + "params": [] + }, + "Value.int": { + "id": "value-int", + "params": [] + }, + "Value.fixed": { + "id": "value-fixed", + "params": [] + }, + "Value.bool": { + "id": "value-bool", + "params": [] + }, + "Value.str": { + "id": "value-str", + "params": [] + }, + "Value.list": { + "id": "value-list", + "params": [] + }, + "Value.object": { + "id": "value-object", + "params": [] + }, + "Value.add": { + "id": "value-add", + "params": [] + }, + "Value.put": { + "id": "value-put", + "params": [] + }, + "Value.get": { + "id": "value-get", + "params": [] + }, + "Value.has": { + "id": "value-has", + "params": [] + }, + "Value.at": { + "id": "value-at", + "params": [] + }, + "Value.key_at": { + "id": "value-key_at", + "params": [] + }, + "Value.count": { + "id": "value-count", + "params": [] + }, + "Value.kind": { + "id": "value-kind", + "params": [] + }, + "Value.as_int": { + "id": "value-as_int", + "params": [] + }, + "Value.as_str": { + "id": "value-as_str", + "params": [] + }, + "Json.encode": { + "id": "json-encode", + "params": [] + }, + "Json.parse": { + "id": "json-parse", + "params": [] + }, + "Job.defer": { + "id": "job-defer", + "params": [] + }, + "Job.run": { + "id": "job-run", + "params": [] + }, + "Job.fulfill": { + "id": "job-fulfill", + "params": [] + }, + "Job.fail": { + "id": "job-fail", + "params": [] + }, + "Job.cancel": { + "id": "job-cancel", + "params": [] + }, + "Job.pump": { + "id": "job-pump", + "params": [] + }, + "Job.done": { + "id": "job-done", + "params": [] + }, + "Job.ok": { + "id": "job-ok", + "params": [] + }, + "Job.failed": { + "id": "job-failed", + "params": [] + }, + "Job.cancelled": { + "id": "job-cancelled", + "params": [] + }, + "Job.result": { + "id": "job-result", + "params": [] + }, + "Job.error": { + "id": "job-error", + "params": [] + }, + "Job.pending": { + "id": "job-pending", + "params": [] + }, + "Job.free": { + "id": "job-free", + "params": [] + }, + "Job.parallel_for": { + "id": "job-parallel_for", + "params": [] + }, + "Job.is_worker": { + "id": "job-is_worker", + "params": [] + }, + "Promise.all": { + "id": "promise-all", + "params": [] + }, + "Promise.race": { + "id": "promise-race", + "params": [] + }, + "Promise.count_done": { + "id": "promise-count_done", + "params": [] + }, + "Promise.all_done": { + "id": "promise-all_done", + "params": [] + }, + "Sync.mutex": { + "id": "sync-mutex", + "params": [] + }, + "Sync.lock": { + "id": "sync-lock", + "params": [] + }, + "Sync.unlock": { + "id": "sync-unlock", + "params": [] + }, + "Sync.try_lock": { + "id": "sync-try_lock", + "params": [] + }, + "Sync.atomic": { + "id": "sync-atomic", + "params": [] + }, + "Sync.get": { + "id": "sync-get", + "params": [] + }, + "Sync.set": { + "id": "sync-set", + "params": [] + }, + "Sync.add": { + "id": "sync-add", + "params": [] + }, + "Sync.cas": { + "id": "sync-cas", + "params": [] + }, + "Sync.channel": { + "id": "sync-channel", + "params": [] + }, + "Sync.send": { + "id": "sync-send", + "params": [] + }, + "Sync.recv": { + "id": "sync-recv", + "params": [] + }, + "Sync.can_recv": { + "id": "sync-can_recv", + "params": [] + }, + "Sync.len": { + "id": "sync-len", + "params": [] + }, + "Sync.cpu_count": { + "id": "sync-cpu_count", + "params": [] + }, + "Xml.parse": { + "id": "xml-parse", + "params": [] + }, + "Xml.tag": { + "id": "xml-tag", + "params": [] + }, + "Xml.text": { + "id": "xml-text", + "params": [] + }, + "Xml.attr": { + "id": "xml-attr", + "params": [] + }, + "Xml.attr_int": { + "id": "xml-attr_int", + "params": [] + }, + "Xml.has": { + "id": "xml-has", + "params": [] + }, + "Xml.attr_count": { + "id": "xml-attr_count", + "params": [] + }, + "Xml.child_count": { + "id": "xml-child_count", + "params": [] + }, + "Xml.child": { + "id": "xml-child", + "params": [] + }, + "Xml.find": { + "id": "xml-find", + "params": [] + }, + "Xml.count": { + "id": "xml-count", + "params": [] + }, + "Base64.decode": { + "id": "base64-decode", + "params": [] + }, + "Base64.encode": { + "id": "base64-encode", + "params": [] + }, + "Tiled.read": { + "id": "tiled-read", + "params": [] + }, + "Tiled.read_tsx": { + "id": "tiled-read_tsx", + "params": [] + }, + "Tiled.load": { + "id": "tiled-load", + "params": [] + }, + "Tiled.gid": { + "id": "tiled-gid", + "params": [] + }, + "Tiled.resolve": { + "id": "tiled-resolve", + "params": [] + }, + "Tiled.width": { + "id": "tiled-width", + "params": [] + }, + "Tiled.height": { + "id": "tiled-height", + "params": [] + }, + "Tiled.layer_count": { + "id": "tiled-layer_count", + "params": [] + }, + "Tiled.layer_name": { + "id": "tiled-layer_name", + "params": [] + }, + "Tiled.draw": { + "id": "tiled-draw", + "params": [] + }, + "Tiled.project": { + "id": "tiled-project", + "params": [] + }, + "Tiled.tree": { + "id": "tiled-tree", + "params": [] + }, + "Tiled.tile_prop": { + "id": "tiled-tile_prop", + "params": [] + }, + "Tiled.collide": { + "id": "tiled-collide", + "params": [] + }, + "Tiled.collision_kind": { + "id": "tiled-collision_kind", + "params": [] + }, + "Tiled.tile_shapes": { + "id": "tiled-tile_shapes", + "params": [] + }, + "Tiled.draw_anim": { + "id": "tiled-draw_anim", + "params": [] + }, + "Tiled.frame_gid": { + "id": "tiled-frame_gid", + "params": [] + }, + "Tiled.animated": { + "id": "tiled-animated", + "params": [] + }, + "Tiled.object_count": { + "id": "tiled-object_count", + "params": [] + }, + "Tiled.object": { + "id": "tiled-object", + "params": [] + }, + "Tiled.object_shape": { + "id": "tiled-object_shape", + "params": [] + }, + "Tiled.prop": { + "id": "tiled-prop", + "params": [] + }, + "Tiled.prop_int": { + "id": "tiled-prop_int", + "params": [] + }, + "Tiled.prop_type": { + "id": "tiled-prop_type", + "params": [] + }, + "Tiled.load_types": { + "id": "tiled-load_types", + "params": [] + }, + "Tiled.template": { + "id": "tiled-template", + "params": [] + }, + "Tiled.spawn": { + "id": "tiled-spawn", + "params": [] + }, + "Tiled.spawn_layer": { + "id": "tiled-spawn_layer", + "params": [] + }, + "Tiled.cell_x": { + "id": "tiled-cell_x", + "params": [] + }, + "Tiled.cell_y": { + "id": "tiled-cell_y", + "params": [] + }, + "Tiled.layer_kind": { + "id": "tiled-layer_kind", + "params": [] + }, + "Tiled.layer_opacity": { + "id": "tiled-layer_opacity", + "params": [] + }, + "Tiled.layer_tint": { + "id": "tiled-layer_tint", + "params": [] + }, + "Tiled.layer_offsetx": { + "id": "tiled-layer_offsetx", + "params": [] + }, + "Tiled.layer_offsety": { + "id": "tiled-layer_offsety", + "params": [] + }, + "Tiled.world": { + "id": "tiled-world", + "params": [] + }, + "Tiled.world_count": { + "id": "tiled-world_count", + "params": [] + }, + "Tiled.world_map": { + "id": "tiled-world_map", + "params": [] + }, + "Ui.build": { + "id": "ui-build", + "params": [] + }, + "Ui.open": { + "id": "ui-open", + "params": [] + }, + "Ui.close": { + "id": "ui-close", + "params": [] + }, + "Ui.tick": { + "id": "ui-tick", + "params": [] + }, + "Ui.clicked": { + "id": "ui-clicked", + "params": [] + }, + "Ui.set_text": { + "id": "ui-set_text", + "params": [] + }, + "Ui.render": { + "id": "ui-render", + "params": [] + }, + "File.open": { + "id": "file-open", + "params": [] + }, + "File.read": { + "id": "file-read", + "params": [] + }, + "File.write": { + "id": "file-write", + "params": [] + }, + "File.seek": { + "id": "file-seek", + "params": [] + }, + "File.tell": { + "id": "file-tell", + "params": [] + }, + "File.close": { + "id": "file-close", + "params": [] + }, + "Font.load": { + "id": "font-load", + "params": [] + }, + "Fx.sparks": { + "id": "fx-sparks", + "params": [] + }, + "Fx.number": { + "id": "fx-number", + "params": [] + }, + "Fx.clear": { + "id": "fx-clear", + "params": [] + }, + "Prefab.spawn": { + "id": "prefab-spawn", + "params": [] + }, + "Prefab.spawn_at": { + "id": "prefab-spawn_at", + "params": [] + }, + "App.splash_hide": { + "id": "app-splash_hide", + "params": [] + }, + "App.set_icon": { + "id": "app-set_icon", + "params": [ + "path" + ] + }, + "App.window_hide": { + "id": "app-window_hide", + "params": [] + }, + "App.window_show": { + "id": "app-window_show", + "params": [] + } + }, + "annotations": { + "@Queries": "annot-queries", + "@Computed": "annot-computed", + "@On": "annot-on", + "@export": "annot-export", + "@Sync": "annot-sync", + "@Handles": "annot-handles", + "@OnAttach": "annot-onattach", + "@OnDespawn": "annot-ondespawn", + "@OnDetach": "annot-ondetach", + "@OnDisable": "annot-ondisable", + "@OnEnable": "annot-onenable", + "@OnQuit": "annot-onquit", + "@OnSpawn": "annot-onspawn", + "@OnStart": "annot-onstart", + "@Owned": "annot-owned", + "@Predicted": "annot-predicted", + "@Public": "annot-public", + "@Reads": "annot-reads", + "@Server": "annot-server", + "@ToClients": "annot-toclients", + "@ToServer": "annot-toserver", + "@Writes": "annot-writes", + "@EngineSystem": "annot-enginesystem", + "@Namespace": "annot-namespace", + "\"@ClearColor\"": "annot-clearcolor", + "@System": "annot-system" + }, + "namespaces": { + "Angle": "ns-angle", + "Anim": "ns-anim", + "App": "ns-app", + "Assets": "ns-assets", + "Audio": "ns-audio", + "Base64": "ns-base64", + "BigInt": "ns-bigint", + "Camera": "ns-camera", + "Clock": "ns-clock", + "Collision": "ns-collision", + "Color": "ns-color", + "Crypto": "ns-crypto", + "Date": "ns-date", + "DateTime": "ns-datetime", + "Decimal": "ns-decimal", + "Dict": "ns-dict", + "Duration": "ns-duration", + "Ease": "ns-ease", + "File": "ns-file", + "Font": "ns-font", + "Fs": "ns-fs", + "Fx": "ns-fx", + "Grid": "ns-grid", + "Hash": "ns-hash", + "Http": "ns-http", + "Huge": "ns-huge", + "IVec2": "ns-ivec2", + "Input": "ns-input", + "Job": "ns-job", + "Json": "ns-json", + "Light": "ns-light", + "List": "ns-list", + "Log": "ns-log", + "Map": "ns-map", + "Math": "ns-math", + "Memory": "ns-memory", + "Mime": "ns-mime", + "Motion": "ns-motion", + "Network": "ns-network", + "Noise": "ns-noise", + "Os": "ns-os", + "Path": "ns-path", + "Percent": "ns-percent", + "Pool": "ns-pool", + "Prefab": "ns-prefab", + "Process": "ns-process", + "Promise": "ns-promise", + "Query": "ns-query", + "Random": "ns-random", + "Rect": "ns-rect", + "Reflect": "ns-reflect", + "Regex": "ns-regex", + "Save": "ns-save", + "Screen": "ns-screen", + "Set": "ns-set", + "Sprite": "ns-sprite", + "Sync": "ns-sync", + "System": "ns-system", + "Text": "ns-text", + "Tiled": "ns-tiled", + "Time": "ns-time", + "Tween": "ns-tween", + "Udp": "ns-udp", + "Ui": "ns-ui", + "Unicode": "ns-unicode", + "Uuid": "ns-uuid", + "Value": "ns-value", + "Vector": "ns-vector", + "World": "ns-world", + "Xml": "ns-xml" + }, + "tips": { + "program": "The top-level unit — one program compiles to one native game.", + "property": "A named record of typed fields — a per-model component, or a plain heap record.", + "model": "A named kind of thing — a fixed bundle of properties you spawn by one name.", + "handler": "A named block the engine runs each frame during phase P.", + "phase": "Names which stage of the frame a handler runs in.", + "const": "A compile-time constant, folded into the code with no storage.", + "var": "A mutable binding — at program scope, your game's persistent named state.", + "let": "An immutable binding — the default choice for a value that never changes.", + "function": "A function — reusable logic called positionally or with named arguments.", + "fn": "A top-level function named as a value - the entry point handed to Job.parallel_for.", + "prefab": "A model with preset component fields — spawn it, override what varies.", + "return": "Hand a value back from a function and stop running it.", + "entry": "The program's main block — runs once, top to bottom.", + "import": "Splice another Ludic file's declarations into this program.", + "public": "Expose a declaration's lifecycle on the public event bus.", + "extern": "Bind a name to an external native symbol — the seam for platform and library calls.", + "new": "Allocate a property record or an empty slice on the heap.", + "namespace": "Group functions into a Name.* namespace and control its public surface.", + "enum": "A named set of integer constants — names for a magic-number space.", + "ui": "Declare a retained widget tree as data; the engine lays it out and draws it.", + "spawn": "Create a model instance carrying the listed properties, seeding their fields.", + "despawn": "Remove a model instance from the world, firing any @OnDespawn hooks.", + "query": "Match every model instance carrying all the listed properties.", + "self": "The model instance currently bound by the enclosing query loop.", + "enable": "Re-activate a disabled property, model, or handler — its data is intact.", + "disable": "Deactivate a property, model, or handler without destroying its data.", + "attach": "Structurally add a property to a live model instance, seeding its fields.", + "detach": "Structurally remove a property from a live model instance.", + "world_attach_dyn": "Attach a property to an instance by numeric id at runtime (dynamic ECS).", + "world_count": "The total number of live model instances in the world.", + "world_detach_dyn": "Detach a property from an instance by numeric id at runtime (dynamic ECS).", + "world_field_id": "Resolve a field name within a property to its numeric index.", + "world_get": "Read one field of a model instance by numeric id (reflection ABI).", + "world_has": "Test whether a model instance currently carries a property.", + "world_kind": "The model id of an instance — which kind of thing it is.", + "world_load": "Restore the whole world from a serialized byte buffer.", + "world_model_id": "Resolve a model's name to its stable numeric id.", + "world_prop_id": "Resolve a property's name to its stable numeric id.", + "world_query_next": "Step to the next instance carrying a property, walking the world by id.", + "world_register_prop": "Register a brand-new property at runtime and get its id (dynamic ECS).", + "world_save": "Serialize the whole world into a buffer; returns the number of bytes written.", + "world_set": "Write one field of a model instance by numeric id (reflection ABI).", + "world_size": "The number of live model instances in the world.", + "world_spawn": "Spawn an instance of a model chosen by numeric id at runtime.", + "if": "A branch — run one block when a condition holds, another when it doesn't.", + "else": "A branch — run one block when a condition holds, another when it doesn't.", + "while": "Loop as long as a condition holds, re-checking it before each pass.", + "for": "Range loop — iterate the half-open range from a up to but not including b.", + "in": "The part of a for loop that names what to iterate — a range or a query.", + "match": "Multi-way branch on one value, matching one or more literals per arm.", + "machine": "An explicit state machine over an int var — dispatches on the store's value.", + "state": "One state of a machine — its body runs while the machine sits in it.", + "become": "Transition to another state of the enclosing machine, or to another scene.", + "break": "Leave the enclosing loop immediately.", + "continue": "Skip to the next iteration of the loop.", + "where": "Filter a query loop to entities that satisfy a condition.", + "try": "Recover a fallible result as a value, with a fallback — no exceptions, no unwinding.", + "scene": "A mutually-exclusive game state — a title screen, the overworld, a battle.", + "layer": "A named group of handlers inside a scene; layers render in declaration order.", + "on": "Scene lifecycle hooks that fire as a scene becomes active or is left.", + "enter": "The scene-entry hook — runs once as a scene becomes active.", + "exit": "Terminate the process immediately with a status code.", + "start": "Marks the one scene the game begins in.", + "event": "Declare a public event carrying a flat payload that listeners react to.", + "emit": "Fire an event, running every listener; as an expression it yields the veto flag.", + "cancel": "Inside a listener, veto the cancellable event being emitted.", + "cancellable": "Marks an event whose listeners may veto it with cancel.", + "Screen.clear": "Fill the whole framebuffer with one color to start a fresh frame.", + "Screen.fill_rectangle": "Draw a solid, filled rectangle at a pixel position.", + "Screen.draw_rectangle": "Draw a one-pixel-thick rectangle outline (not filled).", + "Screen.put_pixel": "Set a single pixel at a pixel coordinate to one color.", + "Screen.draw_text": "Draw a string with the built-in font at an integer scale.", + "Screen.draw_number": "Draw an integer directly, with no string allocation or conversion.", + "Screen.show": "Present the finished frame — copy everything drawn this frame to the window.", + "Screen.width": "The framebuffer width in pixels.", + "Screen.height": "The framebuffer height in pixels.", + "Screen.status": "Set the persistent one-line status/HUD string shown by the runtime.", + "Screen.line": "Draw a straight line between two points.", + "Screen.circle": "Draw a circle outline.", + "Screen.fill_circle": "Draw a filled disc.", + "Screen.triangle": "Draw a triangle outline.", + "Screen.fill_triangle": "Draw a filled triangle.", + "Screen.sprite": "Blit a sprite at its natural size.", + "Screen.sprite_scaled": "Blit a sprite at an integer scale.", + "Screen.oval": "Draw an axis-aligned ellipse outline.", + "Screen.camera": "Set the world-space camera offset for the draw path.", + "Screen.clip": "Restrict drawing to a screen-space rectangle.", + "Screen.clip_reset": "Lift the clip rectangle (draw to the whole screen again).", + "Screen.blend_mode": "Choose replace or additive pixel blending.", + "Screen.measure_text": "The pixel advance width of text in the built-in font.", + "Screen.pixel": "Read a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.", + "Screen.bar": "A filled meter: value of max over a track.", + "Clock.now": "The current simulated instant.", + "Clock.set": "Set the clock to an instant.", + "Clock.advance": "Move the clock forward by a Duration.", + "Clock.reset": "Reset the clock to the epoch (0).", + "Collision.rects": "Do two rectangles overlap?", + "Collision.point_rect": "Is a point inside a rectangle?", + "Collision.circles": "Do two circles overlap?", + "Collision.rect_circle": "Does a rectangle overlap a circle?", + "Color.rgb": "Build a color from red, green, blue.", + "Color.rgba": "Build a color with an alpha byte.", + "Color.lerp": "Blend between two colors.", + "Color.darken": "Scale a color toward black.", + "Color.lighten": "Scale a color toward white.", + "Color.with_alpha": "Replace a color's alpha byte.", + "Crypto.sha256": "SHA-256 of a string, as 64 hex characters.", + "Crypto.hmac_sha256": "Sign a message with a shared secret key.", + "Crypto.verify_hmac": "Constant-time check that a MAC matches.", + "Crypto.hex": "Lowercase hex of a string's bytes.", + "Crypto.ct_equal": "Constant-time string equality for secrets.", + "Crypto.random_bytes": "n bytes from the OS CSPRNG, as a 2n-character hex string.", + "Crypto.random_hex": "Alias for random_bytes — n secure bytes as a 2n-char hex string.", + "Crypto.random_u32": "One CSPRNG-drawn 32-bit integer.", + "Crypto.base64": "Standard base64 (RFC 4648) of a string's bytes.", + "Date.new": "A calendar day as days-since-1970.", + "Date.year": "The calendar year of a date.", + "Date.month": "The month (1-12) of a date.", + "Date.day": "The day of the month (1-31) of a date.", + "Date.weekday": "Day of the week, 0=Sunday..6=Saturday.", + "Date.is_leap": "True if the year is a leap year.", + "Date.days_in_month": "Number of days in a given month.", + "Date.to_epoch": "Midnight UTC of the day, as a DateTime.", + "Date.add_days": "The date n days later (n may be negative).", + "Date.diff_days": "Whole days from b to a (a - b).", + "DateTime.from": "Build an instant from its calendar parts.", + "DateTime.date": "The calendar day the instant falls on.", + "DateTime.add": "The instant `span` seconds later.", + "DateTime.year": "The calendar year of an instant.", + "DateTime.month": "The month (1-12) of an instant.", + "DateTime.day": "The day of the month (1-31) of an instant.", + "DateTime.weekday": "Day of the week, 0=Sunday..6=Saturday.", + "DateTime.hour": "The hour of day (0-23) of an instant.", + "DateTime.minute": "The minute of the hour (0-59) of an instant.", + "DateTime.second": "The second of the minute (0-59) of an instant.", + "DateTime.format": "Render an instant as text using a token pattern.", + "DateTime.parse": "Parse text into an instant; -1 on failure.", + "Duration.seconds": "A span of n whole seconds.", + "Duration.minutes": "A span of n minutes, in seconds.", + "Duration.hours": "A span of n hours, in seconds.", + "Duration.days": "A span of n days, in seconds.", + "Duration.as_seconds": "The span in whole seconds.", + "Duration.as_minutes": "The span in whole minutes.", + "Duration.as_hours": "The span in whole hours.", + "Duration.as_days": "The span in whole days.", + "Ease.in": "Accelerate from rest (quadratic ease-in).", + "Ease.out": "Decelerate to rest (quadratic ease-out).", + "Ease.in_out": "Ease in and then out (smoothstep).", + "Ease.back": "Ease in with a small backward anticipation.", + "Ease.bounce": "Ease out with a settling bounce.", + "Ease.elastic": "Ease out with a springy wobble.", + "Fs.exists": "Does a path exist?", + "Fs.is_dir": "Is a path a directory?", + "Fs.read_text": "Read a whole file as a string.", + "Fs.write_text": "Write a string to a file (atomically).", + "Fs.append_text": "Append a string to a file.", + "Fs.remove": "Delete a file.", + "Fs.size": "File size in bytes.", + "Fs.mkdir": "Create a directory and its parents.", + "Fs.copy": "Copy a file byte-for-byte.", + "Fs.list": "Directory entries, sorted.", + "Hash.of": "The fast default string hash.", + "Hash.fnv1a": "FNV-1a 32-bit, named explicitly.", + "Hash.crc32": "CRC-32 checksum for corruption detection.", + "Hash.mix": "Avalanche one integer into a well-scrambled value.", + "Hash.combine": "Fold several ints into one deterministic seed.", + "Hash.of64": "The fast default 64-bit string hash.", + "Hash.fnv1a_64": "FNV-1a 64-bit, named explicitly.", + "Hash.mix64": "Avalanche one 64-bit integer into a well-scrambled value.", + "Input.key": "The key pressed this frame as a character code (0 when nothing is pressed).", + "Input.bind": "Bind a physical key to a named action, so gameplay reads the action, not the key.", + "Input.rebind": "Remap an action from one key to another at runtime (rebinding menus).", + "Input.poll": "Advance one frame of input; the single per-frame read behind actions and replay.", + "Input.down": "Is a named action held on the frame last polled?", + "Input.pressed": "Did a named action go down this frame (a one-shot edge)?", + "Input.record": "Start recording polled input each frame (for deterministic replay).", + "Input.replay": "Replay recorded input; poll then reads the tape, not the device.", + "Input.key_down": "Is a physical key held this frame?", + "Input.key_pressed": "Did a key go down this frame (edge)?", + "Input.key_released": "Did a key go up this frame (edge)?", + "Input.key_label": "The name to show a player for a key - in their own keyboard layout.", + "Input.press": "Inject a held key (AI, tutorial, testing, network).", + "Input.release": "Release an injected key.", + "Input.axis": "A -1..+1 axis from two keys.", + "Input.axis_i": "A -1/0/1 directional intent as a plain int, no bool->int glue.", + "Input.vector": "A normalized 2D vector from four keys.", + "Input.strength": "0..1 strength of a named action.", + "Input.mouse_x": "The mouse x position this frame.", + "Input.mouse_y": "The mouse y position this frame.", + "Input.mouse_dx": "Mouse x movement since the last poll.", + "Input.mouse_dy": "Mouse y movement since the last poll.", + "Input.mouse_down": "Is a mouse button held?", + "Input.wheel": "Scroll-wheel delta this frame.", + "Input.set_mouse": "Inject the mouse state (headless / AI / testing).", + "Input.pad_connected": "Is a gamepad connected?", + "Input.pad_button": "Is a gamepad button held?", + "Input.pad_axis": "A gamepad analog axis, -1..+1.", + "Input.set_pad": "Inject a gamepad's whole state.", + "Input.touch_count": "How many touch points are active?", + "Input.touch_x": "The x of a touch point.", + "Input.touch_y": "The y of a touch point.", + "Input.set_touch": "Inject a touch point.", + "Input.action": "Register a default key binding for an action (kept if already bound).", + "Input.bind_pad": "Also fire a named action from a gamepad button (device-agnostic).", + "Input.active": "Is a named action active right now (any bound key held, or pad button down)?", + "Input.just_pressed": "Did a named action go active this frame (the deterministic on-press)?", + "Input.just_released": "Did a named action go inactive this frame (the on-release edge)?", + "Input.cursor_mode": "Hide / lock / confine the OS mouse cursor to the window (windowed).", + "Input.move_i": "The standard top-down movement intent, -1/0/1 per axis.", + "List.len": "The number of elements in a slice.", + "List.push": "Append an element to the end of a slice.", + "List.clear": "Remove all elements, keeping capacity.", + "List.first": "The first element of a slice.", + "List.last": "The last element of a slice.", + "List.pop": "Remove and return the last element.", + "List.swap": "Exchange the elements at two indices.", + "List.contains": "Whether a value appears in a slice.", + "List.index_of": "The index of a value in a slice, or -1 if absent.", + "List.reverse": "Reverse the order of a slice in place.", + "List.insert": "Insert an element at an index, shifting the rest up.", + "List.remove_at": "Remove the element at an index, shifting the rest down.", + "List.remove": "Remove the first element equal to a value.", + "List.sort": "Sort a slice in ascending order, in place.", + "List.sort_by": "Sort a slice ascending by a key each element maps to.", + "List.sort_desc_by": "Sort a slice descending by a key each element maps to.", + "List.sort_with": "Sort a slice with a full two-argument comparator.", + "List.sample": "Random picks from an int slice, distinct while it can.", + "Log.trace": "Log at the trace level (0) — the most verbose.", + "Log.debug": "Log at the debug level (1) — development detail.", + "Log.info": "Log at the info level (2) — normal operation.", + "Log.warn": "Log at the warn level (3) — something looks wrong.", + "Log.error": "Log at the error level (4) — a failure.", + "Log.set_level": "Show only messages at level n or above (0 = all).", + "Log.level": "The current logging threshold.", + "Math.min": "The smaller of two values.", + "Math.max": "The larger of two values.", + "Math.abs": "The magnitude of a value, dropping its sign.", + "Math.clamp": "Constrain a value to the range [lo, hi].", + "Math.sign": "The sign of a value as -1, 0, or 1.", + "Math.floor": "Round a fixed value down to the nearest whole int.", + "Math.ceil": "Round a fixed value up to the nearest whole int.", + "Math.round": "Round a fixed value to the nearest whole int.", + "Math.lerp": "Blend between two values by a 0..1 amount.", + "Math.inverse_lerp": "Find where a value sits between two endpoints as a 0..1 fraction.", + "Math.remap": "Rescale a value from one range into another.", + "Math.sqrt": "The square root of a fixed value.", + "Math.sin": "Sine of an angle in radians.", + "Math.cos": "Cosine of an angle in radians.", + "Math.tan": "Tangent of an angle in radians.", + "Math.hypot": "Length of the vector (x, y).", + "Math.dist": "Distance between two points.", + "Math.dist2": "Squared distance between two points.", + "Math.deg_to_rad": "Convert degrees to radians.", + "Math.rad_to_deg": "Convert radians to degrees.", + "Math.posmod": "Modulo that is always non-negative.", + "Math.wrap": "Wrap a value into the range [lo, hi).", + "Math.ping_pong": "Bounce a counter back and forth in [0, len].", + "Math.snapped": "Round a value to the nearest multiple of step.", + "Math.move_toward": "Step from one value toward another by at most delta.", + "Math.smoothstep": "A smooth 0..1 ramp between two edges.", + "Math.atan2": "The angle of the vector (x, y), in radians.", + "Math.asin": "The arcsine of a value, in radians.", + "Math.acos": "The arccosine of a value, in radians.", + "Math.exp": "e raised to the power x.", + "Math.log": "The natural logarithm of x.", + "Math.pow": "base raised to a fixed exponent.", + "Memory.bytes": "Allocate n bytes.", + "Memory.words": "Allocate n 32-bit words.", + "Memory.copy": "Copy n bytes between buffers.", + "Memory.fill": "Set n bytes to a value.", + "Memory.peek": "Read one byte.", + "Memory.poke": "Write one byte.", + "Mime.of": "MIME type from a file extension.", + "Mime.sniff": "MIME type refined by magic bytes.", + "Network.send": "Put a datagram on the wire.", + "Network.poll": "Read an inbound datagram.", + "Network.serialize": "Serialize an entity's synced state.", + "Network.apply": "Apply serialized state to an entity.", + "Network.owner": "The peer that owns an entity.", + "Network.set_owner": "Assign ownership of an entity.", + "Network.is_server": "Is this peer the server?", + "Network.is_owner": "Does this peer own the entity?", + "Network.local_id": "This peer's own id.", + "Noise.value2": "2D value (lattice) noise, deterministic, in [-1, 1].", + "Noise.perlin2": "2D Perlin gradient noise, deterministic, in [-1, 1].", + "Noise.simplex2": "2D simplex noise, the organic default, in [-1, 1].", + "Noise.fbm2": "Fractal Brownian motion — octaves of simplex, in [-1, 1].", + "Noise.cellular2": "Worley (cellular) F1 distance to the nearest cell point.", + "Noise.cellular2_id": "The id of the nearest Worley cell — stable per cell.", + "Noise.unit": "Remap a [-1,1] noise sample to [0,1].", + "Os.args": "Every command-line argument, as a list.", + "Os.arg_count": "How many command-line arguments there are.", + "Os.arg": "The i-th command-line argument.", + "Os.env": "Read an environment variable (null if unset).", + "Os.env_or": "Read an environment variable, or a fallback.", + "Os.has_env": "Is an environment variable set?", + "Os.set_env": "Set an environment variable.", + "Os.unset_env": "Remove an environment variable.", + "Os.exit": "Terminate the process with a status code.", + "Os.platform": "The host platform id.", + "Os.arch": "The host CPU architecture.", + "Os.stdout_write": "Write a string to standard output.", + "Os.stderr_write": "Write a string to standard error.", + "Os.save_dir": "Per-user save directory for an app.", + "Os.config_dir": "Per-user config directory for an app.", + "Os.cache_dir": "Per-user cache directory for an app.", + "Os.temp_dir": "The system temporary directory.", + "Os.pid": "This process's id.", + "Path.join": "Join two path segments with the separator.", + "Path.dir": "The directory part of a path.", + "Path.base": "The final component of a path.", + "Path.ext": "The file extension, including the dot.", + "Path.stem": "The final component without its extension.", + "Path.normalize": "Collapse . / .. / duplicate separators.", + "Save.write": "Save a snapshot of the world.", + "Save.read": "Restore the saved snapshot.", + "System.run": "Run a shell command.", + "System.read_char": "Read one byte from standard input.", + "System.file_open": "Open a file.", + "System.file_read": "Read bytes from a file.", + "System.file_write": "Write bytes to a file.", + "System.file_seek": "Move a file's read/write position.", + "System.file_tell": "The current file position.", + "System.file_close": "Close a file.", + "Text.length": "The number of bytes in a string.", + "Text.char_at": "The byte value at an index, 0..255.", + "Text.slice": "A substring covering bytes [a, b).", + "Text.starts_with": "Whether a string begins with a given prefix.", + "Text.ends_with": "Whether a string ends with a given suffix.", + "Text.contains": "Whether a substring appears anywhere in a string.", + "Text.index_of": "The byte index of a substring, or -1 if absent.", + "Text.equals": "Whether two strings have identical bytes.", + "Text.concat": "Join two strings into a new one.", + "Text.to_int": "Parse a leading integer from a string.", + "Text.from_int": "Render an integer as a string.", + "Text.upper": "An uppercased copy of a string.", + "Text.lower": "A lowercased copy of a string.", + "Text.trim": "A copy with surrounding whitespace removed.", + "Text.repeat": "A string repeated n times.", + "Text.pad_left": "Pad with spaces on the left to a width.", + "Text.pad_right": "Pad with spaces on the right to a width.", + "Text.split": "Split a string on a separator into a list.", + "Text.join": "Join a list of strings with a separator.", + "Text.replace": "Replace every occurrence of a substring.", + "Time.frame": "The number of frames elapsed since start.", + "Time.delta": "Seconds per frame (a fixed 1/60).", + "Time.elapsed": "Seconds since start (deterministic).", + "Time.now": "Wall-clock seconds (non-deterministic).", + "Time.since": "Seconds elapsed from a past instant to now (non-deterministic).", + "Unicode.len": "Number of code points in a string (not bytes).", + "Unicode.byte_len": "Number of bytes in a string.", + "Unicode.is_valid_utf8": "Is a string well-formed UTF-8?", + "Unicode.char_at": "The i-th code point of a string.", + "Unicode.chars": "Every code point of a string, in order.", + "Unicode.upper": "Uppercase a string.", + "Unicode.lower": "Lowercase a string.", + "Unicode.truncate": "First n code points of a string.", + "Unicode.grapheme_len": "Number of user-perceived characters.", + "Uuid.new": "A new random (v4) UUID as a canonical 36-char string.", + "Uuid.v4": "Explicit alias for Uuid.new — a random (v4) UUID.", + "Uuid.new_v7": "A new time-ordered (v7) UUID; sorts by creation time.", + "Uuid.v7": "Explicit alias for Uuid.new_v7 — a time-ordered UUID.", + "Uuid.parse": "Normalise an untrusted string to a lowercase UUID, or the nil UUID.", + "Uuid.is_valid": "Is s a well-formed UUID string?", + "Uuid.to_text": "The canonical text form of a UUID.", + "Uuid.equals": "Case-insensitive UUID equality.", + "Uuid.nil": "The all-zero UUID.", + "World.get": "Read a component field by id.", + "World.set": "Write a component field by id.", + "World.has": "Does an entity have a component?", + "World.count": "The number of live entities.", + "World.size": "The serialized size of the world.", + "World.spawn": "Spawn an entity of a model by id.", + "World.save": "Serialize the world into a buffer.", + "World.load": "Restore the world from a buffer.", + "World.prop_id": "Look up a component's id by name.", + "World.field_id": "Look up a field's id by name.", + "World.model_id": "Look up a model's id by name.", + "World.kind": "The model id of an entity.", + "World.register_prop": "Register a new component at runtime.", + "World.attach": "Attach a component to an entity at runtime.", + "World.detach": "Detach a component from an entity at runtime.", + "World.query_next": "Advance a reflective query.", + "World.despawn": "Despawn an entity by id — runs its @OnDespawn hooks and frees the slot.", + "Audio.load": "Load a sound file into a handle.", + "Audio.play": "Fire a one-shot sound.", + "Audio.play_at": "Fire a one-shot with its own gain, pitch and stereo position.", + "Audio.play_sound": "Fire a one-shot sound (alias of play).", + "Audio.play_music": "Loop a sound as background music.", + "Audio.stop": "Stop one sound.", + "Audio.stop_music": "Stop the music channel.", + "Audio.stop_all": "Stop every sound.", + "Audio.volume": "Set the master volume.", + "Audio.pitch": "Set the playback rate / pitch.", + "Audio.is_playing": "Is this sound playing?", + "Audio.define": "Load a sound into the bank under a name.", + "Audio.named": "The handle registered under a name, or 0.", + "Random.range": "A random integer in the inclusive range [low, high].", + "Random.chance": "Return true with the given percent probability.", + "Random.seed": "Seed the random generator so runs are reproducible.", + "Random.value": "A random fixed value in [0, 1).", + "Random.int": "A random integer in [0, max).", + "Random.sign": "A random +1 or -1.", + "Random.weighted": "An index drawn in proportion to its weight.", + "Vector.make": "Build a vector from x and y components.", + "Vector.zero": "The zero vector, (0, 0).", + "Vector.x": "The x component of a vector.", + "Vector.y": "The y component of a vector.", + "Vector.add": "Component-wise sum a + b.", + "Vector.sub": "Component-wise difference a - b.", + "Vector.scale": "Scale a vector by a scalar.", + "Vector.dot": "The dot product of two vectors.", + "Vector.length": "The length (magnitude) of a vector.", + "Vector.distance": "The distance between two points.", + "Vector.normalize": "A unit vector in the same direction.", + "Vector.lerp": "Linear interpolation between two vectors.", + "Vector.rotate": "Rotate a vector by an angle in radians.", + "Vector.angle": "The angle of a vector in radians.", + "Vector.from_angle": "A unit vector pointing at an angle.", + "Http.get": "Start an async GET request.", + "Http.post": "Start an async POST with a body.", + "Http.request": "Start a request with any method.", + "Http.open": "Open a request to configure before sending.", + "Http.set": "Set a request header before sending.", + "Http.body": "Set a string request body.", + "Http.body_bytes": "Set a raw byte request body.", + "Http.send": "Dispatch an opened request.", + "Http.poll": "Poll a request; -1 pending, 0 error, else status.", + "Http.status": "The response HTTP status code.", + "Http.ok": "Was the response status 2xx?", + "Http.text": "The response body as text.", + "Http.body_len": "The response body length in bytes.", + "Http.header": "A response header value (case-insensitive).", + "Http.free": "Release a request's resources.", + "Http.parse": "Parse a raw HTTP response into a handle.", + "Http.save_to": "Stream the response body to a file.", + "Http.received": "Body bytes received so far.", + "Http.expected": "The body length the server announced, or -1.", + "IVec2.make": "Build an integer vector from x and y components.", + "IVec2.zero": "The origin cell, (0, 0).", + "IVec2.x": "The x component of an integer vector.", + "IVec2.y": "The y component of an integer vector.", + "IVec2.add": "Component-wise sum of two integer vectors.", + "IVec2.sub": "Component-wise difference of two integer vectors.", + "IVec2.scale": "Multiply both components by an integer.", + "IVec2.dot": "The dot product ax*bx + ay*by.", + "IVec2.equal": "True when both components match.", + "IVec2.manhattan": "Grid distance |dx| + |dy|.", + "IVec2.to_vector": "Widen to a fixed-point Vector.", + "IVec2.distance2": "The squared distance between two points.", + "IVec2.within": "Are two points within a radius of each other?", + "IVec2.heading": "The direction from one point to another, in degrees.", + "IVec2.along": "The point a distance along a heading.", + "IVec2.step": "A -1/0/1 unit step along a heading.", + "Map.size": "Set the tilemap dimensions in cells before filling rows.", + "Map.row": "Fill one row of the tilemap from a string of tile characters.", + "Map.tile": "Read the tile character at a cell (out-of-bounds reads answer '#').", + "Map.get": "The glyph at a cell ('#' outside the map).", + "Map.set": "Write one cell in place.", + "Map.fill": "Every cell becomes the glyph.", + "Map.rect": "Fill a rectangle of cells.", + "Map.border": "The outermost ring of cells.", + "Map.random_cell": "A random cell holding the glyph (seeded RNG).", + "Map.is_solid": "Is a cell solid, per the Solids config?", + "Map.is_solid_at": "Is the cell under a pixel position solid?", + "Map.width": "The map's width in cells.", + "Map.height": "The map's height in cells.", + "Map.random_cell_far": "A random cell with the glyph, at least a distance from a point.", + "Map.to_tile": "The tile under a pixel position.", + "Rect.make": "Build a rectangle from a corner and a size.", + "Rect.x": "The left edge (x position).", + "Rect.y": "The top edge (y position).", + "Rect.w": "The width.", + "Rect.h": "The height.", + "Rect.right": "The right edge, x + w.", + "Rect.bottom": "The bottom edge, y + h.", + "Rect.center": "The center point as a Vector.", + "Rect.contains": "True when the point is inside.", + "Rect.intersects": "True when two rectangles overlap.", + "Udp.open": "Open a socket on a port.", + "Udp.port": "The port a socket is bound to.", + "Udp.send": "Send one datagram.", + "Udp.recv": "Read the next waiting datagram.", + "Udp.from_ip": "The sender of the last datagram read.", + "Udp.from_port": "The sender's port.", + "Udp.close": "Close a socket.", + "Udp.resolve": "Look a host name up.", + "Udp.local_ip": "This machine's address.", + "Udp.ip": "Parse a dotted address.", + "Udp.ip_text": "Format an address.", + "BigInt.from": "Turn a plain int into a BigInt.", + "BigInt.parse": "Parse decimal text into a BigInt.", + "BigInt.add": "Exact sum of two big integers.", + "BigInt.sub": "Exact difference of two big integers.", + "BigInt.mul": "Exact product of two big integers.", + "BigInt.neg": "Negate a big integer.", + "BigInt.pow": "Raise a big integer to an int power.", + "BigInt.div": "Divide a big integer by an int (toward zero).", + "BigInt.mod": "Remainder of a big integer divided by an int.", + "BigInt.cmp": "Compare two big integers: -1, 0, or 1.", + "BigInt.eq": "True when two big integers are equal.", + "BigInt.is_zero": "True when a big integer is zero.", + "BigInt.to_int": "Narrow a big integer to a plain int (clamped).", + "BigInt.str": "Render a big integer as decimal text.", + "Process.spawn": "Start a program; never blocks.", + "Process.poll": "-1 while running, else the exit code.", + "Process.kill": "End a running child.", + "Process.free": "Let a process handle go.", + "Decimal.from": "Turn a whole int into a Decimal.", + "Decimal.parse": "Parse decimal text like \"19.99\".", + "Decimal.add": "Exact sum of two decimals.", + "Decimal.sub": "Exact difference of two decimals.", + "Decimal.mul": "Exact product of two decimals.", + "Decimal.neg": "Negate a decimal.", + "Decimal.cmp": "Compare two decimals: -1, 0, or 1.", + "Decimal.eq": "True when two decimals are equal in value.", + "Decimal.scale": "The number of digits after the point.", + "Decimal.rescale": "Change precision (truncates toward zero).", + "Decimal.str": "Render a decimal as text.", + "int": "Convert a number to a whole number, truncating toward zero.", + "fixed": "Convert an int (or a float) into a Q16.16 fixed-point value.", + "long": "Convert a number to a whole number, truncating toward zero.", + "bool": "A truth value — the result of comparisons and of and / or / not.", + "float": "Convert any number to a float (or double).", + "string": "Convert an int, bool, or fixed value to text; a string passes through unchanged.", + "double": "Convert any number to a float (or double).", + "entity": "The id/handle of a spawned model instance, as returned by self().", + "pointer": "A raw address into memory — a byte buffer from bytes(n), or an FFI handle.", + "words": "Allocate a raw buffer of n 32-bit words, indexable with [i].", + "byte": "A raw buffer indexed one byte at a time — each buffer[i] reads or writes a byte.", + "fixeds": "A buffer of fixed-point values — f[i] reads and writes a fixed.", + "countdown": "An int component field the engine steps toward 0 once per Update.", + "floats": "A buffer of floats (or doubles) — v[i] reads and writes one.", + "doubles": "A buffer of floats (or doubles) — v[i] reads and writes one.", + "pointers": "A buffer of pointers — p[i] reads and writes a pointer.", + "Vector": "A 2D vector — two fixed components (x, y), copied by value.", + "IVec2": "An integer 2D vector — two int components (x, y), copied by value.", + "Rect": "A rectangle — position (x, y) and size (w, h), copied by value.", + "void": "The absence of a value — the return type of a function that returns nothing.", + "Dict.new": "Create an empty string-keyed map.", + "Dict.set": "Insert or update a key.", + "Dict.get": "Look up a key (0 if absent).", + "Dict.get_or": "Look up a key with an explicit default.", + "Dict.has": "Is a key present?", + "Dict.remove": "Delete a key.", + "Dict.size": "How many keys are stored.", + "Dict.clear": "Remove every key.", + "Dict.keys": "Every key, as a slice of strings.", + "and": "The boolean combinators, spelled as words — never && or || or a bare !.", + "or": "The boolean combinators, spelled as words — never && or || or a bare !.", + "not": "The boolean combinators, spelled as words — never && or || or a bare !.", + "print": "Print an int or string followed by a newline — for headless tests and debugging.", + "quit": "Stop the game loop cleanly after the current frame finishes.", + "save": "Serialize the whole world — every model instance, property, and program var — in one call.", + "load": "Restore the world from a snapshot previously written by save().", + "len": "The number of elements in a slice, or the byte length of a string.", + "push": "Append one element to the end of a growable slice.", + "abs": "The absolute value of an integer (its magnitude, never negative).", + "clamp": "Constrain a value to the inclusive range [lo, hi].", + "panic": "Abort with a located error message instead of crashing.", + "assert": "Abort with a located message when an invariant is false.", + "ok": "Wrap a success payload in a result — the happy half of try/else.", + "err": "Wrap a failure message in a result — the sad half, recovered by try/else.", + "is_ok": "True when a result carries a success payload.", + "is_err": "True when a result carries a failure.", + "some": "Wrap a present value in an option.", + "none": "The empty option — a missing value.", + "is_some": "True when an option holds a value.", + "is_none": "True when an option is empty.", + "unwrap_or": "The option value, or a fallback if empty.", + "run": "Run a string as a shell command.", + "getenv": "Read an environment variable, returning empty when it is unset.", + "read_char": "Read one byte from standard input, or -1 at end of input.", + "arg": "The i-th command-line argument as a string.", + "arg_count": "The number of command-line arguments, counting the program name.", + "bytes": "Allocate a raw buffer of n bytes and return a pointer to it.", + "file_stderr": "The standard-error stream handle for use with file_write.", + "file_stdout": "The standard-output stream handle for use with file_write.", + "file_write": "Write a run of raw bytes to a stream handle.", + "floor": "Floor a fixed-point value down to the nearest integer.", + "max": "The larger of two integers.", + "min": "The smaller of two integers.", + "ui_build": "Build the declared UI tree so it can be opened and rendered.", + "float_bits": "A float's IEEE bit pattern as an int, and back.", + "float_from_bits": "A float's IEEE bit pattern as an int, and back.", + "double_bits": "A float's IEEE bit pattern as an int, and back.", + "double_from_bits": "A float's IEEE bit pattern as an int, and back.", + "Set.new": "Create an empty string set.", + "Set.add": "Add a member (duplicates ignored).", + "Set.has": "Is a member present?", + "Set.remove": "Remove a member.", + "Set.size": "How many members.", + "Set.clear": "Remove every member.", + "Set.members": "Every member, as a slice of strings.", + "@Queries": "Declare the properties a handler operates on, binding their fields by name in the body.", + "@Computed": "A derived field expanded inline wherever it is read, never stored.", + "@On": "Register a compile-time listener that runs whenever an event is emitted.", + "@export": "Expose a function as a native symbol so a host can call it.", + "@Sync": "Mark fields as replicated and models as owned so the compiler generates networking.", + "@Handles": "Declarative hint naming the event or subsystem a handler is responsible for.", + "@OnAttach": "Run a handler when a property is structurally attached to a live instance.", + "@OnDespawn": "Run a handler when a model instance is torn down, optionally knowing why.", + "@OnDetach": "Run a handler when a property is structurally detached from a live instance.", + "@OnDisable": "Run a handler when a property is paused (disabled) on an instance.", + "@OnEnable": "Run a handler when a paused property is re-enabled on an instance.", + "@OnQuit": "Run a handler once at shutdown, after the last frame.", + "@OnSpawn": "Run a handler once each time an instance of a model is spawned.", + "@OnStart": "Run a handler once at boot instead of assigning it a frame phase.", + "@Owned": "Give a model a network owner slot so its instances can be assigned to a peer.", + "@Predicted": "Run a control handler on the owning client speculatively and on the server authoritatively.", + "@Public": "Promote a lifecycle hook to a public event other modules can listen for.", + "@Reads": "Declare that a handler reads a property — an analysis and scheduling hint.", + "@Server": "Run a handler only on the authority; clients receive the result via @Sync.", + "@ToClients": "A remote event broadcast from the server to clients — a server→clients notification.", + "@ToServer": "A remote event sent from a client to the server — a client→server request.", + "@Writes": "Declare that a handler writes a property — an analysis and scheduling hint.", + "@EngineSystem": "Register a package's engine-owned system so the frame loop runs it each phase.", + "@Namespace": "Let a package provide a Name.method(…) namespace that dispatches to name_method.", + "\"@ClearColor\"": "Declare a clear colour so the Render phase auto-clears + auto-presents for you.", + "@System": "Register a prebuilt binary module's system with the host at load, per phase.", + "Huge.from": "Turn an int into a Huge.", + "Huge.add": "Sum of two big numbers.", + "Huge.sub": "Difference of two big numbers.", + "Huge.mul": "Product of two big numbers.", + "Huge.neg": "Negate a big number.", + "Huge.cmp": "Compare two big numbers: -1, 0, or 1.", + "Huge.sign": "The sign: -1, 0, or 1.", + "Huge.mantissa": "The mantissa in [1, 10).", + "Huge.exp": "The base-10 exponent.", + "Huge.str": "Render as scientific text like 1.23e45.", + "Angle.from_degrees": "Degrees to a wrapped radian angle.", + "Angle.to_degrees": "Radians back to degrees.", + "Angle.wrap": "Normalize any radian value to [-pi, pi).", + "Angle.sin": "Sine of an angle.", + "Angle.cos": "Cosine of an angle.", + "Angle.add": "Add two angles (wrapped).", + "Angle.diff": "Shortest signed rotation from a to b.", + "Angle.lerp": "Interpolate along the shortest arc.", + "Angle.diff_degrees": "The signed difference between two headings in whole degrees.", + "Percent.clamp": "Clamp any fixed into [0, 1].", + "Percent.of": "Clamped ratio num / den.", + "Percent.lerp": "Interpolate a..b by a clamped t.", + "Percent.apply": "Scale a value by a clamped percent.", + "Regex.compile": "Compile a pattern once for reuse; returns an error value (null) if it is invalid.", + "Regex.valid": "True if the pattern is well-formed (compiles without error).", + "Regex.matches": "True if the pattern matches anywhere in the text (a search).", + "Regex.test": "Like matches, but reuses an already-compiled Regex.", + "Regex.find": "The first match of the pattern in the text (a Match, or null if none).", + "Regex.exec": "Like find, but with an already-compiled Regex.", + "Regex.next": "The next match at or after byte offset from — the basis of a find-all loop.", + "Regex.replace": "Replace every match; the replacement expands \\0..\\9 group references.", + "Regex.group": "The text of capture group n (group 0 is the whole match); empty if unset.", + "Regex.group_count": "How many capture groups the match's pattern has.", + "Regex.start": "The start byte offset of group n, or -1 if it did not participate.", + "Regex.end": "The end byte offset of group n, or -1 if it did not participate.", + "Regex.ok": "True if the match succeeded (the Match is non-null).", + "Grid.line": "Every cell a straight line from (x0,y0) to (x1,y1) crosses (Bresenham).", + "Grid.blocked": "True if the cell is out of bounds or holds the wall tile.", + "Grid.line_of_sight": "True if the straight line between two cells crosses no wall.", + "Grid.flood": "Every passable cell reachable from (x,y), 4-connected, in BFS order.", + "Grid.a_star": "The shortest 4-connected path between two cells (A*), or an empty list.", + "Anim.frame": "The looping frame index for an elapsed timer at a given fps.", + "Anim.once": "A non-looping frame index that clamps on the last frame.", + "Anim.pingpong": "A frame index that bounces 0..count-1..0 and repeats.", + "Anim.finished": "True once a one-shot clip has run past its last frame.", + "Anim.duration": "Seconds for one full cycle of a clip: count / fps.", + "Anim.cell_x": "The source x (pixels) of a frame on a grid spritesheet.", + "Anim.cell_y": "The source y (pixels) of a frame on a grid spritesheet.", + "Anim.play": "Start (or restart) a spritesheet clip on an entity in one call.", + "Anim.clip": "Register a named spritesheet clip so Anim.play can play it by name.", + "Anim.on_frame": "Arm a frame event — the engine flags the tick a clip lands on this frame.", + "Anim.fired": "Did the entity's clip land on its armed frame event this tick?", + "Motion.to": "Start a value tween on an entity's Motion component in one call.", + "Tween.progress": "A one-shot 0..1 amount, clamped, for timer over duration.", + "Tween.loop": "A repeating 0..1 sawtooth amount over duration.", + "Tween.yoyo": "A repeating 0..1..0 triangle amount over duration.", + "Tween.done": "True once a one-shot tween's timer reaches its duration.", + "Tween.ease": "Shape a 0..1 amount through an easing curve chosen by a literal mode.", + "Tween.number": "Linear blend of two fixeds by amount t.", + "Tween.round": "Linear blend of two ints by amount t, rounded to the nearest int.", + "Tween.point": "Component-wise blend of two Vectors by amount t.", + "Tween.tint": "Per-channel blend of two colors by amount t.", + "Tween.to": "Start a fluent, engine-advanced tween and return a handle.", + "Tween.chain": "Append a tween segment that runs after the handle's current queue.", + "Tween.delay": "Append a pause to a tween handle's sequence.", + "Tween.value": "The current value of a fluent tween handle this frame.", + "Tween.stop": "Stop and dispose a tween handle, freezing its value.", + "Tween.parallel": "Are two tween handles both finished? — a parallel completion query.", + "Query.count": "How many live entities carry a property.", + "Query.first": "The lowest-id live entity carrying a property, or -1.", + "Query.nearest": "The bearer of prop closest to a point, by squared distance.", + "Query.within": "Every bearer of prop inside a radius of a point.", + "Reflect.prop": "The property id for a property name, or -1 if unknown.", + "Reflect.field": "The field id of a named field within a property, or -1.", + "Reflect.prop_count": "How many properties the world schema defines.", + "Reflect.prop_name": "The name of the property at an index (or \"\").", + "Reflect.field_count": "How many fields a property has.", + "Reflect.field_name": "The name of the field at an index within a property.", + "Reflect.field_type": "The type name of the field at an index within a property.", + "Reflect.get": "Read one field of an entity by (prop, field) id.", + "Reflect.set": "Write one field of an entity by (prop, field) id.", + "Reflect.has": "Does an entity carry a property?", + "Reflect.kind": "The model id of an entity.", + "Reflect.model": "The model id for a model name, or -1 if unknown.", + "Reflect.serialize": "Walk an entity's components into a value tree.", + "Reflect.apply": "Write a value tree's fields back into an entity.", + "Camera.set": "Place the camera at a world-space offset.", + "Camera.follow": "Ease the camera toward centring a target point.", + "Camera.shake": "Jitter the camera by up to +/- amount pixels (seeded RNG).", + "Camera.zoom": "Scale the whole view about the screen centre by a Q16.16 factor (1.0 = none).", + "Camera.shake_for": "Shake for a number of frames, then stop, with no bookkeeping.", + "Light.ambient": "Multiply the whole scene by a tint — the night/cave modulate.", + "Light.point": "Add a radial glow that falls off with distance.", + "Light.occlude": "Register a rectangle that blocks light — a hard shadow caster.", + "Light.clear_occluders": "Forget every occluder — call once per frame before re-registering.", + "Light.spot": "A cone / flashlight light aimed at a direction with a half-angle spread.", + "Light.falloff": "Set the brightness-ramp exponent for later lights (1 linear, 2 quadratic…).", + "Light.soft": "Soft shadows — an occluder edge fades through a penumbra of this radius.", + "Light.gel": "A colour cookie — later lights gel from their centre colour to this rim colour.", + "Light.clear_gel": "Clear the gel — later lights are a flat single colour again.", + "Light.normal": "Stamp a surface normal over a rectangle so lights shade it by facing (N·L).", + "Light.clear_normals": "Forget every stamped normal — call once per frame before re-stamping.", + "Light.height": "Set the virtual height of lights above the surface, for normal-map shading.", + "Light.time_of_day": "Set the ambient tint from a 0..1 time-of-day — a day/night cycle in one value.", + "Sprite.sheet": "Load a spritesheet and remember its cell grid; returns a sheet handle.", + "Sprite.cell": "One cell of a sheet, by grid coords; returns a sprite id.", + "Sprite.cell_span": "A sprite spanning cols x rows cells (tall/wide art).", + "Sprite.define": "Name a cell for later lookup; returns its sprite id.", + "Sprite.named": "Look up a named sprite's id, or -1.", + "Sprite.draw": "Blit an atlas sprite at (x, y) through the camera/zoom/clip.", + "Sprite.draw_scaled": "Blit an atlas sprite scaled by an integer factor.", + "Sprite.width": "The atlas sprite's width in pixels.", + "Sprite.height": "The atlas sprite's height in pixels.", + "Sprite.strip": "A run of animation frames as consecutive sprite ids.", + "Sprite.draw_meter": "A value as a row of full / half / empty icons.", + "Assets.image": "Load a whole image file as one sprite; returns its id.", + "Assets.load": "Alias of Assets.image.", + "Assets.get": "Look up a named sprite's id (alias of Sprite.named).", + "Assets.enqueue": "Queue a named image file to preload later.", + "Assets.pump": "Load up to max queued assets this frame; returns how many it loaded.", + "Assets.total": "How many assets are enqueued.", + "Assets.loaded": "How many enqueued assets have loaded so far.", + "Assets.ready": "1 once every enqueued asset has loaded.", + "Assets.font": "A font loaded through the preload queue, by name.", + "Assets.progress": "Loading progress as a 0..100 percent.", + "Assets.enqueue_dir": "Enqueue every file of a directory, named by its file name.", + "test": "A named test block, run automatically with pass/fail reporting.", + "Pool.live": "How many entities are currently alive.", + "Pool.free": "How many freed slots are waiting to be reused.", + "Pool.reserved": "High-water: how many slots have ever been allocated.", + "Pool.capacity": "The maximum number of entities.", + "Value.null": "An empty null node.", + "Value.int": "Wrap an int in a value node.", + "Value.fixed": "Wrap a fixed in a value node.", + "Value.bool": "Wrap a bool in a value node.", + "Value.str": "Wrap a string in a value node.", + "Value.list": "An empty list node.", + "Value.object": "An empty object node.", + "Value.add": "Append an item to a list; returns the list.", + "Value.put": "Set a key on an object; returns the object.", + "Value.get": "Read a member of an object by key.", + "Value.has": "Whether an object has a member.", + "Value.at": "Read a list item by index.", + "Value.key_at": "The key of an object member by position.", + "Value.count": "Number of items/members in a list or object.", + "Value.kind": "The node's kind tag.", + "Value.as_int": "Read a scalar node as an int.", + "Value.as_str": "Read a str node's text.", + "Json.encode": "Serialize a value tree to compact JSON text.", + "Json.parse": "Parse JSON text into a value tree.", + "Job.defer": "A future you resolve yourself later.", + "Job.run": "Start a background compute job.", + "Job.fulfill": "Resolve a pending job with a value.", + "Job.fail": "Resolve a pending job as failed.", + "Job.cancel": "Cancel a job before it finishes.", + "Job.pump": "Advance background jobs; collect results.", + "Job.done": "Has the job resolved (any outcome)?", + "Job.ok": "Did the job succeed?", + "Job.failed": "Did the job fail?", + "Job.cancelled": "Was the job cancelled?", + "Job.result": "The success value of a done job.", + "Job.error": "The error code of a failed job.", + "Job.pending": "How many jobs are still unresolved.", + "Job.free": "Release a job slot back to the pool.", + "Job.parallel_for": "Run work(i, ctx) for every i in [0, count) across all cores; returns when every call is done.", + "Job.is_worker": "True on a Job.parallel_for pool thread, false on the main thread.", + "Promise.all": "Succeeds when every member succeeds.", + "Promise.race": "Succeeds when the first member does.", + "Promise.count_done": "How many members have resolved.", + "Promise.all_done": "Have all members resolved?", + "Sync.mutex": "Create a cooperative lock.", + "Sync.lock": "Take the lock.", + "Sync.unlock": "Release the lock.", + "Sync.try_lock": "Take the lock only if it is free.", + "Sync.atomic": "Create an atomic counter (starts at 0).", + "Sync.get": "Read the counter.", + "Sync.set": "Store a value in the counter.", + "Sync.add": "Add to the counter; return the new value.", + "Sync.cas": "Compare-and-set the counter.", + "Sync.channel": "Create a bounded int FIFO channel.", + "Sync.send": "Enqueue a value (false if full).", + "Sync.recv": "Dequeue the oldest value.", + "Sync.can_recv": "Is there a value waiting?", + "Sync.len": "How many values are queued.", + "Sync.cpu_count": "The machine's logical cores (1 to 64); Job.parallel_for uses one thread per core.", + "Xml.parse": "Parse XML text into an element tree.", + "Xml.tag": "The element's tag name.", + "Xml.text": "The element's character data.", + "Xml.attr": "An attribute's value by name.", + "Xml.attr_int": "An attribute parsed as an integer.", + "Xml.has": "Whether an attribute is present.", + "Xml.attr_count": "How many attributes the element has.", + "Xml.child_count": "How many child elements.", + "Xml.child": "A child element by index.", + "Xml.find": "The first child with a given tag.", + "Xml.count": "How many children have a given tag.", + "Base64.decode": "Decode standard base64 text to bytes.", + "Base64.encode": "Encode bytes as standard base64 text.", + "Tiled.read": "Read a map file into the intermediate value tree.", + "Tiled.read_tsx": "Read a tileset file into a value object.", + "Tiled.load": "Load a map file into the runtime map model.", + "Tiled.gid": "The raw GID at a cell in a tile layer.", + "Tiled.resolve": "Decode a GID into tileset, local id and flip flags.", + "Tiled.width": "The map width in tiles.", + "Tiled.height": "The map height in tiles.", + "Tiled.layer_count": "How many layers the map has.", + "Tiled.layer_name": "A layer's name by index.", + "Tiled.draw": "Draw every visible tile layer.", + "Tiled.project": "Project a collision layer to the byte tilemap.", + "Tiled.tree": "The underlying intermediate value tree.", + "Tiled.tile_prop": "Whether a GID's tile carries a bool property.", + "Tiled.collide": "Drive collision from per-tile metadata.", + "Tiled.collision_kind": "Classify a GID's collision from its tile metadata.", + "Tiled.tile_shapes": "Whether a GID's tile has a collision shape.", + "Tiled.draw_anim": "Draw with animated tiles advanced to a frame.", + "Tiled.frame_gid": "The current GID of an animated tile at a frame.", + "Tiled.animated": "Whether a GID's tile is animated.", + "Tiled.object_count": "How many objects an object layer holds.", + "Tiled.object": "An object on an object layer by index.", + "Tiled.object_shape": "Classify an object's shape.", + "Tiled.prop": "A custom property's value, with class-default fallback.", + "Tiled.prop_int": "A custom property parsed as an integer.", + "Tiled.prop_type": "A custom property's declared type.", + "Tiled.load_types": "Load a project custom-type table.", + "Tiled.template": "Read a template file into an object value.", + "Tiled.spawn": "Spawn a Ludic entity from an object.", + "Tiled.spawn_layer": "Spawn every object on a layer.", + "Tiled.cell_x": "The screen x of a tile cell for the map orientation.", + "Tiled.cell_y": "The screen y of a tile cell for the map orientation.", + "Tiled.layer_kind": "A layer's kind.", + "Tiled.layer_opacity": "A layer's opacity.", + "Tiled.layer_tint": "A layer's tint colour.", + "Tiled.layer_offsetx": "A layer's horizontal offset.", + "Tiled.layer_offsety": "A layer's vertical offset.", + "Tiled.world": "Read a .world file.", + "Tiled.world_count": "How many maps a world stitches.", + "Tiled.world_map": "A world member map by index.", + "Ui.build": "Construct every declared ui block (loads skins, measures fonts).", + "Ui.open": "Make one menu active and focus its first button.", + "Ui.close": "Deactivate the menu: no menu is open.", + "Ui.tick": "Advance navigation by one key (the frame loop does this for you).", + "Ui.clicked": "Was this control activated this frame? (polled form of UiClicked)", + "Ui.set_text": "Replace a label's or button's text.", + "Ui.render": "Draw the active menu.", + "File.open": "Open a file (fopen); null when it cannot be opened.", + "File.read": "Read up to n bytes into a buffer; returns the bytes read.", + "File.write": "Write n bytes from a buffer; returns the bytes written.", + "File.seek": "Move the file cursor (0 start, 1 current, 2 end).", + "File.tell": "The cursor position in bytes.", + "File.close": "Close a handle from File.open.", + "Font.load": "Load a TrueType font; returns a font handle.", + "Fx.sparks": "A burst of sparks flying out of a point.", + "Fx.number": "A number that floats up from a point and fades.", + "Fx.clear": "Drop every spark and number at once.", + "Prefab.spawn": "Spawn a prefab chosen by name at runtime; -1 if none has that name.", + "Prefab.spawn_at": "Spawn a prefab by name and place it.", + "App.splash_hide": "Dismiss the boot splash.", + "App.set_icon": "Set the Dock icon from an image the game ships.", + "App.window_hide": "Take the game's window off the screen.", + "App.window_show": "Bring the game's window back.", + "Start": "Runs once at boot, before the game loop — the place to set up initial state.", + "Input": "The first per-frame phase — read the keyboard and record the player's intent.", + "FixedUpdate": "The deterministic simulation step — physics and gameplay meant to be reproducible.", + "Update": "The ordinary per-frame game-logic step, run after Input and FixedUpdate.", + "LateUpdate": "Runs each frame after Update and before Render.", + "Render": "The last per-frame phase — draw the world, then call Screen.show() once.", + "Overlay": "The HUD pass — runs after Render and after every engine-owned drawing system." + }, + "cards": { + "kw-program": { + "name": "program", + "sig": "program Name { … }", + "tip": "The top-level unit — one program compiles to one native game.", + "kind": "keyword", + "page": "kw-program.html", + "section": "Program structure" + }, + "kw-property": { + "name": "property", + "sig": "property Name { field: T = default, … }", + "tip": "A named record of typed fields — a per-model component, or a plain heap record.", + "kind": "keyword", + "page": "kw-property.html", + "section": "Program structure" + }, + "kw-model": { + "name": "model", + "sig": "model Name { PropA, PropB, … }", + "tip": "A named kind of thing — a fixed bundle of properties you spawn by one name.", + "kind": "keyword", + "page": "kw-model.html", + "section": "Program structure" + }, + "kw-handler": { + "name": "handler", + "sig": "handler Name phase P { … }", + "tip": "A named block the engine runs each frame during phase P.", + "kind": "keyword", + "page": "kw-handler.html", + "section": "Program structure" + }, + "kw-phase": { + "name": "phase", + "sig": "phase Start | Input | Update | FixedUpdate | Render", + "tip": "Names which stage of the frame a handler runs in.", + "kind": "keyword", + "page": "kw-phase.html", + "section": "Program structure" + }, + "kw-const": { + "name": "const", + "sig": "const NAME: T = value", + "tip": "A compile-time constant, folded into the code with no storage.", + "kind": "keyword", + "page": "kw-const.html", + "section": "Program structure" + }, + "kw-var": { + "name": "var", + "sig": "var name: T = value", + "tip": "A mutable binding — at program scope, your game's persistent named state.", + "kind": "keyword", + "page": "kw-var.html", + "section": "Program structure" + }, + "kw-let": { + "name": "let", + "sig": "let name = value", + "tip": "An immutable binding — the default choice for a value that never changes.", + "kind": "keyword", + "page": "kw-let.html", + "section": "Program structure" + }, + "kw-function": { + "name": "function", + "sig": "function name(a: T, b: T) -> R { … }", + "tip": "A function — reusable logic called positionally or with named arguments.", + "kind": "keyword", + "page": "kw-function.html", + "section": "Program structure" + }, + "kw-fn": { + "name": "fn", + "sig": "fn name", + "tip": "A top-level function named as a value - the entry point handed to Job.parallel_for.", + "kind": "keyword", + "page": "kw-fn.html", + "section": "Program structure" + }, + "kw-prefab": { + "name": "prefab", + "sig": "prefab Name: Model { Comp { field: value }, … }", + "tip": "A model with preset component fields — spawn it, override what varies.", + "kind": "keyword", + "page": "kw-prefab.html", + "section": "Program structure" + }, + "kw-return": { + "name": "return", + "sig": "return value", + "tip": "Hand a value back from a function and stop running it.", + "kind": "keyword", + "page": "kw-return.html", + "section": "Program structure" + }, + "kw-entry": { + "name": "entry", + "sig": "entry { … }", + "tip": "The program's main block — runs once, top to bottom.", + "kind": "keyword", + "page": "kw-entry.html", + "section": "Program structure" + }, + "kw-import": { + "name": "import", + "sig": "import \"file.ludic\"", + "tip": "Splice another Ludic file's declarations into this program.", + "kind": "keyword", + "page": "kw-import.html", + "section": "Program structure" + }, + "kw-public": { + "name": "public", + "sig": "scene Name start public { … }", + "tip": "Expose a declaration's lifecycle on the public event bus.", + "kind": "keyword", + "page": "kw-public.html", + "section": "Program structure" + }, + "kw-extern": { + "name": "extern", + "sig": "extern function name(a: T) -> R = \"symbol\"", + "tip": "Bind a name to an external native symbol — the seam for platform and library calls.", + "kind": "keyword", + "page": "kw-extern.html", + "section": "Program structure" + }, + "kw-new": { + "name": "new", + "sig": "new Record { … } new []T", + "tip": "Allocate a property record or an empty slice on the heap.", + "kind": "keyword", + "page": "kw-new.html", + "section": "Program structure" + }, + "kw-namespace": { + "name": "namespace", + "sig": "namespace Name { export function foo(…) … internal function bar(…) … }", + "tip": "Group functions into a Name.* namespace and control its public surface.", + "kind": "keyword", + "page": "kw-namespace.html", + "section": "Program structure" + }, + "kw-enum": { + "name": "enum", + "sig": "enum Name { A, B, C }", + "tip": "A named set of integer constants — names for a magic-number space.", + "kind": "keyword", + "page": "kw-enum.html", + "section": "Program structure" + }, + "kw-ui": { + "name": "ui", + "sig": "ui { panel { … } }", + "tip": "Declare a retained widget tree as data; the engine lays it out and draws it.", + "kind": "keyword", + "page": "kw-ui.html", + "section": "Program structure" + }, + "kw-spawn": { + "name": "spawn", + "sig": "spawn Label { Prop { field: v }; … }", + "tip": "Create a model instance carrying the listed properties, seeding their fields.", + "kind": "keyword", + "page": "kw-spawn.html", + "section": "Entities & the ECS" + }, + "kw-despawn": { + "name": "despawn", + "sig": "despawn entity", + "tip": "Remove a model instance from the world, firing any @OnDespawn hooks.", + "kind": "keyword", + "page": "kw-despawn.html", + "section": "Entities & the ECS" + }, + "kw-query": { + "name": "query", + "sig": "query [PropA, PropB, {Tag}]", + "tip": "Match every model instance carrying all the listed properties.", + "kind": "keyword", + "page": "kw-query.html", + "section": "Entities & the ECS" + }, + "kw-self": { + "name": "self", + "sig": "self()", + "tip": "The model instance currently bound by the enclosing query loop.", + "kind": "keyword", + "page": "kw-self.html", + "section": "Entities & the ECS" + }, + "kw-enable": { + "name": "enable", + "sig": "enable Prop on entity", + "tip": "Re-activate a disabled property, model, or handler — its data is intact.", + "kind": "keyword", + "page": "kw-enable.html", + "section": "Entities & the ECS" + }, + "kw-disable": { + "name": "disable", + "sig": "disable Prop on entity", + "tip": "Deactivate a property, model, or handler without destroying its data.", + "kind": "keyword", + "page": "kw-disable.html", + "section": "Entities & the ECS" + }, + "kw-attach": { + "name": "attach", + "sig": "attach Prop on entity { overrides }", + "tip": "Structurally add a property to a live model instance, seeding its fields.", + "kind": "keyword", + "page": "kw-attach.html", + "section": "Entities & the ECS" + }, + "kw-detach": { + "name": "detach", + "sig": "detach Prop on entity", + "tip": "Structurally remove a property from a live model instance.", + "kind": "keyword", + "page": "kw-detach.html", + "section": "Entities & the ECS" + }, + "fn-world_attach_dyn": { + "name": "world_attach_dyn", + "sig": "world_attach_dyn(target, property)", + "tip": "Attach a property to an instance by numeric id at runtime (dynamic ECS).", + "kind": "builtin", + "page": "fn-world_attach_dyn.html", + "section": "Entities & the ECS" + }, + "fn-world_count": { + "name": "world_count", + "sig": "world_count() -> int", + "tip": "The total number of live model instances in the world.", + "kind": "builtin", + "page": "fn-world_count.html", + "section": "Entities & the ECS" + }, + "fn-world_detach_dyn": { + "name": "world_detach_dyn", + "sig": "world_detach_dyn(target, property)", + "tip": "Detach a property from an instance by numeric id at runtime (dynamic ECS).", + "kind": "builtin", + "page": "fn-world_detach_dyn.html", + "section": "Entities & the ECS" + }, + "fn-world_field_id": { + "name": "world_field_id", + "sig": "world_field_id(property, name) -> int", + "tip": "Resolve a field name within a property to its numeric index.", + "kind": "builtin", + "page": "fn-world_field_id.html", + "section": "Entities & the ECS" + }, + "fn-world_get": { + "name": "world_get", + "sig": "world_get(target, property, field) -> int", + "tip": "Read one field of a model instance by numeric id (reflection ABI).", + "kind": "builtin", + "page": "fn-world_get.html", + "section": "Entities & the ECS" + }, + "fn-world_has": { + "name": "world_has", + "sig": "world_has(target, property) -> bool", + "tip": "Test whether a model instance currently carries a property.", + "kind": "builtin", + "page": "fn-world_has.html", + "section": "Entities & the ECS" + }, + "fn-world_kind": { + "name": "world_kind", + "sig": "world_kind(target) -> int", + "tip": "The model id of an instance — which kind of thing it is.", + "kind": "builtin", + "page": "fn-world_kind.html", + "section": "Entities & the ECS" + }, + "fn-world_load": { + "name": "world_load", + "sig": "world_load(buf, len)", + "tip": "Restore the whole world from a serialized byte buffer.", + "kind": "builtin", + "page": "fn-world_load.html", + "section": "Entities & the ECS" + }, + "fn-world_model_id": { + "name": "world_model_id", + "sig": "world_model_id(name) -> int", + "tip": "Resolve a model's name to its stable numeric id.", + "kind": "builtin", + "page": "fn-world_model_id.html", + "section": "Entities & the ECS" + }, + "fn-world_prop_id": { + "name": "world_prop_id", + "sig": "world_prop_id(name) -> int", + "tip": "Resolve a property's name to its stable numeric id.", + "kind": "builtin", + "page": "fn-world_prop_id.html", + "section": "Entities & the ECS" + }, + "fn-world_query_next": { + "name": "world_query_next", + "sig": "world_query_next(property, cursor) -> entity", + "tip": "Step to the next instance carrying a property, walking the world by id.", + "kind": "builtin", + "page": "fn-world_query_next.html", + "section": "Entities & the ECS" + }, + "fn-world_register_prop": { + "name": "world_register_prop", + "sig": "world_register_prop(name, fields) -> int", + "tip": "Register a brand-new property at runtime and get its id (dynamic ECS).", + "kind": "builtin", + "page": "fn-world_register_prop.html", + "section": "Entities & the ECS" + }, + "fn-world_save": { + "name": "world_save", + "sig": "world_save(buf) -> int", + "tip": "Serialize the whole world into a buffer; returns the number of bytes written.", + "kind": "builtin", + "page": "fn-world_save.html", + "section": "Entities & the ECS" + }, + "fn-world_set": { + "name": "world_set", + "sig": "world_set(target, property, field, value)", + "tip": "Write one field of a model instance by numeric id (reflection ABI).", + "kind": "builtin", + "page": "fn-world_set.html", + "section": "Entities & the ECS" + }, + "fn-world_size": { + "name": "world_size", + "sig": "world_size() -> int", + "tip": "The number of live model instances in the world.", + "kind": "builtin", + "page": "fn-world_size.html", + "section": "Entities & the ECS" + }, + "fn-world_spawn": { + "name": "world_spawn", + "sig": "world_spawn(model) -> entity", + "tip": "Spawn an instance of a model chosen by numeric id at runtime.", + "kind": "builtin", + "page": "fn-world_spawn.html", + "section": "Entities & the ECS" + }, + "kw-if": { + "name": "if / else", + "sig": "if cond { … } else { … }", + "tip": "A branch — run one block when a condition holds, another when it doesn't.", + "kind": "keyword", + "page": "kw-if.html", + "section": "Control flow" + }, + "kw-while": { + "name": "while", + "sig": "while cond { … }", + "tip": "Loop as long as a condition holds, re-checking it before each pass.", + "kind": "keyword", + "page": "kw-while.html", + "section": "Control flow" + }, + "kw-for": { + "name": "for … in", + "sig": "for name in a .. b { … }", + "tip": "Range loop — iterate the half-open range from a up to but not including b.", + "kind": "keyword", + "page": "kw-for.html", + "section": "Control flow" + }, + "kw-in": { + "name": "in", + "sig": "for x in range | query", + "tip": "The part of a for loop that names what to iterate — a range or a query.", + "kind": "keyword", + "page": "kw-in.html", + "section": "Control flow" + }, + "kw-match": { + "name": "match", + "sig": "match value { 0, 1 => … _ => … }", + "tip": "Multi-way branch on one value, matching one or more literals per arm.", + "kind": "keyword", + "page": "kw-match.html", + "section": "Control flow" + }, + "kw-machine": { + "name": "machine", + "sig": "machine store { state Name { … } }", + "tip": "An explicit state machine over an int var — dispatches on the store's value.", + "kind": "keyword", + "page": "kw-machine.html", + "section": "Control flow" + }, + "kw-state": { + "name": "state", + "sig": "state Name { … }", + "tip": "One state of a machine — its body runs while the machine sits in it.", + "kind": "keyword", + "page": "kw-state.html", + "section": "Control flow" + }, + "kw-become": { + "name": "become", + "sig": "become Name", + "tip": "Transition to another state of the enclosing machine, or to another scene.", + "kind": "keyword", + "page": "kw-become.html", + "section": "Control flow" + }, + "kw-break": { + "name": "break", + "sig": "break", + "tip": "Leave the enclosing loop immediately.", + "kind": "keyword", + "page": "kw-break.html", + "section": "Control flow" + }, + "kw-continue": { + "name": "continue", + "sig": "continue", + "tip": "Skip to the next iteration of the loop.", + "kind": "keyword", + "page": "kw-continue.html", + "section": "Control flow" + }, + "kw-where": { + "name": "where", + "sig": "for (Binds) in query [Comps] where cond { … }", + "tip": "Filter a query loop to entities that satisfy a condition.", + "kind": "keyword", + "page": "kw-where.html", + "section": "Control flow" + }, + "kw-try": { + "name": "try", + "sig": "try EXPR else { … }", + "tip": "Recover a fallible result as a value, with a fallback — no exceptions, no unwinding.", + "kind": "keyword", + "page": "kw-try.html", + "section": "Control flow" + }, + "kw-scene": { + "name": "scene", + "sig": "scene Name [start] { on enter{} on exit{} layer … }", + "tip": "A mutually-exclusive game state — a title screen, the overworld, a battle.", + "kind": "keyword", + "page": "kw-scene.html", + "section": "Scenes & layers" + }, + "kw-layer": { + "name": "layer", + "sig": "layer Name { handlers }", + "tip": "A named group of handlers inside a scene; layers render in declaration order.", + "kind": "keyword", + "page": "kw-layer.html", + "section": "Scenes & layers" + }, + "kw-on": { + "name": "on enter / on exit", + "sig": "on enter { … } on exit { … }", + "tip": "Scene lifecycle hooks that fire as a scene becomes active or is left.", + "kind": "keyword", + "page": "kw-on.html", + "section": "Scenes & layers" + }, + "kw-enter": { + "name": "enter", + "sig": "on enter { … }", + "tip": "The scene-entry hook — runs once as a scene becomes active.", + "kind": "keyword", + "page": "kw-enter.html", + "section": "Scenes & layers" + }, + "kw-exit": { + "name": "exit", + "sig": "on exit { … }", + "tip": "The scene-exit hook — runs once as a scene is left.", + "kind": "keyword", + "page": "kw-exit.html", + "section": "Scenes & layers" + }, + "kw-start": { + "name": "start", + "sig": "scene Name start { … }", + "tip": "Marks the one scene the game begins in.", + "kind": "keyword", + "page": "kw-start.html", + "section": "Scenes & layers" + }, + "kw-event": { + "name": "event", + "sig": "event Name { field: T = default }", + "tip": "Declare a public event carrying a flat payload that listeners react to.", + "kind": "keyword", + "page": "kw-event.html", + "section": "Events" + }, + "kw-emit": { + "name": "emit", + "sig": "emit Name(field: value)", + "tip": "Fire an event, running every listener; as an expression it yields the veto flag.", + "kind": "keyword", + "page": "kw-emit.html", + "section": "Events" + }, + "kw-cancel": { + "name": "cancel", + "sig": "cancel", + "tip": "Inside a listener, veto the cancellable event being emitted.", + "kind": "keyword", + "page": "kw-cancel.html", + "section": "Events" + }, + "kw-cancellable": { + "name": "cancellable", + "sig": "event cancellable Name { … }", + "tip": "Marks an event whose listeners may veto it with cancel.", + "kind": "keyword", + "page": "kw-cancellable.html", + "section": "Events" + }, + "screen-clear": { + "name": "Screen.clear", + "sig": "Screen.clear(color)", + "tip": "Fill the whole framebuffer with one color to start a fresh frame.", + "kind": "namespace-method", + "page": "screen-clear.html", + "section": "Screen — drawing" + }, + "screen-fill_rectangle": { + "name": "Screen.fill_rectangle", + "sig": "Screen.fill_rectangle(x, y, width, height, color)", + "tip": "Draw a solid, filled rectangle at a pixel position.", + "kind": "namespace-method", + "page": "screen-fill_rectangle.html", + "section": "Screen — drawing" + }, + "screen-draw_rectangle": { + "name": "Screen.draw_rectangle", + "sig": "Screen.draw_rectangle(x, y, width, height, color)", + "tip": "Draw a one-pixel-thick rectangle outline (not filled).", + "kind": "namespace-method", + "page": "screen-draw_rectangle.html", + "section": "Screen — drawing" + }, + "screen-put_pixel": { + "name": "Screen.put_pixel", + "sig": "Screen.put_pixel(x, y, color)", + "tip": "Set a single pixel at a pixel coordinate to one color.", + "kind": "namespace-method", + "page": "screen-put_pixel.html", + "section": "Screen — drawing" + }, + "screen-draw_text": { + "name": "Screen.draw_text", + "sig": "Screen.draw_text(x, y, text, color, scale)", + "tip": "Draw a string with the built-in font at an integer scale.", + "kind": "namespace-method", + "page": "screen-draw_text.html", + "section": "Screen — drawing" + }, + "screen-draw_number": { + "name": "Screen.draw_number", + "sig": "Screen.draw_number(x, y, value, color, scale)", + "tip": "Draw an integer directly, with no string allocation or conversion.", + "kind": "namespace-method", + "page": "screen-draw_number.html", + "section": "Screen — drawing" + }, + "screen-show": { + "name": "Screen.show", + "sig": "Screen.show()", + "tip": "Present the finished frame — copy everything drawn this frame to the window.", + "kind": "namespace-method", + "page": "screen-show.html", + "section": "Screen — drawing" + }, + "screen-width": { + "name": "Screen.width", + "sig": "Screen.width() -> int", + "tip": "The framebuffer width in pixels.", + "kind": "namespace-method", + "page": "screen-width.html", + "section": "Screen — drawing" + }, + "screen-height": { + "name": "Screen.height", + "sig": "Screen.height() -> int", + "tip": "The framebuffer height in pixels.", + "kind": "namespace-method", + "page": "screen-height.html", + "section": "Screen — drawing" + }, + "screen-status": { + "name": "Screen.status", + "sig": "Screen.status(text)", + "tip": "Set the persistent one-line status/HUD string shown by the runtime.", + "kind": "namespace-method", + "page": "screen-status.html", + "section": "Screen — drawing" + }, + "screen-line": { + "name": "Screen.line", + "sig": "Screen.line(x1, y1, x2, y2, color)", + "tip": "Draw a straight line between two points.", + "kind": "namespace-method", + "page": "screen-line.html", + "section": "Screen — drawing" + }, + "screen-circle": { + "name": "Screen.circle", + "sig": "Screen.circle(x, y, radius, color)", + "tip": "Draw a circle outline.", + "kind": "namespace-method", + "page": "screen-circle.html", + "section": "Screen — drawing" + }, + "screen-fill_circle": { + "name": "Screen.fill_circle", + "sig": "Screen.fill_circle(x, y, radius, color)", + "tip": "Draw a filled disc.", + "kind": "namespace-method", + "page": "screen-fill_circle.html", + "section": "Screen — drawing" + }, + "screen-triangle": { + "name": "Screen.triangle", + "sig": "Screen.triangle(x1, y1, x2, y2, x3, y3, color)", + "tip": "Draw a triangle outline.", + "kind": "namespace-method", + "page": "screen-triangle.html", + "section": "Screen — drawing" + }, + "screen-fill_triangle": { + "name": "Screen.fill_triangle", + "sig": "Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color)", + "tip": "Draw a filled triangle.", + "kind": "namespace-method", + "page": "screen-fill_triangle.html", + "section": "Screen — drawing" + }, + "screen-sprite": { + "name": "Screen.sprite", + "sig": "Screen.sprite(id, x, y)", + "tip": "Blit a sprite at its natural size.", + "kind": "namespace-method", + "page": "screen-sprite.html", + "section": "Screen — drawing" + }, + "screen-sprite_scaled": { + "name": "Screen.sprite_scaled", + "sig": "Screen.sprite_scaled(id, x, y, scale)", + "tip": "Blit a sprite at an integer scale.", + "kind": "namespace-method", + "page": "screen-sprite_scaled.html", + "section": "Screen — drawing" + }, + "screen-oval": { + "name": "Screen.oval", + "sig": "Screen.oval(x, y, rx, ry, color)", + "tip": "Draw an axis-aligned ellipse outline.", + "kind": "namespace-method", + "page": "screen-oval.html", + "section": "Screen — drawing" + }, + "screen-camera": { + "name": "Screen.camera", + "sig": "Screen.camera(x, y)", + "tip": "Set the world-space camera offset for the draw path.", + "kind": "namespace-method", + "page": "screen-camera.html", + "section": "Screen — drawing" + }, + "screen-clip": { + "name": "Screen.clip", + "sig": "Screen.clip(x, y, width, height)", + "tip": "Restrict drawing to a screen-space rectangle.", + "kind": "namespace-method", + "page": "screen-clip.html", + "section": "Screen — drawing" + }, + "screen-clip_reset": { + "name": "Screen.clip_reset", + "sig": "Screen.clip_reset()", + "tip": "Lift the clip rectangle (draw to the whole screen again).", + "kind": "namespace-method", + "page": "screen-clip_reset.html", + "section": "Screen — drawing" + }, + "screen-blend_mode": { + "name": "Screen.blend_mode", + "sig": "Screen.blend_mode(mode)", + "tip": "Choose replace or additive pixel blending.", + "kind": "namespace-method", + "page": "screen-blend_mode.html", + "section": "Screen — drawing" + }, + "screen-measure_text": { + "name": "Screen.measure_text", + "sig": "Screen.measure_text(text) -> int", + "tip": "The pixel advance width of text in the built-in font.", + "kind": "namespace-method", + "page": "screen-measure_text.html", + "section": "Screen — drawing" + }, + "screen-pixel": { + "name": "Screen.pixel", + "sig": "Screen.pixel(x, y) -> int", + "tip": "Read a framebuffer pixel (0x00RRGGBB), or 0 if off-screen.", + "kind": "namespace-method", + "page": "screen-pixel.html", + "section": "Screen — drawing" + }, + "screen-bar": { + "name": "Screen.bar", + "sig": "Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:)", + "tip": "A filled meter: value of max over a track.", + "kind": "namespace-method", + "page": "screen-bar.html", + "section": "Screen — drawing" + }, + "clock-now": { + "name": "Clock.now", + "sig": "Clock.now() -> int", + "tip": "The current simulated instant.", + "kind": "namespace-method", + "page": "clock-now.html", + "section": "Clock" + }, + "clock-set": { + "name": "Clock.set", + "sig": "Clock.set(t)", + "tip": "Set the clock to an instant.", + "kind": "namespace-method", + "page": "clock-set.html", + "section": "Clock" + }, + "clock-advance": { + "name": "Clock.advance", + "sig": "Clock.advance(span)", + "tip": "Move the clock forward by a Duration.", + "kind": "namespace-method", + "page": "clock-advance.html", + "section": "Clock" + }, + "clock-reset": { + "name": "Clock.reset", + "sig": "Clock.reset()", + "tip": "Reset the clock to the epoch (0).", + "kind": "namespace-method", + "page": "clock-reset.html", + "section": "Clock" + }, + "collision-rects": { + "name": "Collision.rects", + "sig": "Collision.rects(ax, ay, aw, ah, bx, by, bw, bh) -> bool", + "tip": "Do two rectangles overlap?", + "kind": "namespace-method", + "page": "collision-rects.html", + "section": "Collision" + }, + "collision-point_rect": { + "name": "Collision.point_rect", + "sig": "Collision.point_rect(px, py, rx, ry, rw, rh) -> bool", + "tip": "Is a point inside a rectangle?", + "kind": "namespace-method", + "page": "collision-point_rect.html", + "section": "Collision" + }, + "collision-circles": { + "name": "Collision.circles", + "sig": "Collision.circles(ax, ay, ar, bx, by, br) -> bool", + "tip": "Do two circles overlap?", + "kind": "namespace-method", + "page": "collision-circles.html", + "section": "Collision" + }, + "collision-rect_circle": { + "name": "Collision.rect_circle", + "sig": "Collision.rect_circle(rx, ry, rw, rh, cx, cy, cr) -> bool", + "tip": "Does a rectangle overlap a circle?", + "kind": "namespace-method", + "page": "collision-rect_circle.html", + "section": "Collision" + }, + "color-rgb": { + "name": "Color.rgb", + "sig": "Color.rgb(r, g, b) -> int", + "tip": "Build a color from red, green, blue.", + "kind": "namespace-method", + "page": "color-rgb.html", + "section": "Color functions" + }, + "color-rgba": { + "name": "Color.rgba", + "sig": "Color.rgba(r, g, b, a) -> int", + "tip": "Build a color with an alpha byte.", + "kind": "namespace-method", + "page": "color-rgba.html", + "section": "Color functions" + }, + "color-lerp": { + "name": "Color.lerp", + "sig": "Color.lerp(c0, c1, t) -> int", + "tip": "Blend between two colors.", + "kind": "namespace-method", + "page": "color-lerp.html", + "section": "Color functions" + }, + "color-darken": { + "name": "Color.darken", + "sig": "Color.darken(c, amount) -> int", + "tip": "Scale a color toward black.", + "kind": "namespace-method", + "page": "color-darken.html", + "section": "Color functions" + }, + "color-lighten": { + "name": "Color.lighten", + "sig": "Color.lighten(c, amount) -> int", + "tip": "Scale a color toward white.", + "kind": "namespace-method", + "page": "color-lighten.html", + "section": "Color functions" + }, + "color-with_alpha": { + "name": "Color.with_alpha", + "sig": "Color.with_alpha(c, a) -> int", + "tip": "Replace a color's alpha byte.", + "kind": "namespace-method", + "page": "color-with_alpha.html", + "section": "Color functions" + }, + "crypto-sha256": { + "name": "Crypto.sha256", + "sig": "Crypto.sha256(s) -> string", + "tip": "SHA-256 of a string, as 64 hex characters.", + "kind": "namespace-method", + "page": "crypto-sha256.html", + "section": "Crypto" + }, + "crypto-hmac_sha256": { + "name": "Crypto.hmac_sha256", + "sig": "Crypto.hmac_sha256(key, msg) -> string", + "tip": "Sign a message with a shared secret key.", + "kind": "namespace-method", + "page": "crypto-hmac_sha256.html", + "section": "Crypto" + }, + "crypto-verify_hmac": { + "name": "Crypto.verify_hmac", + "sig": "Crypto.verify_hmac(key, msg, mac) -> bool", + "tip": "Constant-time check that a MAC matches.", + "kind": "namespace-method", + "page": "crypto-verify_hmac.html", + "section": "Crypto" + }, + "crypto-hex": { + "name": "Crypto.hex", + "sig": "Crypto.hex(s) -> string", + "tip": "Lowercase hex of a string's bytes.", + "kind": "namespace-method", + "page": "crypto-hex.html", + "section": "Crypto" + }, + "crypto-ct_equal": { + "name": "Crypto.ct_equal", + "sig": "Crypto.ct_equal(a, b) -> bool", + "tip": "Constant-time string equality for secrets.", + "kind": "namespace-method", + "page": "crypto-ct_equal.html", + "section": "Crypto" + }, + "crypto-random_bytes": { + "name": "Crypto.random_bytes", + "sig": "Crypto.random_bytes(n) -> string", + "tip": "n bytes from the OS CSPRNG, as a 2n-character hex string.", + "kind": "namespace-method", + "page": "crypto-random_bytes.html", + "section": "Crypto" + }, + "crypto-random_hex": { + "name": "Crypto.random_hex", + "sig": "Crypto.random_hex(n) -> string", + "tip": "Alias for random_bytes — n secure bytes as a 2n-char hex string.", + "kind": "namespace-method", + "page": "crypto-random_hex.html", + "section": "Crypto" + }, + "crypto-random_u32": { + "name": "Crypto.random_u32", + "sig": "Crypto.random_u32() -> int", + "tip": "One CSPRNG-drawn 32-bit integer.", + "kind": "namespace-method", + "page": "crypto-random_u32.html", + "section": "Crypto" + }, + "crypto-base64": { + "name": "Crypto.base64", + "sig": "Crypto.base64(s) -> string", + "tip": "Standard base64 (RFC 4648) of a string's bytes.", + "kind": "namespace-method", + "page": "crypto-base64.html", + "section": "Crypto" + }, + "date-new": { + "name": "Date.new", + "sig": "Date.new(year, month, day) -> int", + "tip": "A calendar day as days-since-1970.", + "kind": "namespace-method", + "page": "date-new.html", + "section": "Date" + }, + "date-year": { + "name": "Date.year", + "sig": "Date.year(date) -> int", + "tip": "The calendar year of a date.", + "kind": "namespace-method", + "page": "date-year.html", + "section": "Date" + }, + "date-month": { + "name": "Date.month", + "sig": "Date.month(date) -> int", + "tip": "The month (1-12) of a date.", + "kind": "namespace-method", + "page": "date-month.html", + "section": "Date" + }, + "date-day": { + "name": "Date.day", + "sig": "Date.day(date) -> int", + "tip": "The day of the month (1-31) of a date.", + "kind": "namespace-method", + "page": "date-day.html", + "section": "Date" + }, + "date-weekday": { + "name": "Date.weekday", + "sig": "Date.weekday(date) -> int", + "tip": "Day of the week, 0=Sunday..6=Saturday.", + "kind": "namespace-method", + "page": "date-weekday.html", + "section": "Date" + }, + "date-is_leap": { + "name": "Date.is_leap", + "sig": "Date.is_leap(year) -> bool", + "tip": "True if the year is a leap year.", + "kind": "namespace-method", + "page": "date-is_leap.html", + "section": "Date" + }, + "date-days_in_month": { + "name": "Date.days_in_month", + "sig": "Date.days_in_month(year, month) -> int", + "tip": "Number of days in a given month.", + "kind": "namespace-method", + "page": "date-days_in_month.html", + "section": "Date" + }, + "date-to_epoch": { + "name": "Date.to_epoch", + "sig": "Date.to_epoch(date) -> int", + "tip": "Midnight UTC of the day, as a DateTime.", + "kind": "namespace-method", + "page": "date-to_epoch.html", + "section": "Date" + }, + "date-add_days": { + "name": "Date.add_days", + "sig": "Date.add_days(date, n) -> int", + "tip": "The date n days later (n may be negative).", + "kind": "namespace-method", + "page": "date-add_days.html", + "section": "Date" + }, + "date-diff_days": { + "name": "Date.diff_days", + "sig": "Date.diff_days(a, b) -> int", + "tip": "Whole days from b to a (a - b).", + "kind": "namespace-method", + "page": "date-diff_days.html", + "section": "Date" + }, + "datetime-from": { + "name": "DateTime.from", + "sig": "DateTime.from(year, month, day, hour, minute, second) -> int", + "tip": "Build an instant from its calendar parts.", + "kind": "namespace-method", + "page": "datetime-from.html", + "section": "DateTime" + }, + "datetime-date": { + "name": "DateTime.date", + "sig": "DateTime.date(dt) -> int", + "tip": "The calendar day the instant falls on.", + "kind": "namespace-method", + "page": "datetime-date.html", + "section": "DateTime" + }, + "datetime-add": { + "name": "DateTime.add", + "sig": "DateTime.add(dt, span) -> int", + "tip": "The instant `span` seconds later.", + "kind": "namespace-method", + "page": "datetime-add.html", + "section": "DateTime" + }, + "datetime-year": { + "name": "DateTime.year", + "sig": "DateTime.year(dt) -> int", + "tip": "The calendar year of an instant.", + "kind": "namespace-method", + "page": "datetime-year.html", + "section": "DateTime" + }, + "datetime-month": { + "name": "DateTime.month", + "sig": "DateTime.month(dt) -> int", + "tip": "The month (1-12) of an instant.", + "kind": "namespace-method", + "page": "datetime-month.html", + "section": "DateTime" + }, + "datetime-day": { + "name": "DateTime.day", + "sig": "DateTime.day(dt) -> int", + "tip": "The day of the month (1-31) of an instant.", + "kind": "namespace-method", + "page": "datetime-day.html", + "section": "DateTime" + }, + "datetime-weekday": { + "name": "DateTime.weekday", + "sig": "DateTime.weekday(dt) -> int", + "tip": "Day of the week, 0=Sunday..6=Saturday.", + "kind": "namespace-method", + "page": "datetime-weekday.html", + "section": "DateTime" + }, + "datetime-hour": { + "name": "DateTime.hour", + "sig": "DateTime.hour(dt) -> int", + "tip": "The hour of day (0-23) of an instant.", + "kind": "namespace-method", + "page": "datetime-hour.html", + "section": "DateTime" + }, + "datetime-minute": { + "name": "DateTime.minute", + "sig": "DateTime.minute(dt) -> int", + "tip": "The minute of the hour (0-59) of an instant.", + "kind": "namespace-method", + "page": "datetime-minute.html", + "section": "DateTime" + }, + "datetime-second": { + "name": "DateTime.second", + "sig": "DateTime.second(dt) -> int", + "tip": "The second of the minute (0-59) of an instant.", + "kind": "namespace-method", + "page": "datetime-second.html", + "section": "DateTime" + }, + "datetime-format": { + "name": "DateTime.format", + "sig": "DateTime.format(dt, pattern) -> string", + "tip": "Render an instant as text using a token pattern.", + "kind": "namespace-method", + "page": "datetime-format.html", + "section": "DateTime" + }, + "datetime-parse": { + "name": "DateTime.parse", + "sig": "DateTime.parse(text, pattern) -> int", + "tip": "Parse text into an instant; -1 on failure.", + "kind": "namespace-method", + "page": "datetime-parse.html", + "section": "DateTime" + }, + "duration-seconds": { + "name": "Duration.seconds", + "sig": "Duration.seconds(n) -> int", + "tip": "A span of n whole seconds.", + "kind": "namespace-method", + "page": "duration-seconds.html", + "section": "Duration" + }, + "duration-minutes": { + "name": "Duration.minutes", + "sig": "Duration.minutes(n) -> int", + "tip": "A span of n minutes, in seconds.", + "kind": "namespace-method", + "page": "duration-minutes.html", + "section": "Duration" + }, + "duration-hours": { + "name": "Duration.hours", + "sig": "Duration.hours(n) -> int", + "tip": "A span of n hours, in seconds.", + "kind": "namespace-method", + "page": "duration-hours.html", + "section": "Duration" + }, + "duration-days": { + "name": "Duration.days", + "sig": "Duration.days(n) -> int", + "tip": "A span of n days, in seconds.", + "kind": "namespace-method", + "page": "duration-days.html", + "section": "Duration" + }, + "duration-as_seconds": { + "name": "Duration.as_seconds", + "sig": "Duration.as_seconds(span) -> int", + "tip": "The span in whole seconds.", + "kind": "namespace-method", + "page": "duration-as_seconds.html", + "section": "Duration" + }, + "duration-as_minutes": { + "name": "Duration.as_minutes", + "sig": "Duration.as_minutes(span) -> int", + "tip": "The span in whole minutes.", + "kind": "namespace-method", + "page": "duration-as_minutes.html", + "section": "Duration" + }, + "duration-as_hours": { + "name": "Duration.as_hours", + "sig": "Duration.as_hours(span) -> int", + "tip": "The span in whole hours.", + "kind": "namespace-method", + "page": "duration-as_hours.html", + "section": "Duration" + }, + "duration-as_days": { + "name": "Duration.as_days", + "sig": "Duration.as_days(span) -> int", + "tip": "The span in whole days.", + "kind": "namespace-method", + "page": "duration-as_days.html", + "section": "Duration" + }, + "ease-in": { + "name": "Ease.in", + "sig": "Ease.in(t) -> fixed", + "tip": "Accelerate from rest (quadratic ease-in).", + "kind": "namespace-method", + "page": "ease-in.html", + "section": "Ease" + }, + "ease-out": { + "name": "Ease.out", + "sig": "Ease.out(t) -> fixed", + "tip": "Decelerate to rest (quadratic ease-out).", + "kind": "namespace-method", + "page": "ease-out.html", + "section": "Ease" + }, + "ease-in_out": { + "name": "Ease.in_out", + "sig": "Ease.in_out(t) -> fixed", + "tip": "Ease in and then out (smoothstep).", + "kind": "namespace-method", + "page": "ease-in_out.html", + "section": "Ease" + }, + "ease-back": { + "name": "Ease.back", + "sig": "Ease.back(t) -> fixed", + "tip": "Ease in with a small backward anticipation.", + "kind": "namespace-method", + "page": "ease-back.html", + "section": "Ease" + }, + "ease-bounce": { + "name": "Ease.bounce", + "sig": "Ease.bounce(t) -> fixed", + "tip": "Ease out with a settling bounce.", + "kind": "namespace-method", + "page": "ease-bounce.html", + "section": "Ease" + }, + "ease-elastic": { + "name": "Ease.elastic", + "sig": "Ease.elastic(t) -> fixed", + "tip": "Ease out with a springy wobble.", + "kind": "namespace-method", + "page": "ease-elastic.html", + "section": "Ease" + }, + "fs-exists": { + "name": "Fs.exists", + "sig": "Fs.exists(p) -> bool", + "tip": "Does a path exist?", + "kind": "namespace-method", + "page": "fs-exists.html", + "section": "Fs" + }, + "fs-is_dir": { + "name": "Fs.is_dir", + "sig": "Fs.is_dir(p) -> bool", + "tip": "Is a path a directory?", + "kind": "namespace-method", + "page": "fs-is_dir.html", + "section": "Fs" + }, + "fs-read_text": { + "name": "Fs.read_text", + "sig": "Fs.read_text(p) -> string", + "tip": "Read a whole file as a string.", + "kind": "namespace-method", + "page": "fs-read_text.html", + "section": "Fs" + }, + "fs-write_text": { + "name": "Fs.write_text", + "sig": "Fs.write_text(p, s) -> bool", + "tip": "Write a string to a file (atomically).", + "kind": "namespace-method", + "page": "fs-write_text.html", + "section": "Fs" + }, + "fs-append_text": { + "name": "Fs.append_text", + "sig": "Fs.append_text(p, s) -> bool", + "tip": "Append a string to a file.", + "kind": "namespace-method", + "page": "fs-append_text.html", + "section": "Fs" + }, + "fs-remove": { + "name": "Fs.remove", + "sig": "Fs.remove(p) -> bool", + "tip": "Delete a file.", + "kind": "namespace-method", + "page": "fs-remove.html", + "section": "Fs" + }, + "fs-size": { + "name": "Fs.size", + "sig": "Fs.size(p) -> int", + "tip": "File size in bytes.", + "kind": "namespace-method", + "page": "fs-size.html", + "section": "Fs" + }, + "fs-mkdir": { + "name": "Fs.mkdir", + "sig": "Fs.mkdir(p) -> bool", + "tip": "Create a directory and its parents.", + "kind": "namespace-method", + "page": "fs-mkdir.html", + "section": "Fs" + }, + "fs-copy": { + "name": "Fs.copy", + "sig": "Fs.copy(src, dst) -> bool", + "tip": "Copy a file byte-for-byte.", + "kind": "namespace-method", + "page": "fs-copy.html", + "section": "Fs" + }, + "fs-list": { + "name": "Fs.list", + "sig": "Fs.list(dir) -> []string", + "tip": "Directory entries, sorted.", + "kind": "namespace-method", + "page": "fs-list.html", + "section": "Fs" + }, + "hash-of": { + "name": "Hash.of", + "sig": "Hash.of(s) -> int", + "tip": "The fast default string hash.", + "kind": "namespace-method", + "page": "hash-of.html", + "section": "Hash" + }, + "hash-fnv1a": { + "name": "Hash.fnv1a", + "sig": "Hash.fnv1a(s) -> int", + "tip": "FNV-1a 32-bit, named explicitly.", + "kind": "namespace-method", + "page": "hash-fnv1a.html", + "section": "Hash" + }, + "hash-crc32": { + "name": "Hash.crc32", + "sig": "Hash.crc32(s) -> int", + "tip": "CRC-32 checksum for corruption detection.", + "kind": "namespace-method", + "page": "hash-crc32.html", + "section": "Hash" + }, + "hash-mix": { + "name": "Hash.mix", + "sig": "Hash.mix(x) -> int", + "tip": "Avalanche one integer into a well-scrambled value.", + "kind": "namespace-method", + "page": "hash-mix.html", + "section": "Hash" + }, + "hash-combine": { + "name": "Hash.combine", + "sig": "Hash.combine(...) -> int", + "tip": "Fold several ints into one deterministic seed.", + "kind": "namespace-method", + "page": "hash-combine.html", + "section": "Hash" + }, + "hash-of64": { + "name": "Hash.of64", + "sig": "Hash.of64(s) -> long", + "tip": "The fast default 64-bit string hash.", + "kind": "namespace-method", + "page": "hash-of64.html", + "section": "Hash" + }, + "hash-fnv1a_64": { + "name": "Hash.fnv1a_64", + "sig": "Hash.fnv1a_64(s) -> long", + "tip": "FNV-1a 64-bit, named explicitly.", + "kind": "namespace-method", + "page": "hash-fnv1a_64.html", + "section": "Hash" + }, + "hash-mix64": { + "name": "Hash.mix64", + "sig": "Hash.mix64(x) -> long", + "tip": "Avalanche one 64-bit integer into a well-scrambled value.", + "kind": "namespace-method", + "page": "hash-mix64.html", + "section": "Hash" + }, + "input-key": { + "name": "Input.key", + "sig": "Input.key() -> int", + "tip": "The key pressed this frame as a character code (0 when nothing is pressed).", + "kind": "namespace-method", + "page": "input-key.html", + "section": "Input" + }, + "input-bind": { + "name": "Input.bind", + "sig": "Input.bind(action: str, key: int) -> void", + "tip": "Bind a physical key to a named action, so gameplay reads the action, not the key.", + "kind": "namespace-method", + "page": "input-bind.html", + "section": "Input" + }, + "input-rebind": { + "name": "Input.rebind", + "sig": "Input.rebind(action: str, from: int, to: int) -> void", + "tip": "Remap an action from one key to another at runtime (rebinding menus).", + "kind": "namespace-method", + "page": "input-rebind.html", + "section": "Input" + }, + "input-poll": { + "name": "Input.poll", + "sig": "Input.poll() -> int", + "tip": "Advance one frame of input; the single per-frame read behind actions and replay.", + "kind": "namespace-method", + "page": "input-poll.html", + "section": "Input" + }, + "input-down": { + "name": "Input.down", + "sig": "Input.down(action: str) -> bool", + "tip": "Is a named action held on the frame last polled?", + "kind": "namespace-method", + "page": "input-down.html", + "section": "Input" + }, + "input-pressed": { + "name": "Input.pressed", + "sig": "Input.pressed(action: str) -> bool", + "tip": "Did a named action go down this frame (a one-shot edge)?", + "kind": "namespace-method", + "page": "input-pressed.html", + "section": "Input" + }, + "input-record": { + "name": "Input.record", + "sig": "Input.record() -> void", + "tip": "Start recording polled input each frame (for deterministic replay).", + "kind": "namespace-method", + "page": "input-record.html", + "section": "Input" + }, + "input-replay": { + "name": "Input.replay", + "sig": "Input.replay() -> void", + "tip": "Replay recorded input; poll then reads the tape, not the device.", + "kind": "namespace-method", + "page": "input-replay.html", + "section": "Input" + }, + "input-key_down": { + "name": "Input.key_down", + "sig": "Input.key_down(key) -> bool", + "tip": "Is a physical key held this frame?", + "kind": "namespace-method", + "page": "input-key_down.html", + "section": "Input" + }, + "input-key_pressed": { + "name": "Input.key_pressed", + "sig": "Input.key_pressed(key) -> bool", + "tip": "Did a key go down this frame (edge)?", + "kind": "namespace-method", + "page": "input-key_pressed.html", + "section": "Input" + }, + "input-key_released": { + "name": "Input.key_released", + "sig": "Input.key_released(key) -> bool", + "tip": "Did a key go up this frame (edge)?", + "kind": "namespace-method", + "page": "input-key_released.html", + "section": "Input" + }, + "input-key_label": { + "name": "Input.key_label", + "sig": "Input.key_label(key) -> string", + "tip": "The name to show a player for a key - in their own keyboard layout.", + "kind": "namespace-method", + "page": "input-key_label.html", + "section": "Input" + }, + "input-press": { + "name": "Input.press", + "sig": "Input.press(key)", + "tip": "Inject a held key (AI, tutorial, testing, network).", + "kind": "namespace-method", + "page": "input-press.html", + "section": "Input" + }, + "input-release": { + "name": "Input.release", + "sig": "Input.release(key)", + "tip": "Release an injected key.", + "kind": "namespace-method", + "page": "input-release.html", + "section": "Input" + }, + "input-axis": { + "name": "Input.axis", + "sig": "Input.axis(neg, pos) -> fixed", + "tip": "A -1..+1 axis from two keys.", + "kind": "namespace-method", + "page": "input-axis.html", + "section": "Input" + }, + "input-axis_i": { + "name": "Input.axis_i", + "sig": "Input.axis_i(neg, pos) -> int", + "tip": "A -1/0/1 directional intent as a plain int, no bool->int glue.", + "kind": "namespace-method", + "page": "input-axis_i.html", + "section": "Input" + }, + "input-vector": { + "name": "Input.vector", + "sig": "Input.vector(left, right, up, down) -> Vector", + "tip": "A normalized 2D vector from four keys.", + "kind": "namespace-method", + "page": "input-vector.html", + "section": "Input" + }, + "input-strength": { + "name": "Input.strength", + "sig": "Input.strength(action) -> fixed", + "tip": "0..1 strength of a named action.", + "kind": "namespace-method", + "page": "input-strength.html", + "section": "Input" + }, + "input-mouse_x": { + "name": "Input.mouse_x", + "sig": "Input.mouse_x() -> int", + "tip": "The mouse x position this frame.", + "kind": "namespace-method", + "page": "input-mouse_x.html", + "section": "Input" + }, + "input-mouse_y": { + "name": "Input.mouse_y", + "sig": "Input.mouse_y() -> int", + "tip": "The mouse y position this frame.", + "kind": "namespace-method", + "page": "input-mouse_y.html", + "section": "Input" + }, + "input-mouse_dx": { + "name": "Input.mouse_dx", + "sig": "Input.mouse_dx() -> int", + "tip": "Mouse x movement since the last poll.", + "kind": "namespace-method", + "page": "input-mouse_dx.html", + "section": "Input" + }, + "input-mouse_dy": { + "name": "Input.mouse_dy", + "sig": "Input.mouse_dy() -> int", + "tip": "Mouse y movement since the last poll.", + "kind": "namespace-method", + "page": "input-mouse_dy.html", + "section": "Input" + }, + "input-mouse_down": { + "name": "Input.mouse_down", + "sig": "Input.mouse_down(button) -> bool", + "tip": "Is a mouse button held?", + "kind": "namespace-method", + "page": "input-mouse_down.html", + "section": "Input" + }, + "input-wheel": { + "name": "Input.wheel", + "sig": "Input.wheel() -> int", + "tip": "Scroll-wheel delta this frame.", + "kind": "namespace-method", + "page": "input-wheel.html", + "section": "Input" + }, + "input-set_mouse": { + "name": "Input.set_mouse", + "sig": "Input.set_mouse(x, y, buttons, wheel)", + "tip": "Inject the mouse state (headless / AI / testing).", + "kind": "namespace-method", + "page": "input-set_mouse.html", + "section": "Input" + }, + "input-pad_connected": { + "name": "Input.pad_connected", + "sig": "Input.pad_connected(pad) -> bool", + "tip": "Is a gamepad connected?", + "kind": "namespace-method", + "page": "input-pad_connected.html", + "section": "Input" + }, + "input-pad_button": { + "name": "Input.pad_button", + "sig": "Input.pad_button(pad, button) -> bool", + "tip": "Is a gamepad button held?", + "kind": "namespace-method", + "page": "input-pad_button.html", + "section": "Input" + }, + "input-pad_axis": { + "name": "Input.pad_axis", + "sig": "Input.pad_axis(pad, axis) -> fixed", + "tip": "A gamepad analog axis, -1..+1.", + "kind": "namespace-method", + "page": "input-pad_axis.html", + "section": "Input" + }, + "input-set_pad": { + "name": "Input.set_pad", + "sig": "Input.set_pad(pad, connected, buttons, lx, ly, rx, ry)", + "tip": "Inject a gamepad's whole state.", + "kind": "namespace-method", + "page": "input-set_pad.html", + "section": "Input" + }, + "input-touch_count": { + "name": "Input.touch_count", + "sig": "Input.touch_count() -> int", + "tip": "How many touch points are active?", + "kind": "namespace-method", + "page": "input-touch_count.html", + "section": "Input" + }, + "input-touch_x": { + "name": "Input.touch_x", + "sig": "Input.touch_x(index) -> int", + "tip": "The x of a touch point.", + "kind": "namespace-method", + "page": "input-touch_x.html", + "section": "Input" + }, + "input-touch_y": { + "name": "Input.touch_y", + "sig": "Input.touch_y(index) -> int", + "tip": "The y of a touch point.", + "kind": "namespace-method", + "page": "input-touch_y.html", + "section": "Input" + }, + "input-set_touch": { + "name": "Input.set_touch", + "sig": "Input.set_touch(index, x, y, active)", + "tip": "Inject a touch point.", + "kind": "namespace-method", + "page": "input-set_touch.html", + "section": "Input" + }, + "input-action": { + "name": "Input.action", + "sig": "Input.action(action: str, key: int) -> void", + "tip": "Register a default key binding for an action (kept if already bound).", + "kind": "namespace-method", + "page": "input-action.html", + "section": "Input" + }, + "input-bind_pad": { + "name": "Input.bind_pad", + "sig": "Input.bind_pad(action: str, button: int) -> void", + "tip": "Also fire a named action from a gamepad button (device-agnostic).", + "kind": "namespace-method", + "page": "input-bind_pad.html", + "section": "Input" + }, + "input-active": { + "name": "Input.active", + "sig": "Input.active(action: str) -> bool", + "tip": "Is a named action active right now (any bound key held, or pad button down)?", + "kind": "namespace-method", + "page": "input-active.html", + "section": "Input" + }, + "input-just_pressed": { + "name": "Input.just_pressed", + "sig": "Input.just_pressed(action: str) -> bool", + "tip": "Did a named action go active this frame (the deterministic on-press)?", + "kind": "namespace-method", + "page": "input-just_pressed.html", + "section": "Input" + }, + "input-just_released": { + "name": "Input.just_released", + "sig": "Input.just_released(action: str) -> bool", + "tip": "Did a named action go inactive this frame (the on-release edge)?", + "kind": "namespace-method", + "page": "input-just_released.html", + "section": "Input" + }, + "input-cursor_mode": { + "name": "Input.cursor_mode", + "sig": "Input.cursor_mode(mode)", + "tip": "Hide / lock / confine the OS mouse cursor to the window (windowed).", + "kind": "namespace-method", + "page": "input-cursor_mode.html", + "section": "Input" + }, + "input-move_i": { + "name": "Input.move_i", + "sig": "Input.move_i() -> IVec2", + "tip": "The standard top-down movement intent, -1/0/1 per axis.", + "kind": "namespace-method", + "page": "input-move_i.html", + "section": "Input" + }, + "list-len": { + "name": "List.len", + "sig": "List.len(s) -> int", + "tip": "The number of elements in a slice.", + "kind": "namespace-method", + "page": "list-len.html", + "section": "List" + }, + "list-push": { + "name": "List.push", + "sig": "List.push(s, v) -> void", + "tip": "Append an element to the end of a slice.", + "kind": "namespace-method", + "page": "list-push.html", + "section": "List" + }, + "list-clear": { + "name": "List.clear", + "sig": "List.clear(s) -> void", + "tip": "Remove all elements, keeping capacity.", + "kind": "namespace-method", + "page": "list-clear.html", + "section": "List" + }, + "list-first": { + "name": "List.first", + "sig": "List.first(s) -> T", + "tip": "The first element of a slice.", + "kind": "namespace-method", + "page": "list-first.html", + "section": "List" + }, + "list-last": { + "name": "List.last", + "sig": "List.last(s) -> T", + "tip": "The last element of a slice.", + "kind": "namespace-method", + "page": "list-last.html", + "section": "List" + }, + "list-pop": { + "name": "List.pop", + "sig": "List.pop(s) -> T", + "tip": "Remove and return the last element.", + "kind": "namespace-method", + "page": "list-pop.html", + "section": "List" + }, + "list-swap": { + "name": "List.swap", + "sig": "List.swap(s, i, j) -> void", + "tip": "Exchange the elements at two indices.", + "kind": "namespace-method", + "page": "list-swap.html", + "section": "List" + }, + "list-contains": { + "name": "List.contains", + "sig": "List.contains(s, v) -> bool", + "tip": "Whether a value appears in a slice.", + "kind": "namespace-method", + "page": "list-contains.html", + "section": "List" + }, + "list-index_of": { + "name": "List.index_of", + "sig": "List.index_of(s, v) -> int", + "tip": "The index of a value in a slice, or -1 if absent.", + "kind": "namespace-method", + "page": "list-index_of.html", + "section": "List" + }, + "list-reverse": { + "name": "List.reverse", + "sig": "List.reverse(s) -> void", + "tip": "Reverse the order of a slice in place.", + "kind": "namespace-method", + "page": "list-reverse.html", + "section": "List" + }, + "list-insert": { + "name": "List.insert", + "sig": "List.insert(s, i, v) -> void", + "tip": "Insert an element at an index, shifting the rest up.", + "kind": "namespace-method", + "page": "list-insert.html", + "section": "List" + }, + "list-remove_at": { + "name": "List.remove_at", + "sig": "List.remove_at(s, i) -> void", + "tip": "Remove the element at an index, shifting the rest down.", + "kind": "namespace-method", + "page": "list-remove_at.html", + "section": "List" + }, + "list-remove": { + "name": "List.remove", + "sig": "List.remove(s, v) -> void", + "tip": "Remove the first element equal to a value.", + "kind": "namespace-method", + "page": "list-remove.html", + "section": "List" + }, + "list-sort": { + "name": "List.sort", + "sig": "List.sort(s) -> void", + "tip": "Sort a slice in ascending order, in place.", + "kind": "namespace-method", + "page": "list-sort.html", + "section": "List" + }, + "list-sort_by": { + "name": "List.sort_by", + "sig": "List.sort_by(s, key) -> void", + "tip": "Sort a slice ascending by a key each element maps to.", + "kind": "namespace-method", + "page": "list-sort_by.html", + "section": "List" + }, + "list-sort_desc_by": { + "name": "List.sort_desc_by", + "sig": "List.sort_desc_by(s, key) -> void", + "tip": "Sort a slice descending by a key each element maps to.", + "kind": "namespace-method", + "page": "list-sort_desc_by.html", + "section": "List" + }, + "list-sort_with": { + "name": "List.sort_with", + "sig": "List.sort_with(s, cmp) -> void", + "tip": "Sort a slice with a full two-argument comparator.", + "kind": "namespace-method", + "page": "list-sort_with.html", + "section": "List" + }, + "list-sample": { + "name": "List.sample", + "sig": "List.sample(pool, count) -> []int", + "tip": "Random picks from an int slice, distinct while it can.", + "kind": "namespace-method", + "page": "list-sample.html", + "section": "List" + }, + "log-trace": { + "name": "Log.trace", + "sig": "Log.trace(msg, [key, value]...) -> void", + "tip": "Log at the trace level (0) — the most verbose.", + "kind": "namespace-method", + "page": "log-trace.html", + "section": "Log" + }, + "log-debug": { + "name": "Log.debug", + "sig": "Log.debug(msg, [key, value]...) -> void", + "tip": "Log at the debug level (1) — development detail.", + "kind": "namespace-method", + "page": "log-debug.html", + "section": "Log" + }, + "log-info": { + "name": "Log.info", + "sig": "Log.info(msg, [key, value]...) -> void", + "tip": "Log at the info level (2) — normal operation.", + "kind": "namespace-method", + "page": "log-info.html", + "section": "Log" + }, + "log-warn": { + "name": "Log.warn", + "sig": "Log.warn(msg, [key, value]...) -> void", + "tip": "Log at the warn level (3) — something looks wrong.", + "kind": "namespace-method", + "page": "log-warn.html", + "section": "Log" + }, + "log-error": { + "name": "Log.error", + "sig": "Log.error(msg, [key, value]...) -> void", + "tip": "Log at the error level (4) — a failure.", + "kind": "namespace-method", + "page": "log-error.html", + "section": "Log" + }, + "log-set_level": { + "name": "Log.set_level", + "sig": "Log.set_level(n) -> void", + "tip": "Show only messages at level n or above (0 = all).", + "kind": "namespace-method", + "page": "log-set_level.html", + "section": "Log" + }, + "log-level": { + "name": "Log.level", + "sig": "Log.level() -> int", + "tip": "The current logging threshold.", + "kind": "namespace-method", + "page": "log-level.html", + "section": "Log" + }, + "math-min": { + "name": "Math.min", + "sig": "Math.min(a, b) -> int", + "tip": "The smaller of two values.", + "kind": "namespace-method", + "page": "math-min.html", + "section": "Math" + }, + "math-max": { + "name": "Math.max", + "sig": "Math.max(a, b) -> int", + "tip": "The larger of two values.", + "kind": "namespace-method", + "page": "math-max.html", + "section": "Math" + }, + "math-abs": { + "name": "Math.abs", + "sig": "Math.abs(x) -> int", + "tip": "The magnitude of a value, dropping its sign.", + "kind": "namespace-method", + "page": "math-abs.html", + "section": "Math" + }, + "math-clamp": { + "name": "Math.clamp", + "sig": "Math.clamp(v, lo, hi) -> int", + "tip": "Constrain a value to the range [lo, hi].", + "kind": "namespace-method", + "page": "math-clamp.html", + "section": "Math" + }, + "math-sign": { + "name": "Math.sign", + "sig": "Math.sign(x) -> int", + "tip": "The sign of a value as -1, 0, or 1.", + "kind": "namespace-method", + "page": "math-sign.html", + "section": "Math" + }, + "math-floor": { + "name": "Math.floor", + "sig": "Math.floor(x) -> int", + "tip": "Round a fixed value down to the nearest whole int.", + "kind": "namespace-method", + "page": "math-floor.html", + "section": "Math" + }, + "math-ceil": { + "name": "Math.ceil", + "sig": "Math.ceil(x) -> int", + "tip": "Round a fixed value up to the nearest whole int.", + "kind": "namespace-method", + "page": "math-ceil.html", + "section": "Math" + }, + "math-round": { + "name": "Math.round", + "sig": "Math.round(x) -> int", + "tip": "Round a fixed value to the nearest whole int.", + "kind": "namespace-method", + "page": "math-round.html", + "section": "Math" + }, + "math-lerp": { + "name": "Math.lerp", + "sig": "Math.lerp(a, b, t) -> fixed", + "tip": "Blend between two values by a 0..1 amount.", + "kind": "namespace-method", + "page": "math-lerp.html", + "section": "Math" + }, + "math-inverse_lerp": { + "name": "Math.inverse_lerp", + "sig": "Math.inverse_lerp(a, b, v) -> fixed", + "tip": "Find where a value sits between two endpoints as a 0..1 fraction.", + "kind": "namespace-method", + "page": "math-inverse_lerp.html", + "section": "Math" + }, + "math-remap": { + "name": "Math.remap", + "sig": "Math.remap(v, in0, in1, out0, out1) -> fixed", + "tip": "Rescale a value from one range into another.", + "kind": "namespace-method", + "page": "math-remap.html", + "section": "Math" + }, + "math-sqrt": { + "name": "Math.sqrt", + "sig": "Math.sqrt(x) -> fixed", + "tip": "The square root of a fixed value.", + "kind": "namespace-method", + "page": "math-sqrt.html", + "section": "Math" + }, + "math-sin": { + "name": "Math.sin", + "sig": "Math.sin(radians) -> fixed", + "tip": "Sine of an angle in radians.", + "kind": "namespace-method", + "page": "math-sin.html", + "section": "Math" + }, + "math-cos": { + "name": "Math.cos", + "sig": "Math.cos(radians) -> fixed", + "tip": "Cosine of an angle in radians.", + "kind": "namespace-method", + "page": "math-cos.html", + "section": "Math" + }, + "math-tan": { + "name": "Math.tan", + "sig": "Math.tan(radians) -> fixed", + "tip": "Tangent of an angle in radians.", + "kind": "namespace-method", + "page": "math-tan.html", + "section": "Math" + }, + "math-hypot": { + "name": "Math.hypot", + "sig": "Math.hypot(x, y) -> fixed", + "tip": "Length of the vector (x, y).", + "kind": "namespace-method", + "page": "math-hypot.html", + "section": "Math" + }, + "math-dist": { + "name": "Math.dist", + "sig": "Math.dist(x0, y0, x1, y1) -> fixed", + "tip": "Distance between two points.", + "kind": "namespace-method", + "page": "math-dist.html", + "section": "Math" + }, + "math-dist2": { + "name": "Math.dist2", + "sig": "Math.dist2(x0, y0, x1, y1) -> fixed", + "tip": "Squared distance between two points.", + "kind": "namespace-method", + "page": "math-dist2.html", + "section": "Math" + }, + "math-deg_to_rad": { + "name": "Math.deg_to_rad", + "sig": "Math.deg_to_rad(degrees) -> fixed", + "tip": "Convert degrees to radians.", + "kind": "namespace-method", + "page": "math-deg_to_rad.html", + "section": "Math" + }, + "math-rad_to_deg": { + "name": "Math.rad_to_deg", + "sig": "Math.rad_to_deg(radians) -> fixed", + "tip": "Convert radians to degrees.", + "kind": "namespace-method", + "page": "math-rad_to_deg.html", + "section": "Math" + }, + "math-posmod": { + "name": "Math.posmod", + "sig": "Math.posmod(a, m) -> int", + "tip": "Modulo that is always non-negative.", + "kind": "namespace-method", + "page": "math-posmod.html", + "section": "Math" + }, + "math-wrap": { + "name": "Math.wrap", + "sig": "Math.wrap(v, lo, hi) -> int", + "tip": "Wrap a value into the range [lo, hi).", + "kind": "namespace-method", + "page": "math-wrap.html", + "section": "Math" + }, + "math-ping_pong": { + "name": "Math.ping_pong", + "sig": "Math.ping_pong(t, len) -> int", + "tip": "Bounce a counter back and forth in [0, len].", + "kind": "namespace-method", + "page": "math-ping_pong.html", + "section": "Math" + }, + "math-snapped": { + "name": "Math.snapped", + "sig": "Math.snapped(v, step) -> fixed", + "tip": "Round a value to the nearest multiple of step.", + "kind": "namespace-method", + "page": "math-snapped.html", + "section": "Math" + }, + "math-move_toward": { + "name": "Math.move_toward", + "sig": "Math.move_toward(from, to, delta) -> fixed", + "tip": "Step from one value toward another by at most delta.", + "kind": "namespace-method", + "page": "math-move_toward.html", + "section": "Math" + }, + "math-smoothstep": { + "name": "Math.smoothstep", + "sig": "Math.smoothstep(e0, e1, x) -> fixed", + "tip": "A smooth 0..1 ramp between two edges.", + "kind": "namespace-method", + "page": "math-smoothstep.html", + "section": "Math" + }, + "math-atan2": { + "name": "Math.atan2", + "sig": "Math.atan2(y, x) -> fixed", + "tip": "The angle of the vector (x, y), in radians.", + "kind": "namespace-method", + "page": "math-atan2.html", + "section": "Math" + }, + "math-asin": { + "name": "Math.asin", + "sig": "Math.asin(x) -> fixed", + "tip": "The arcsine of a value, in radians.", + "kind": "namespace-method", + "page": "math-asin.html", + "section": "Math" + }, + "math-acos": { + "name": "Math.acos", + "sig": "Math.acos(x) -> fixed", + "tip": "The arccosine of a value, in radians.", + "kind": "namespace-method", + "page": "math-acos.html", + "section": "Math" + }, + "math-exp": { + "name": "Math.exp", + "sig": "Math.exp(x) -> fixed", + "tip": "e raised to the power x.", + "kind": "namespace-method", + "page": "math-exp.html", + "section": "Math" + }, + "math-log": { + "name": "Math.log", + "sig": "Math.log(x) -> fixed", + "tip": "The natural logarithm of x.", + "kind": "namespace-method", + "page": "math-log.html", + "section": "Math" + }, + "math-pow": { + "name": "Math.pow", + "sig": "Math.pow(base, exp) -> fixed", + "tip": "base raised to a fixed exponent.", + "kind": "namespace-method", + "page": "math-pow.html", + "section": "Math" + }, + "memory-bytes": { + "name": "Memory.bytes", + "sig": "Memory.bytes(n) -> pointer", + "tip": "Allocate n bytes.", + "kind": "namespace-method", + "page": "memory-bytes.html", + "section": "Memory" + }, + "memory-words": { + "name": "Memory.words", + "sig": "Memory.words(n) -> words", + "tip": "Allocate n 32-bit words.", + "kind": "namespace-method", + "page": "memory-words.html", + "section": "Memory" + }, + "memory-copy": { + "name": "Memory.copy", + "sig": "Memory.copy(dst, src, n) -> void", + "tip": "Copy n bytes between buffers.", + "kind": "namespace-method", + "page": "memory-copy.html", + "section": "Memory" + }, + "memory-fill": { + "name": "Memory.fill", + "sig": "Memory.fill(buf, value, n) -> void", + "tip": "Set n bytes to a value.", + "kind": "namespace-method", + "page": "memory-fill.html", + "section": "Memory" + }, + "memory-peek": { + "name": "Memory.peek", + "sig": "Memory.peek(buf, i) -> int", + "tip": "Read one byte.", + "kind": "namespace-method", + "page": "memory-peek.html", + "section": "Memory" + }, + "memory-poke": { + "name": "Memory.poke", + "sig": "Memory.poke(buf, i, value) -> void", + "tip": "Write one byte.", + "kind": "namespace-method", + "page": "memory-poke.html", + "section": "Memory" + }, + "mime-of": { + "name": "Mime.of", + "sig": "Mime.of(path) -> string", + "tip": "MIME type from a file extension.", + "kind": "namespace-method", + "page": "mime-of.html", + "section": "Mime" + }, + "mime-sniff": { + "name": "Mime.sniff", + "sig": "Mime.sniff(path) -> string", + "tip": "MIME type refined by magic bytes.", + "kind": "namespace-method", + "page": "mime-sniff.html", + "section": "Mime" + }, + "network-send": { + "name": "Network.send", + "sig": "Network.send(peer, buf, len) -> void", + "tip": "Put a datagram on the wire.", + "kind": "namespace-method", + "page": "network-send.html", + "section": "Network" + }, + "network-poll": { + "name": "Network.poll", + "sig": "Network.poll(buf, cap) -> int", + "tip": "Read an inbound datagram.", + "kind": "namespace-method", + "page": "network-poll.html", + "section": "Network" + }, + "network-serialize": { + "name": "Network.serialize", + "sig": "Network.serialize(entity, buf) -> int", + "tip": "Serialize an entity's synced state.", + "kind": "namespace-method", + "page": "network-serialize.html", + "section": "Network" + }, + "network-apply": { + "name": "Network.apply", + "sig": "Network.apply(entity, buf) -> int", + "tip": "Apply serialized state to an entity.", + "kind": "namespace-method", + "page": "network-apply.html", + "section": "Network" + }, + "network-owner": { + "name": "Network.owner", + "sig": "Network.owner(entity) -> int", + "tip": "The peer that owns an entity.", + "kind": "namespace-method", + "page": "network-owner.html", + "section": "Network" + }, + "network-set_owner": { + "name": "Network.set_owner", + "sig": "Network.set_owner(entity, peer) -> void", + "tip": "Assign ownership of an entity.", + "kind": "namespace-method", + "page": "network-set_owner.html", + "section": "Network" + }, + "network-is_server": { + "name": "Network.is_server", + "sig": "Network.is_server() -> bool", + "tip": "Is this peer the server?", + "kind": "namespace-method", + "page": "network-is_server.html", + "section": "Network" + }, + "network-is_owner": { + "name": "Network.is_owner", + "sig": "Network.is_owner(entity) -> bool", + "tip": "Does this peer own the entity?", + "kind": "namespace-method", + "page": "network-is_owner.html", + "section": "Network" + }, + "network-local_id": { + "name": "Network.local_id", + "sig": "Network.local_id() -> int", + "tip": "This peer's own id.", + "kind": "namespace-method", + "page": "network-local_id.html", + "section": "Network" + }, + "noise-value2": { + "name": "Noise.value2", + "sig": "Noise.value2(x, y, seed) -> fixed", + "tip": "2D value (lattice) noise, deterministic, in [-1, 1].", + "kind": "namespace-method", + "page": "noise-value2.html", + "section": "Noise" + }, + "noise-perlin2": { + "name": "Noise.perlin2", + "sig": "Noise.perlin2(x, y, seed) -> fixed", + "tip": "2D Perlin gradient noise, deterministic, in [-1, 1].", + "kind": "namespace-method", + "page": "noise-perlin2.html", + "section": "Noise" + }, + "noise-simplex2": { + "name": "Noise.simplex2", + "sig": "Noise.simplex2(x, y, seed) -> fixed", + "tip": "2D simplex noise, the organic default, in [-1, 1].", + "kind": "namespace-method", + "page": "noise-simplex2.html", + "section": "Noise" + }, + "noise-fbm2": { + "name": "Noise.fbm2", + "sig": "Noise.fbm2(x, y, seed, octaves) -> fixed", + "tip": "Fractal Brownian motion — octaves of simplex, in [-1, 1].", + "kind": "namespace-method", + "page": "noise-fbm2.html", + "section": "Noise" + }, + "noise-cellular2": { + "name": "Noise.cellular2", + "sig": "Noise.cellular2(x, y, seed) -> fixed", + "tip": "Worley (cellular) F1 distance to the nearest cell point.", + "kind": "namespace-method", + "page": "noise-cellular2.html", + "section": "Noise" + }, + "noise-cellular2_id": { + "name": "Noise.cellular2_id", + "sig": "Noise.cellular2_id(x, y, seed) -> int", + "tip": "The id of the nearest Worley cell — stable per cell.", + "kind": "namespace-method", + "page": "noise-cellular2_id.html", + "section": "Noise" + }, + "noise-unit": { + "name": "Noise.unit", + "sig": "Noise.unit(n) -> fixed", + "tip": "Remap a [-1,1] noise sample to [0,1].", + "kind": "namespace-method", + "page": "noise-unit.html", + "section": "Noise" + }, + "os-args": { + "name": "Os.args", + "sig": "Os.args() -> []string", + "tip": "Every command-line argument, as a list.", + "kind": "namespace-method", + "page": "os-args.html", + "section": "Os" + }, + "os-arg_count": { + "name": "Os.arg_count", + "sig": "Os.arg_count() -> int", + "tip": "How many command-line arguments there are.", + "kind": "namespace-method", + "page": "os-arg_count.html", + "section": "Os" + }, + "os-arg": { + "name": "Os.arg", + "sig": "Os.arg(i) -> string", + "tip": "The i-th command-line argument.", + "kind": "namespace-method", + "page": "os-arg.html", + "section": "Os" + }, + "os-env": { + "name": "Os.env", + "sig": "Os.env(name) -> string", + "tip": "Read an environment variable (null if unset).", + "kind": "namespace-method", + "page": "os-env.html", + "section": "Os" + }, + "os-env_or": { + "name": "Os.env_or", + "sig": "Os.env_or(name, fallback) -> string", + "tip": "Read an environment variable, or a fallback.", + "kind": "namespace-method", + "page": "os-env_or.html", + "section": "Os" + }, + "os-has_env": { + "name": "Os.has_env", + "sig": "Os.has_env(name) -> bool", + "tip": "Is an environment variable set?", + "kind": "namespace-method", + "page": "os-has_env.html", + "section": "Os" + }, + "os-set_env": { + "name": "Os.set_env", + "sig": "Os.set_env(name, value) -> bool", + "tip": "Set an environment variable.", + "kind": "namespace-method", + "page": "os-set_env.html", + "section": "Os" + }, + "os-unset_env": { + "name": "Os.unset_env", + "sig": "Os.unset_env(name) -> bool", + "tip": "Remove an environment variable.", + "kind": "namespace-method", + "page": "os-unset_env.html", + "section": "Os" + }, + "os-exit": { + "name": "Os.exit", + "sig": "Os.exit(code)", + "tip": "Terminate the process with a status code.", + "kind": "namespace-method", + "page": "os-exit.html", + "section": "Os" + }, + "os-platform": { + "name": "Os.platform", + "sig": "Os.platform() -> string", + "tip": "The host platform id.", + "kind": "namespace-method", + "page": "os-platform.html", + "section": "Os" + }, + "os-arch": { + "name": "Os.arch", + "sig": "Os.arch() -> string", + "tip": "The host CPU architecture.", + "kind": "namespace-method", + "page": "os-arch.html", + "section": "Os" + }, + "os-stdout_write": { + "name": "Os.stdout_write", + "sig": "Os.stdout_write(s)", + "tip": "Write a string to standard output.", + "kind": "namespace-method", + "page": "os-stdout_write.html", + "section": "Os" + }, + "os-stderr_write": { + "name": "Os.stderr_write", + "sig": "Os.stderr_write(s)", + "tip": "Write a string to standard error.", + "kind": "namespace-method", + "page": "os-stderr_write.html", + "section": "Os" + }, + "os-save_dir": { + "name": "Os.save_dir", + "sig": "Os.save_dir(app) -> string", + "tip": "Per-user save directory for an app.", + "kind": "namespace-method", + "page": "os-save_dir.html", + "section": "Os" + }, + "os-config_dir": { + "name": "Os.config_dir", + "sig": "Os.config_dir(app) -> string", + "tip": "Per-user config directory for an app.", + "kind": "namespace-method", + "page": "os-config_dir.html", + "section": "Os" + }, + "os-cache_dir": { + "name": "Os.cache_dir", + "sig": "Os.cache_dir(app) -> string", + "tip": "Per-user cache directory for an app.", + "kind": "namespace-method", + "page": "os-cache_dir.html", + "section": "Os" + }, + "os-temp_dir": { + "name": "Os.temp_dir", + "sig": "Os.temp_dir() -> string", + "tip": "The system temporary directory.", + "kind": "namespace-method", + "page": "os-temp_dir.html", + "section": "Os" + }, + "os-pid": { + "name": "Os.pid", + "sig": "Os.pid() -> int", + "tip": "This process's id.", + "kind": "namespace-method", + "page": "os-pid.html", + "section": "Os" + }, + "path-join": { + "name": "Path.join", + "sig": "Path.join(a, b) -> string", + "tip": "Join two path segments with the separator.", + "kind": "namespace-method", + "page": "path-join.html", + "section": "Path" + }, + "path-dir": { + "name": "Path.dir", + "sig": "Path.dir(p) -> string", + "tip": "The directory part of a path.", + "kind": "namespace-method", + "page": "path-dir.html", + "section": "Path" + }, + "path-base": { + "name": "Path.base", + "sig": "Path.base(p) -> string", + "tip": "The final component of a path.", + "kind": "namespace-method", + "page": "path-base.html", + "section": "Path" + }, + "path-ext": { + "name": "Path.ext", + "sig": "Path.ext(p) -> string", + "tip": "The file extension, including the dot.", + "kind": "namespace-method", + "page": "path-ext.html", + "section": "Path" + }, + "path-stem": { + "name": "Path.stem", + "sig": "Path.stem(p) -> string", + "tip": "The final component without its extension.", + "kind": "namespace-method", + "page": "path-stem.html", + "section": "Path" + }, + "path-normalize": { + "name": "Path.normalize", + "sig": "Path.normalize(p) -> string", + "tip": "Collapse . / .. / duplicate separators.", + "kind": "namespace-method", + "page": "path-normalize.html", + "section": "Path" + }, + "save-write": { + "name": "Save.write", + "sig": "Save.write() -> void", + "tip": "Save a snapshot of the world.", + "kind": "namespace-method", + "page": "save-write.html", + "section": "Save" + }, + "save-read": { + "name": "Save.read", + "sig": "Save.read() -> bool", + "tip": "Restore the saved snapshot.", + "kind": "namespace-method", + "page": "save-read.html", + "section": "Save" + }, + "system-run": { + "name": "System.run", + "sig": "System.run(command) -> int", + "tip": "Run a shell command.", + "kind": "namespace-method", + "page": "system-run.html", + "section": "System" + }, + "system-read_char": { + "name": "System.read_char", + "sig": "System.read_char() -> int", + "tip": "Read one byte from standard input.", + "kind": "namespace-method", + "page": "system-read_char.html", + "section": "System" + }, + "system-file_open": { + "name": "System.file_open", + "sig": "System.file_open(path, mode) -> pointer", + "tip": "Open a file.", + "kind": "namespace-method", + "page": "system-file_open.html", + "section": "System" + }, + "system-file_read": { + "name": "System.file_read", + "sig": "System.file_read(file, buf, n) -> int", + "tip": "Read bytes from a file.", + "kind": "namespace-method", + "page": "system-file_read.html", + "section": "System" + }, + "system-file_write": { + "name": "System.file_write", + "sig": "System.file_write(file, buf, n) -> int", + "tip": "Write bytes to a file.", + "kind": "namespace-method", + "page": "system-file_write.html", + "section": "System" + }, + "system-file_seek": { + "name": "System.file_seek", + "sig": "System.file_seek(file, offset, whence) -> int", + "tip": "Move a file's read/write position.", + "kind": "namespace-method", + "page": "system-file_seek.html", + "section": "System" + }, + "system-file_tell": { + "name": "System.file_tell", + "sig": "System.file_tell(file) -> int", + "tip": "The current file position.", + "kind": "namespace-method", + "page": "system-file_tell.html", + "section": "System" + }, + "system-file_close": { + "name": "System.file_close", + "sig": "System.file_close(file) -> void", + "tip": "Close a file.", + "kind": "namespace-method", + "page": "system-file_close.html", + "section": "System" + }, + "text-length": { + "name": "Text.length", + "sig": "Text.length(s) -> int", + "tip": "The number of bytes in a string.", + "kind": "namespace-method", + "page": "text-length.html", + "section": "Text" + }, + "text-char_at": { + "name": "Text.char_at", + "sig": "Text.char_at(s, i) -> int", + "tip": "The byte value at an index, 0..255.", + "kind": "namespace-method", + "page": "text-char_at.html", + "section": "Text" + }, + "text-slice": { + "name": "Text.slice", + "sig": "Text.slice(s, a, b) -> string", + "tip": "A substring covering bytes [a, b).", + "kind": "namespace-method", + "page": "text-slice.html", + "section": "Text" + }, + "text-starts_with": { + "name": "Text.starts_with", + "sig": "Text.starts_with(s, prefix) -> bool", + "tip": "Whether a string begins with a given prefix.", + "kind": "namespace-method", + "page": "text-starts_with.html", + "section": "Text" + }, + "text-ends_with": { + "name": "Text.ends_with", + "sig": "Text.ends_with(s, suffix) -> bool", + "tip": "Whether a string ends with a given suffix.", + "kind": "namespace-method", + "page": "text-ends_with.html", + "section": "Text" + }, + "text-contains": { + "name": "Text.contains", + "sig": "Text.contains(s, sub) -> bool", + "tip": "Whether a substring appears anywhere in a string.", + "kind": "namespace-method", + "page": "text-contains.html", + "section": "Text" + }, + "text-index_of": { + "name": "Text.index_of", + "sig": "Text.index_of(s, sub) -> int", + "tip": "The byte index of a substring, or -1 if absent.", + "kind": "namespace-method", + "page": "text-index_of.html", + "section": "Text" + }, + "text-equals": { + "name": "Text.equals", + "sig": "Text.equals(a, b) -> bool", + "tip": "Whether two strings have identical bytes.", + "kind": "namespace-method", + "page": "text-equals.html", + "section": "Text" + }, + "text-concat": { + "name": "Text.concat", + "sig": "Text.concat(a, b) -> string", + "tip": "Join two strings into a new one.", + "kind": "namespace-method", + "page": "text-concat.html", + "section": "Text" + }, + "text-to_int": { + "name": "Text.to_int", + "sig": "Text.to_int(s) -> int", + "tip": "Parse a leading integer from a string.", + "kind": "namespace-method", + "page": "text-to_int.html", + "section": "Text" + }, + "text-from_int": { + "name": "Text.from_int", + "sig": "Text.from_int(n) -> string", + "tip": "Render an integer as a string.", + "kind": "namespace-method", + "page": "text-from_int.html", + "section": "Text" + }, + "text-upper": { + "name": "Text.upper", + "sig": "Text.upper(s) -> string", + "tip": "An uppercased copy of a string.", + "kind": "namespace-method", + "page": "text-upper.html", + "section": "Text" + }, + "text-lower": { + "name": "Text.lower", + "sig": "Text.lower(s) -> string", + "tip": "A lowercased copy of a string.", + "kind": "namespace-method", + "page": "text-lower.html", + "section": "Text" + }, + "text-trim": { + "name": "Text.trim", + "sig": "Text.trim(s) -> string", + "tip": "A copy with surrounding whitespace removed.", + "kind": "namespace-method", + "page": "text-trim.html", + "section": "Text" + }, + "text-repeat": { + "name": "Text.repeat", + "sig": "Text.repeat(s, n) -> string", + "tip": "A string repeated n times.", + "kind": "namespace-method", + "page": "text-repeat.html", + "section": "Text" + }, + "text-pad_left": { + "name": "Text.pad_left", + "sig": "Text.pad_left(s, width) -> string", + "tip": "Pad with spaces on the left to a width.", + "kind": "namespace-method", + "page": "text-pad_left.html", + "section": "Text" + }, + "text-pad_right": { + "name": "Text.pad_right", + "sig": "Text.pad_right(s, width) -> string", + "tip": "Pad with spaces on the right to a width.", + "kind": "namespace-method", + "page": "text-pad_right.html", + "section": "Text" + }, + "text-split": { + "name": "Text.split", + "sig": "Text.split(s, sep) -> []string", + "tip": "Split a string on a separator into a list.", + "kind": "namespace-method", + "page": "text-split.html", + "section": "Text" + }, + "text-join": { + "name": "Text.join", + "sig": "Text.join(parts, sep) -> string", + "tip": "Join a list of strings with a separator.", + "kind": "namespace-method", + "page": "text-join.html", + "section": "Text" + }, + "text-replace": { + "name": "Text.replace", + "sig": "Text.replace(s, from, to) -> string", + "tip": "Replace every occurrence of a substring.", + "kind": "namespace-method", + "page": "text-replace.html", + "section": "Text" + }, + "time-frame": { + "name": "Time.frame", + "sig": "Time.frame() -> int", + "tip": "The number of frames elapsed since start.", + "kind": "namespace-method", + "page": "time-frame.html", + "section": "Time" + }, + "time-delta": { + "name": "Time.delta", + "sig": "Time.delta() -> fixed", + "tip": "Seconds per frame (a fixed 1/60).", + "kind": "namespace-method", + "page": "time-delta.html", + "section": "Time" + }, + "time-elapsed": { + "name": "Time.elapsed", + "sig": "Time.elapsed() -> fixed", + "tip": "Seconds since start (deterministic).", + "kind": "namespace-method", + "page": "time-elapsed.html", + "section": "Time" + }, + "time-now": { + "name": "Time.now", + "sig": "Time.now() -> int", + "tip": "Wall-clock seconds (non-deterministic).", + "kind": "namespace-method", + "page": "time-now.html", + "section": "Time" + }, + "time-since": { + "name": "Time.since", + "sig": "Time.since(past) -> int", + "tip": "Seconds elapsed from a past instant to now (non-deterministic).", + "kind": "namespace-method", + "page": "time-since.html", + "section": "Time" + }, + "unicode-len": { + "name": "Unicode.len", + "sig": "Unicode.len(s) -> int", + "tip": "Number of code points in a string (not bytes).", + "kind": "namespace-method", + "page": "unicode-len.html", + "section": "Unicode" + }, + "unicode-byte_len": { + "name": "Unicode.byte_len", + "sig": "Unicode.byte_len(s) -> int", + "tip": "Number of bytes in a string.", + "kind": "namespace-method", + "page": "unicode-byte_len.html", + "section": "Unicode" + }, + "unicode-is_valid_utf8": { + "name": "Unicode.is_valid_utf8", + "sig": "Unicode.is_valid_utf8(s) -> bool", + "tip": "Is a string well-formed UTF-8?", + "kind": "namespace-method", + "page": "unicode-is_valid_utf8.html", + "section": "Unicode" + }, + "unicode-char_at": { + "name": "Unicode.char_at", + "sig": "Unicode.char_at(s, i) -> int", + "tip": "The i-th code point of a string.", + "kind": "namespace-method", + "page": "unicode-char_at.html", + "section": "Unicode" + }, + "unicode-chars": { + "name": "Unicode.chars", + "sig": "Unicode.chars(s) -> []int", + "tip": "Every code point of a string, in order.", + "kind": "namespace-method", + "page": "unicode-chars.html", + "section": "Unicode" + }, + "unicode-upper": { + "name": "Unicode.upper", + "sig": "Unicode.upper(s) -> string", + "tip": "Uppercase a string.", + "kind": "namespace-method", + "page": "unicode-upper.html", + "section": "Unicode" + }, + "unicode-lower": { + "name": "Unicode.lower", + "sig": "Unicode.lower(s) -> string", + "tip": "Lowercase a string.", + "kind": "namespace-method", + "page": "unicode-lower.html", + "section": "Unicode" + }, + "unicode-truncate": { + "name": "Unicode.truncate", + "sig": "Unicode.truncate(s, n) -> string", + "tip": "First n code points of a string.", + "kind": "namespace-method", + "page": "unicode-truncate.html", + "section": "Unicode" + }, + "unicode-grapheme_len": { + "name": "Unicode.grapheme_len", + "sig": "Unicode.grapheme_len(s) -> int", + "tip": "Number of user-perceived characters.", + "kind": "namespace-method", + "page": "unicode-grapheme_len.html", + "section": "Unicode" + }, + "uuid-new": { + "name": "Uuid.new", + "sig": "Uuid.new() -> string", + "tip": "A new random (v4) UUID as a canonical 36-char string.", + "kind": "namespace-method", + "page": "uuid-new.html", + "section": "Uuid" + }, + "uuid-v4": { + "name": "Uuid.v4", + "sig": "Uuid.v4() -> string", + "tip": "Explicit alias for Uuid.new — a random (v4) UUID.", + "kind": "namespace-method", + "page": "uuid-v4.html", + "section": "Uuid" + }, + "uuid-new_v7": { + "name": "Uuid.new_v7", + "sig": "Uuid.new_v7() -> string", + "tip": "A new time-ordered (v7) UUID; sorts by creation time.", + "kind": "namespace-method", + "page": "uuid-new_v7.html", + "section": "Uuid" + }, + "uuid-v7": { + "name": "Uuid.v7", + "sig": "Uuid.v7() -> string", + "tip": "Explicit alias for Uuid.new_v7 — a time-ordered UUID.", + "kind": "namespace-method", + "page": "uuid-v7.html", + "section": "Uuid" + }, + "uuid-parse": { + "name": "Uuid.parse", + "sig": "Uuid.parse(s) -> string", + "tip": "Normalise an untrusted string to a lowercase UUID, or the nil UUID.", + "kind": "namespace-method", + "page": "uuid-parse.html", + "section": "Uuid" + }, + "uuid-is_valid": { + "name": "Uuid.is_valid", + "sig": "Uuid.is_valid(s) -> bool", + "tip": "Is s a well-formed UUID string?", + "kind": "namespace-method", + "page": "uuid-is_valid.html", + "section": "Uuid" + }, + "uuid-to_text": { + "name": "Uuid.to_text", + "sig": "Uuid.to_text(id) -> string", + "tip": "The canonical text form of a UUID.", + "kind": "namespace-method", + "page": "uuid-to_text.html", + "section": "Uuid" + }, + "uuid-equals": { + "name": "Uuid.equals", + "sig": "Uuid.equals(a, b) -> bool", + "tip": "Case-insensitive UUID equality.", + "kind": "namespace-method", + "page": "uuid-equals.html", + "section": "Uuid" + }, + "uuid-nil": { + "name": "Uuid.nil", + "sig": "Uuid.nil() -> string", + "tip": "The all-zero UUID.", + "kind": "namespace-method", + "page": "uuid-nil.html", + "section": "Uuid" + }, + "world-get": { + "name": "World.get", + "sig": "World.get(prop_id, field_id, entity) -> int", + "tip": "Read a component field by id.", + "kind": "namespace-method", + "page": "world-get.html", + "section": "World" + }, + "world-set": { + "name": "World.set", + "sig": "World.set(prop_id, field_id, entity, value) -> void", + "tip": "Write a component field by id.", + "kind": "namespace-method", + "page": "world-set.html", + "section": "World" + }, + "world-has": { + "name": "World.has", + "sig": "World.has(entity, prop_id) -> int", + "tip": "Does an entity have a component?", + "kind": "namespace-method", + "page": "world-has.html", + "section": "World" + }, + "world-count": { + "name": "World.count", + "sig": "World.count() -> int", + "tip": "The number of live entities.", + "kind": "namespace-method", + "page": "world-count.html", + "section": "World" + }, + "world-size": { + "name": "World.size", + "sig": "World.size() -> int", + "tip": "The serialized size of the world.", + "kind": "namespace-method", + "page": "world-size.html", + "section": "World" + }, + "world-spawn": { + "name": "World.spawn", + "sig": "World.spawn(model_id) -> entity", + "tip": "Spawn an entity of a model by id.", + "kind": "namespace-method", + "page": "world-spawn.html", + "section": "World" + }, + "world-save": { + "name": "World.save", + "sig": "World.save(buf) -> int", + "tip": "Serialize the world into a buffer.", + "kind": "namespace-method", + "page": "world-save.html", + "section": "World" + }, + "world-load": { + "name": "World.load", + "sig": "World.load(buf, len) -> void", + "tip": "Restore the world from a buffer.", + "kind": "namespace-method", + "page": "world-load.html", + "section": "World" + }, + "world-prop_id": { + "name": "World.prop_id", + "sig": "World.prop_id(name) -> int", + "tip": "Look up a component's id by name.", + "kind": "namespace-method", + "page": "world-prop_id.html", + "section": "World" + }, + "world-field_id": { + "name": "World.field_id", + "sig": "World.field_id(prop_id, name) -> int", + "tip": "Look up a field's id by name.", + "kind": "namespace-method", + "page": "world-field_id.html", + "section": "World" + }, + "world-model_id": { + "name": "World.model_id", + "sig": "World.model_id(name) -> int", + "tip": "Look up a model's id by name.", + "kind": "namespace-method", + "page": "world-model_id.html", + "section": "World" + }, + "world-kind": { + "name": "World.kind", + "sig": "World.kind(entity) -> int", + "tip": "The model id of an entity.", + "kind": "namespace-method", + "page": "world-kind.html", + "section": "World" + }, + "world-register_prop": { + "name": "World.register_prop", + "sig": "World.register_prop(name, size) -> int", + "tip": "Register a new component at runtime.", + "kind": "namespace-method", + "page": "world-register_prop.html", + "section": "World" + }, + "world-attach": { + "name": "World.attach", + "sig": "World.attach(entity, prop_id) -> void", + "tip": "Attach a component to an entity at runtime.", + "kind": "namespace-method", + "page": "world-attach.html", + "section": "World" + }, + "world-detach": { + "name": "World.detach", + "sig": "World.detach(entity, prop_id) -> void", + "tip": "Detach a component from an entity at runtime.", + "kind": "namespace-method", + "page": "world-detach.html", + "section": "World" + }, + "world-query_next": { + "name": "World.query_next", + "sig": "World.query_next(query_id, cursor) -> int", + "tip": "Advance a reflective query.", + "kind": "namespace-method", + "page": "world-query_next.html", + "section": "World" + }, + "world-despawn": { + "name": "World.despawn", + "sig": "World.despawn(entity)", + "tip": "Despawn an entity by id — runs its @OnDespawn hooks and frees the slot.", + "kind": "namespace-method", + "page": "world-despawn.html", + "section": "World" + }, + "audio-load": { + "name": "Audio.load", + "sig": "Audio.load(path) -> int", + "tip": "Load a sound file into a handle.", + "kind": "namespace-method", + "page": "audio-load.html", + "section": "Audio" + }, + "audio-play": { + "name": "Audio.play", + "sig": "Audio.play(id) -> void", + "tip": "Fire a one-shot sound.", + "kind": "namespace-method", + "page": "audio-play.html", + "section": "Audio" + }, + "audio-play_at": { + "name": "Audio.play_at", + "sig": "Audio.play_at(id, gain, pitch, pan) -> void", + "tip": "Fire a one-shot with its own gain, pitch and stereo position.", + "kind": "namespace-method", + "page": "audio-play_at.html", + "section": "Audio" + }, + "audio-play_sound": { + "name": "Audio.play_sound", + "sig": "Audio.play_sound(id) -> void", + "tip": "Fire a one-shot sound (alias of play).", + "kind": "namespace-method", + "page": "audio-play_sound.html", + "section": "Audio" + }, + "audio-play_music": { + "name": "Audio.play_music", + "sig": "Audio.play_music(id) -> void", + "tip": "Loop a sound as background music.", + "kind": "namespace-method", + "page": "audio-play_music.html", + "section": "Audio" + }, + "audio-stop": { + "name": "Audio.stop", + "sig": "Audio.stop(id) -> void", + "tip": "Stop one sound.", + "kind": "namespace-method", + "page": "audio-stop.html", + "section": "Audio" + }, + "audio-stop_music": { + "name": "Audio.stop_music", + "sig": "Audio.stop_music() -> void", + "tip": "Stop the music channel.", + "kind": "namespace-method", + "page": "audio-stop_music.html", + "section": "Audio" + }, + "audio-stop_all": { + "name": "Audio.stop_all", + "sig": "Audio.stop_all() -> void", + "tip": "Stop every sound.", + "kind": "namespace-method", + "page": "audio-stop_all.html", + "section": "Audio" + }, + "audio-volume": { + "name": "Audio.volume", + "sig": "Audio.volume(v) -> void", + "tip": "Set the master volume.", + "kind": "namespace-method", + "page": "audio-volume.html", + "section": "Audio" + }, + "audio-pitch": { + "name": "Audio.pitch", + "sig": "Audio.pitch(v) -> void", + "tip": "Set the playback rate / pitch.", + "kind": "namespace-method", + "page": "audio-pitch.html", + "section": "Audio" + }, + "audio-is_playing": { + "name": "Audio.is_playing", + "sig": "Audio.is_playing(id) -> bool", + "tip": "Is this sound playing?", + "kind": "namespace-method", + "page": "audio-is_playing.html", + "section": "Audio" + }, + "audio-define": { + "name": "Audio.define", + "sig": "Audio.define(name: s, path: p) -> int", + "tip": "Load a sound into the bank under a name.", + "kind": "namespace-method", + "page": "audio-define.html", + "section": "Audio" + }, + "audio-named": { + "name": "Audio.named", + "sig": "Audio.named(name: s) -> int", + "tip": "The handle registered under a name, or 0.", + "kind": "namespace-method", + "page": "audio-named.html", + "section": "Audio" + }, + "random-range": { + "name": "Random.range", + "sig": "Random.range(low, high) -> int", + "tip": "A random integer in the inclusive range [low, high].", + "kind": "namespace-method", + "page": "random-range.html", + "section": "Random" + }, + "random-chance": { + "name": "Random.chance", + "sig": "Random.chance(percent) -> bool", + "tip": "Return true with the given percent probability.", + "kind": "namespace-method", + "page": "random-chance.html", + "section": "Random" + }, + "random-seed": { + "name": "Random.seed", + "sig": "Random.seed(value)", + "tip": "Seed the random generator so runs are reproducible.", + "kind": "namespace-method", + "page": "random-seed.html", + "section": "Random" + }, + "random-value": { + "name": "Random.value", + "sig": "Random.value() -> fixed", + "tip": "A random fixed value in [0, 1).", + "kind": "namespace-method", + "page": "random-value.html", + "section": "Random" + }, + "random-int": { + "name": "Random.int", + "sig": "Random.int(max) -> int", + "tip": "A random integer in [0, max).", + "kind": "namespace-method", + "page": "random-int.html", + "section": "Random" + }, + "random-sign": { + "name": "Random.sign", + "sig": "Random.sign() -> int", + "tip": "A random +1 or -1.", + "kind": "namespace-method", + "page": "random-sign.html", + "section": "Random" + }, + "random-weighted": { + "name": "Random.weighted", + "sig": "Random.weighted(weights: []int) -> int", + "tip": "An index drawn in proportion to its weight.", + "kind": "namespace-method", + "page": "random-weighted.html", + "section": "Random" + }, + "vector-make": { + "name": "Vector.make", + "sig": "Vector.make(x, y) -> Vector", + "tip": "Build a vector from x and y components.", + "kind": "namespace-method", + "page": "vector-make.html", + "section": "Vector" + }, + "vector-zero": { + "name": "Vector.zero", + "sig": "Vector.zero() -> Vector", + "tip": "The zero vector, (0, 0).", + "kind": "namespace-method", + "page": "vector-zero.html", + "section": "Vector" + }, + "vector-x": { + "name": "Vector.x", + "sig": "Vector.x(v) -> fixed", + "tip": "The x component of a vector.", + "kind": "namespace-method", + "page": "vector-x.html", + "section": "Vector" + }, + "vector-y": { + "name": "Vector.y", + "sig": "Vector.y(v) -> fixed", + "tip": "The y component of a vector.", + "kind": "namespace-method", + "page": "vector-y.html", + "section": "Vector" + }, + "vector-add": { + "name": "Vector.add", + "sig": "Vector.add(a, b) -> Vector", + "tip": "Component-wise sum a + b.", + "kind": "namespace-method", + "page": "vector-add.html", + "section": "Vector" + }, + "vector-sub": { + "name": "Vector.sub", + "sig": "Vector.sub(a, b) -> Vector", + "tip": "Component-wise difference a - b.", + "kind": "namespace-method", + "page": "vector-sub.html", + "section": "Vector" + }, + "vector-scale": { + "name": "Vector.scale", + "sig": "Vector.scale(v, s) -> Vector", + "tip": "Scale a vector by a scalar.", + "kind": "namespace-method", + "page": "vector-scale.html", + "section": "Vector" + }, + "vector-dot": { + "name": "Vector.dot", + "sig": "Vector.dot(a, b) -> fixed", + "tip": "The dot product of two vectors.", + "kind": "namespace-method", + "page": "vector-dot.html", + "section": "Vector" + }, + "vector-length": { + "name": "Vector.length", + "sig": "Vector.length(v) -> fixed", + "tip": "The length (magnitude) of a vector.", + "kind": "namespace-method", + "page": "vector-length.html", + "section": "Vector" + }, + "vector-distance": { + "name": "Vector.distance", + "sig": "Vector.distance(a, b) -> fixed", + "tip": "The distance between two points.", + "kind": "namespace-method", + "page": "vector-distance.html", + "section": "Vector" + }, + "vector-normalize": { + "name": "Vector.normalize", + "sig": "Vector.normalize(v) -> Vector", + "tip": "A unit vector in the same direction.", + "kind": "namespace-method", + "page": "vector-normalize.html", + "section": "Vector" + }, + "vector-lerp": { + "name": "Vector.lerp", + "sig": "Vector.lerp(a, b, t) -> Vector", + "tip": "Linear interpolation between two vectors.", + "kind": "namespace-method", + "page": "vector-lerp.html", + "section": "Vector" + }, + "vector-rotate": { + "name": "Vector.rotate", + "sig": "Vector.rotate(v, angle) -> Vector", + "tip": "Rotate a vector by an angle in radians.", + "kind": "namespace-method", + "page": "vector-rotate.html", + "section": "Vector" + }, + "vector-angle": { + "name": "Vector.angle", + "sig": "Vector.angle(v) -> fixed", + "tip": "The angle of a vector in radians.", + "kind": "namespace-method", + "page": "vector-angle.html", + "section": "Vector" + }, + "vector-from_angle": { + "name": "Vector.from_angle", + "sig": "Vector.from_angle(a) -> Vector", + "tip": "A unit vector pointing at an angle.", + "kind": "namespace-method", + "page": "vector-from_angle.html", + "section": "Vector" + }, + "http-get": { + "name": "Http.get", + "sig": "Http.get(url) -> int", + "tip": "Start an async GET request.", + "kind": "namespace-method", + "page": "http-get.html", + "section": "Http" + }, + "http-post": { + "name": "Http.post", + "sig": "Http.post(url, body) -> int", + "tip": "Start an async POST with a body.", + "kind": "namespace-method", + "page": "http-post.html", + "section": "Http" + }, + "http-request": { + "name": "Http.request", + "sig": "Http.request(method, url, body) -> int", + "tip": "Start a request with any method.", + "kind": "namespace-method", + "page": "http-request.html", + "section": "Http" + }, + "http-open": { + "name": "Http.open", + "sig": "Http.open(method, url) -> int", + "tip": "Open a request to configure before sending.", + "kind": "namespace-method", + "page": "http-open.html", + "section": "Http" + }, + "http-set": { + "name": "Http.set", + "sig": "Http.set(handle, name, value) -> void", + "tip": "Set a request header before sending.", + "kind": "namespace-method", + "page": "http-set.html", + "section": "Http" + }, + "http-body": { + "name": "Http.body", + "sig": "Http.body(handle, body) -> void", + "tip": "Set a string request body.", + "kind": "namespace-method", + "page": "http-body.html", + "section": "Http" + }, + "http-body_bytes": { + "name": "Http.body_bytes", + "sig": "Http.body_bytes(handle, bytes, len) -> void", + "tip": "Set a raw byte request body.", + "kind": "namespace-method", + "page": "http-body_bytes.html", + "section": "Http" + }, + "http-send": { + "name": "Http.send", + "sig": "Http.send(handle) -> void", + "tip": "Dispatch an opened request.", + "kind": "namespace-method", + "page": "http-send.html", + "section": "Http" + }, + "http-poll": { + "name": "Http.poll", + "sig": "Http.poll(handle) -> int", + "tip": "Poll a request; -1 pending, 0 error, else status.", + "kind": "namespace-method", + "page": "http-poll.html", + "section": "Http" + }, + "http-status": { + "name": "Http.status", + "sig": "Http.status(handle) -> int", + "tip": "The response HTTP status code.", + "kind": "namespace-method", + "page": "http-status.html", + "section": "Http" + }, + "http-ok": { + "name": "Http.ok", + "sig": "Http.ok(handle) -> bool", + "tip": "Was the response status 2xx?", + "kind": "namespace-method", + "page": "http-ok.html", + "section": "Http" + }, + "http-text": { + "name": "Http.text", + "sig": "Http.text(handle) -> str", + "tip": "The response body as text.", + "kind": "namespace-method", + "page": "http-text.html", + "section": "Http" + }, + "http-body_len": { + "name": "Http.body_len", + "sig": "Http.body_len(handle) -> int", + "tip": "The response body length in bytes.", + "kind": "namespace-method", + "page": "http-body_len.html", + "section": "Http" + }, + "http-header": { + "name": "Http.header", + "sig": "Http.header(handle, name) -> str", + "tip": "A response header value (case-insensitive).", + "kind": "namespace-method", + "page": "http-header.html", + "section": "Http" + }, + "http-free": { + "name": "Http.free", + "sig": "Http.free(handle) -> void", + "tip": "Release a request's resources.", + "kind": "namespace-method", + "page": "http-free.html", + "section": "Http" + }, + "http-parse": { + "name": "Http.parse", + "sig": "Http.parse(bytes, len) -> int", + "tip": "Parse a raw HTTP response into a handle.", + "kind": "namespace-method", + "page": "http-parse.html", + "section": "Http" + }, + "http-save_to": { + "name": "Http.save_to", + "sig": "Http.save_to(handle, path) -> void", + "tip": "Stream the response body to a file.", + "kind": "namespace-method", + "page": "http-save_to.html", + "section": "Http" + }, + "http-received": { + "name": "Http.received", + "sig": "Http.received(handle) -> int", + "tip": "Body bytes received so far.", + "kind": "namespace-method", + "page": "http-received.html", + "section": "Http" + }, + "http-expected": { + "name": "Http.expected", + "sig": "Http.expected(handle) -> int", + "tip": "The body length the server announced, or -1.", + "kind": "namespace-method", + "page": "http-expected.html", + "section": "Http" + }, + "ivec2-make": { + "name": "IVec2.make", + "sig": "IVec2.make(x, y) -> IVec2", + "tip": "Build an integer vector from x and y components.", + "kind": "namespace-method", + "page": "ivec2-make.html", + "section": "IVec2" + }, + "ivec2-zero": { + "name": "IVec2.zero", + "sig": "IVec2.zero() -> IVec2", + "tip": "The origin cell, (0, 0).", + "kind": "namespace-method", + "page": "ivec2-zero.html", + "section": "IVec2" + }, + "ivec2-x": { + "name": "IVec2.x", + "sig": "IVec2.x(v) -> int", + "tip": "The x component of an integer vector.", + "kind": "namespace-method", + "page": "ivec2-x.html", + "section": "IVec2" + }, + "ivec2-y": { + "name": "IVec2.y", + "sig": "IVec2.y(v) -> int", + "tip": "The y component of an integer vector.", + "kind": "namespace-method", + "page": "ivec2-y.html", + "section": "IVec2" + }, + "ivec2-add": { + "name": "IVec2.add", + "sig": "IVec2.add(a, b) -> IVec2", + "tip": "Component-wise sum of two integer vectors.", + "kind": "namespace-method", + "page": "ivec2-add.html", + "section": "IVec2" + }, + "ivec2-sub": { + "name": "IVec2.sub", + "sig": "IVec2.sub(a, b) -> IVec2", + "tip": "Component-wise difference of two integer vectors.", + "kind": "namespace-method", + "page": "ivec2-sub.html", + "section": "IVec2" + }, + "ivec2-scale": { + "name": "IVec2.scale", + "sig": "IVec2.scale(v, s) -> IVec2", + "tip": "Multiply both components by an integer.", + "kind": "namespace-method", + "page": "ivec2-scale.html", + "section": "IVec2" + }, + "ivec2-dot": { + "name": "IVec2.dot", + "sig": "IVec2.dot(a, b) -> int", + "tip": "The dot product ax*bx + ay*by.", + "kind": "namespace-method", + "page": "ivec2-dot.html", + "section": "IVec2" + }, + "ivec2-equal": { + "name": "IVec2.equal", + "sig": "IVec2.equal(a, b) -> bool", + "tip": "True when both components match.", + "kind": "namespace-method", + "page": "ivec2-equal.html", + "section": "IVec2" + }, + "ivec2-manhattan": { + "name": "IVec2.manhattan", + "sig": "IVec2.manhattan(a, b) -> int", + "tip": "Grid distance |dx| + |dy|.", + "kind": "namespace-method", + "page": "ivec2-manhattan.html", + "section": "IVec2" + }, + "ivec2-to_vector": { + "name": "IVec2.to_vector", + "sig": "IVec2.to_vector(v) -> Vector", + "tip": "Widen to a fixed-point Vector.", + "kind": "namespace-method", + "page": "ivec2-to_vector.html", + "section": "IVec2" + }, + "ivec2-distance2": { + "name": "IVec2.distance2", + "sig": "IVec2.distance2(a, b) -> int", + "tip": "The squared distance between two points.", + "kind": "namespace-method", + "page": "ivec2-distance2.html", + "section": "IVec2" + }, + "ivec2-within": { + "name": "IVec2.within", + "sig": "IVec2.within(a, b, radius) -> bool", + "tip": "Are two points within a radius of each other?", + "kind": "namespace-method", + "page": "ivec2-within.html", + "section": "IVec2" + }, + "ivec2-heading": { + "name": "IVec2.heading", + "sig": "IVec2.heading(from, to) -> int", + "tip": "The direction from one point to another, in degrees.", + "kind": "namespace-method", + "page": "ivec2-heading.html", + "section": "IVec2" + }, + "ivec2-along": { + "name": "IVec2.along", + "sig": "IVec2.along(origin, degrees, distance) -> IVec2", + "tip": "The point a distance along a heading.", + "kind": "namespace-method", + "page": "ivec2-along.html", + "section": "IVec2" + }, + "ivec2-step": { + "name": "IVec2.step", + "sig": "IVec2.step(degrees) -> IVec2", + "tip": "A -1/0/1 unit step along a heading.", + "kind": "namespace-method", + "page": "ivec2-step.html", + "section": "IVec2" + }, + "map-size": { + "name": "Map.size", + "sig": "Map.size(width, height)", + "tip": "Set the tilemap dimensions in cells before filling rows.", + "kind": "namespace-method", + "page": "map-size.html", + "section": "Map — tilemap" + }, + "map-row": { + "name": "Map.row", + "sig": "Map.row(y, cells)", + "tip": "Fill one row of the tilemap from a string of tile characters.", + "kind": "namespace-method", + "page": "map-row.html", + "section": "Map — tilemap" + }, + "map-tile": { + "name": "Map.tile", + "sig": "Map.tile(x, y) -> int", + "tip": "Read the tile character at a cell (out-of-bounds reads answer '#').", + "kind": "namespace-method", + "page": "map-tile.html", + "section": "Map — tilemap" + }, + "map-get": { + "name": "Map.get", + "sig": "Map.get(x: tx, y: ty) -> int", + "tip": "The glyph at a cell ('#' outside the map).", + "kind": "namespace-method", + "page": "map-get.html", + "section": "Map — tilemap" + }, + "map-set": { + "name": "Map.set", + "sig": "Map.set(x: tx, y: ty, glyph: g)", + "tip": "Write one cell in place.", + "kind": "namespace-method", + "page": "map-set.html", + "section": "Map — tilemap" + }, + "map-fill": { + "name": "Map.fill", + "sig": "Map.fill(glyph: g)", + "tip": "Every cell becomes the glyph.", + "kind": "namespace-method", + "page": "map-fill.html", + "section": "Map — tilemap" + }, + "map-rect": { + "name": "Map.rect", + "sig": "Map.rect(x: tx, y: ty, width: w, height: h, glyph: g)", + "tip": "Fill a rectangle of cells.", + "kind": "namespace-method", + "page": "map-rect.html", + "section": "Map — tilemap" + }, + "map-border": { + "name": "Map.border", + "sig": "Map.border(glyph: g)", + "tip": "The outermost ring of cells.", + "kind": "namespace-method", + "page": "map-border.html", + "section": "Map — tilemap" + }, + "map-random_cell": { + "name": "Map.random_cell", + "sig": "Map.random_cell(glyph: g) -> IVec2", + "tip": "A random cell holding the glyph (seeded RNG).", + "kind": "namespace-method", + "page": "map-random_cell.html", + "section": "Map — tilemap" + }, + "map-is_solid": { + "name": "Map.is_solid", + "sig": "Map.is_solid(x: tx, y: ty) -> bool", + "tip": "Is a cell solid, per the Solids config?", + "kind": "namespace-method", + "page": "map-is_solid.html", + "section": "Map — tilemap" + }, + "map-is_solid_at": { + "name": "Map.is_solid_at", + "sig": "Map.is_solid_at(x: px, y: py) -> bool", + "tip": "Is the cell under a pixel position solid?", + "kind": "namespace-method", + "page": "map-is_solid_at.html", + "section": "Map — tilemap" + }, + "map-width": { + "name": "Map.width", + "sig": "Map.width() -> int", + "tip": "The map's width in cells.", + "kind": "namespace-method", + "page": "map-width.html", + "section": "Map — tilemap" + }, + "map-height": { + "name": "Map.height", + "sig": "Map.height() -> int", + "tip": "The map's height in cells.", + "kind": "namespace-method", + "page": "map-height.html", + "section": "Map — tilemap" + }, + "map-random_cell_far": { + "name": "Map.random_cell_far", + "sig": "Map.random_cell_far(glyph:, from:, min_tiles:) -> IVec2", + "tip": "A random cell with the glyph, at least a distance from a point.", + "kind": "namespace-method", + "page": "map-random_cell_far.html", + "section": "Map — tilemap" + }, + "map-to_tile": { + "name": "Map.to_tile", + "sig": "Map.to_tile(pixel: IVec2) -> IVec2", + "tip": "The tile under a pixel position.", + "kind": "namespace-method", + "page": "map-to_tile.html", + "section": "Map — tilemap" + }, + "rect-make": { + "name": "Rect.make", + "sig": "Rect.make(x, y, w, h) -> Rect", + "tip": "Build a rectangle from a corner and a size.", + "kind": "namespace-method", + "page": "rect-make.html", + "section": "Rect" + }, + "rect-x": { + "name": "Rect.x", + "sig": "Rect.x(r) -> fixed", + "tip": "The left edge (x position).", + "kind": "namespace-method", + "page": "rect-x.html", + "section": "Rect" + }, + "rect-y": { + "name": "Rect.y", + "sig": "Rect.y(r) -> fixed", + "tip": "The top edge (y position).", + "kind": "namespace-method", + "page": "rect-y.html", + "section": "Rect" + }, + "rect-w": { + "name": "Rect.w", + "sig": "Rect.w(r) -> fixed", + "tip": "The width.", + "kind": "namespace-method", + "page": "rect-w.html", + "section": "Rect" + }, + "rect-h": { + "name": "Rect.h", + "sig": "Rect.h(r) -> fixed", + "tip": "The height.", + "kind": "namespace-method", + "page": "rect-h.html", + "section": "Rect" + }, + "rect-right": { + "name": "Rect.right", + "sig": "Rect.right(r) -> fixed", + "tip": "The right edge, x + w.", + "kind": "namespace-method", + "page": "rect-right.html", + "section": "Rect" + }, + "rect-bottom": { + "name": "Rect.bottom", + "sig": "Rect.bottom(r) -> fixed", + "tip": "The bottom edge, y + h.", + "kind": "namespace-method", + "page": "rect-bottom.html", + "section": "Rect" + }, + "rect-center": { + "name": "Rect.center", + "sig": "Rect.center(r) -> Vector", + "tip": "The center point as a Vector.", + "kind": "namespace-method", + "page": "rect-center.html", + "section": "Rect" + }, + "rect-contains": { + "name": "Rect.contains", + "sig": "Rect.contains(r, px, py) -> bool", + "tip": "True when the point is inside.", + "kind": "namespace-method", + "page": "rect-contains.html", + "section": "Rect" + }, + "rect-intersects": { + "name": "Rect.intersects", + "sig": "Rect.intersects(a, b) -> bool", + "tip": "True when two rectangles overlap.", + "kind": "namespace-method", + "page": "rect-intersects.html", + "section": "Rect" + }, + "udp-open": { + "name": "Udp.open", + "sig": "Udp.open(port) -> int", + "tip": "Open a socket on a port.", + "kind": "namespace-method", + "page": "udp-open.html", + "section": "Udp" + }, + "udp-port": { + "name": "Udp.port", + "sig": "Udp.port(socket) -> int", + "tip": "The port a socket is bound to.", + "kind": "namespace-method", + "page": "udp-port.html", + "section": "Udp" + }, + "udp-send": { + "name": "Udp.send", + "sig": "Udp.send(socket, ip, port, bytes, len) -> int", + "tip": "Send one datagram.", + "kind": "namespace-method", + "page": "udp-send.html", + "section": "Udp" + }, + "udp-recv": { + "name": "Udp.recv", + "sig": "Udp.recv(socket, bytes, cap) -> int", + "tip": "Read the next waiting datagram.", + "kind": "namespace-method", + "page": "udp-recv.html", + "section": "Udp" + }, + "udp-from_ip": { + "name": "Udp.from_ip", + "sig": "Udp.from_ip(socket) -> int", + "tip": "The sender of the last datagram read.", + "kind": "namespace-method", + "page": "udp-from_ip.html", + "section": "Udp" + }, + "udp-from_port": { + "name": "Udp.from_port", + "sig": "Udp.from_port(socket) -> int", + "tip": "The sender's port.", + "kind": "namespace-method", + "page": "udp-from_port.html", + "section": "Udp" + }, + "udp-close": { + "name": "Udp.close", + "sig": "Udp.close(socket) -> void", + "tip": "Close a socket.", + "kind": "namespace-method", + "page": "udp-close.html", + "section": "Udp" + }, + "udp-resolve": { + "name": "Udp.resolve", + "sig": "Udp.resolve(name) -> int", + "tip": "Look a host name up.", + "kind": "namespace-method", + "page": "udp-resolve.html", + "section": "Udp" + }, + "udp-local_ip": { + "name": "Udp.local_ip", + "sig": "Udp.local_ip() -> int", + "tip": "This machine's address.", + "kind": "namespace-method", + "page": "udp-local_ip.html", + "section": "Udp" + }, + "udp-ip": { + "name": "Udp.ip", + "sig": "Udp.ip(text) -> int", + "tip": "Parse a dotted address.", + "kind": "namespace-method", + "page": "udp-ip.html", + "section": "Udp" + }, + "udp-ip_text": { + "name": "Udp.ip_text", + "sig": "Udp.ip_text(ip) -> string", + "tip": "Format an address.", + "kind": "namespace-method", + "page": "udp-ip_text.html", + "section": "Udp" + }, + "bigint-from": { + "name": "BigInt.from", + "sig": "BigInt.from(value) -> BigInt", + "tip": "Turn a plain int into a BigInt.", + "kind": "namespace-method", + "page": "bigint-from.html", + "section": "BigInt" + }, + "bigint-parse": { + "name": "BigInt.parse", + "sig": "BigInt.parse(text) -> BigInt", + "tip": "Parse decimal text into a BigInt.", + "kind": "namespace-method", + "page": "bigint-parse.html", + "section": "BigInt" + }, + "bigint-add": { + "name": "BigInt.add", + "sig": "BigInt.add(a, b) -> BigInt", + "tip": "Exact sum of two big integers.", + "kind": "namespace-method", + "page": "bigint-add.html", + "section": "BigInt" + }, + "bigint-sub": { + "name": "BigInt.sub", + "sig": "BigInt.sub(a, b) -> BigInt", + "tip": "Exact difference of two big integers.", + "kind": "namespace-method", + "page": "bigint-sub.html", + "section": "BigInt" + }, + "bigint-mul": { + "name": "BigInt.mul", + "sig": "BigInt.mul(a, b) -> BigInt", + "tip": "Exact product of two big integers.", + "kind": "namespace-method", + "page": "bigint-mul.html", + "section": "BigInt" + }, + "bigint-neg": { + "name": "BigInt.neg", + "sig": "BigInt.neg(a) -> BigInt", + "tip": "Negate a big integer.", + "kind": "namespace-method", + "page": "bigint-neg.html", + "section": "BigInt" + }, + "bigint-pow": { + "name": "BigInt.pow", + "sig": "BigInt.pow(a, exp) -> BigInt", + "tip": "Raise a big integer to an int power.", + "kind": "namespace-method", + "page": "bigint-pow.html", + "section": "BigInt" + }, + "bigint-div": { + "name": "BigInt.div", + "sig": "BigInt.div(a, d) -> BigInt", + "tip": "Divide a big integer by an int (toward zero).", + "kind": "namespace-method", + "page": "bigint-div.html", + "section": "BigInt" + }, + "bigint-mod": { + "name": "BigInt.mod", + "sig": "BigInt.mod(a, d) -> int", + "tip": "Remainder of a big integer divided by an int.", + "kind": "namespace-method", + "page": "bigint-mod.html", + "section": "BigInt" + }, + "bigint-cmp": { + "name": "BigInt.cmp", + "sig": "BigInt.cmp(a, b) -> int", + "tip": "Compare two big integers: -1, 0, or 1.", + "kind": "namespace-method", + "page": "bigint-cmp.html", + "section": "BigInt" + }, + "bigint-eq": { + "name": "BigInt.eq", + "sig": "BigInt.eq(a, b) -> bool", + "tip": "True when two big integers are equal.", + "kind": "namespace-method", + "page": "bigint-eq.html", + "section": "BigInt" + }, + "bigint-is_zero": { + "name": "BigInt.is_zero", + "sig": "BigInt.is_zero(a) -> bool", + "tip": "True when a big integer is zero.", + "kind": "namespace-method", + "page": "bigint-is_zero.html", + "section": "BigInt" + }, + "bigint-to_int": { + "name": "BigInt.to_int", + "sig": "BigInt.to_int(a) -> int", + "tip": "Narrow a big integer to a plain int (clamped).", + "kind": "namespace-method", + "page": "bigint-to_int.html", + "section": "BigInt" + }, + "bigint-str": { + "name": "BigInt.str", + "sig": "BigInt.str(a) -> string", + "tip": "Render a big integer as decimal text.", + "kind": "namespace-method", + "page": "bigint-str.html", + "section": "BigInt" + }, + "process-spawn": { + "name": "Process.spawn", + "sig": "Process.spawn(path, args) -> int", + "tip": "Start a program; never blocks.", + "kind": "namespace-method", + "page": "process-spawn.html", + "section": "Process" + }, + "process-poll": { + "name": "Process.poll", + "sig": "Process.poll(handle) -> int", + "tip": "-1 while running, else the exit code.", + "kind": "namespace-method", + "page": "process-poll.html", + "section": "Process" + }, + "process-kill": { + "name": "Process.kill", + "sig": "Process.kill(handle) -> void", + "tip": "End a running child.", + "kind": "namespace-method", + "page": "process-kill.html", + "section": "Process" + }, + "process-free": { + "name": "Process.free", + "sig": "Process.free(handle) -> void", + "tip": "Let a process handle go.", + "kind": "namespace-method", + "page": "process-free.html", + "section": "Process" + }, + "decimal-from": { + "name": "Decimal.from", + "sig": "Decimal.from(value) -> Decimal", + "tip": "Turn a whole int into a Decimal.", + "kind": "namespace-method", + "page": "decimal-from.html", + "section": "Decimal" + }, + "decimal-parse": { + "name": "Decimal.parse", + "sig": "Decimal.parse(text) -> Decimal", + "tip": "Parse decimal text like \"19.99\".", + "kind": "namespace-method", + "page": "decimal-parse.html", + "section": "Decimal" + }, + "decimal-add": { + "name": "Decimal.add", + "sig": "Decimal.add(a, b) -> Decimal", + "tip": "Exact sum of two decimals.", + "kind": "namespace-method", + "page": "decimal-add.html", + "section": "Decimal" + }, + "decimal-sub": { + "name": "Decimal.sub", + "sig": "Decimal.sub(a, b) -> Decimal", + "tip": "Exact difference of two decimals.", + "kind": "namespace-method", + "page": "decimal-sub.html", + "section": "Decimal" + }, + "decimal-mul": { + "name": "Decimal.mul", + "sig": "Decimal.mul(a, b) -> Decimal", + "tip": "Exact product of two decimals.", + "kind": "namespace-method", + "page": "decimal-mul.html", + "section": "Decimal" + }, + "decimal-neg": { + "name": "Decimal.neg", + "sig": "Decimal.neg(a) -> Decimal", + "tip": "Negate a decimal.", + "kind": "namespace-method", + "page": "decimal-neg.html", + "section": "Decimal" + }, + "decimal-cmp": { + "name": "Decimal.cmp", + "sig": "Decimal.cmp(a, b) -> int", + "tip": "Compare two decimals: -1, 0, or 1.", + "kind": "namespace-method", + "page": "decimal-cmp.html", + "section": "Decimal" + }, + "decimal-eq": { + "name": "Decimal.eq", + "sig": "Decimal.eq(a, b) -> bool", + "tip": "True when two decimals are equal in value.", + "kind": "namespace-method", + "page": "decimal-eq.html", + "section": "Decimal" + }, + "decimal-scale": { + "name": "Decimal.scale", + "sig": "Decimal.scale(d) -> int", + "tip": "The number of digits after the point.", + "kind": "namespace-method", + "page": "decimal-scale.html", + "section": "Decimal" + }, + "decimal-rescale": { + "name": "Decimal.rescale", + "sig": "Decimal.rescale(d, places) -> Decimal", + "tip": "Change precision (truncates toward zero).", + "kind": "namespace-method", + "page": "decimal-rescale.html", + "section": "Decimal" + }, + "decimal-str": { + "name": "Decimal.str", + "sig": "Decimal.str(d) -> string", + "tip": "Render a decimal as text.", + "kind": "namespace-method", + "page": "decimal-str.html", + "section": "Decimal" + }, + "type-int": { + "name": "int", + "sig": "int", + "tip": "The default 32-bit signed integer — also how colors, keys, and tiles are carried.", + "kind": "type", + "page": "type-int.html", + "section": "Types" + }, + "type-fixed": { + "name": "fixed", + "sig": "fixed", + "tip": "Q16.16 fixed-point for deterministic fractional math — no floats.", + "kind": "type", + "page": "type-fixed.html", + "section": "Types" + }, + "type-long": { + "name": "long", + "sig": "long", + "tip": "A 64-bit signed integer for values that overflow a 32-bit int.", + "kind": "type", + "page": "type-long.html", + "section": "Types" + }, + "type-bool": { + "name": "bool", + "sig": "bool", + "tip": "A truth value — the result of comparisons and of and / or / not.", + "kind": "type", + "page": "type-bool.html", + "section": "Types" + }, + "type-float": { + "name": "float", + "sig": "float", + "tip": "A 32-bit IEEE floating-point number — what the GPU uses.", + "kind": "type", + "page": "type-float.html", + "section": "Types" + }, + "type-string": { + "name": "string", + "sig": "string", + "tip": "An immutable string — compared by content, sliceable, and interpolatable.", + "kind": "type", + "page": "type-string.html", + "section": "Types" + }, + "type-double": { + "name": "double", + "sig": "double", + "tip": "A 64-bit IEEE floating-point number for precise math.", + "kind": "type", + "page": "type-double.html", + "section": "Types" + }, + "type-entity": { + "name": "entity", + "sig": "entity", + "tip": "The id/handle of a spawned model instance, as returned by self().", + "kind": "type", + "page": "type-entity.html", + "section": "Types" + }, + "type-pointer": { + "name": "pointer", + "sig": "pointer", + "tip": "A raw address into memory — a byte buffer from bytes(n), or an FFI handle.", + "kind": "type", + "page": "type-pointer.html", + "section": "Types" + }, + "type-words": { + "name": "words", + "sig": "words", + "tip": "A raw buffer indexed as 32-bit words — each buffer[i] reads or writes an int.", + "kind": "type", + "page": "type-words.html", + "section": "Types" + }, + "type-byte": { + "name": "byte", + "sig": "byte", + "tip": "A raw buffer indexed one byte at a time — each buffer[i] reads or writes a byte.", + "kind": "type", + "page": "type-byte.html", + "section": "Types" + }, + "type-slices": { + "name": "[]T (slices)", + "sig": "[]T", + "tip": "A growable slice of T — new []T makes one, push appends, len counts, s[i] indexes.", + "kind": "type", + "page": "type-slices.html", + "section": "Types" + }, + "type-fixeds": { + "name": "fixeds", + "sig": "fixeds", + "tip": "A buffer of fixed-point values — f[i] reads and writes a fixed.", + "kind": "type", + "page": "type-fixeds.html", + "section": "Types" + }, + "type-countdown": { + "name": "countdown", + "sig": "countdown", + "tip": "An int component field the engine steps toward 0 once per Update.", + "kind": "type", + "page": "type-countdown.html", + "section": "Types" + }, + "type-floats": { + "name": "floats", + "sig": "floats / doubles", + "tip": "A buffer of floats (or doubles) — v[i] reads and writes one.", + "kind": "type", + "page": "type-floats.html", + "section": "Types" + }, + "type-pointers": { + "name": "pointers", + "sig": "pointers", + "tip": "A buffer of pointers — p[i] reads and writes a pointer.", + "kind": "type", + "page": "type-pointers.html", + "section": "Types" + }, + "type-vector": { + "name": "Vector", + "sig": "Vector", + "tip": "A 2D vector — two fixed components (x, y), copied by value.", + "kind": "type", + "page": "type-vector.html", + "section": "Types" + }, + "type-ivec2": { + "name": "IVec2", + "sig": "IVec2", + "tip": "An integer 2D vector — two int components (x, y), copied by value.", + "kind": "type", + "page": "type-ivec2.html", + "section": "Types" + }, + "type-rect": { + "name": "Rect", + "sig": "Rect", + "tip": "A rectangle — position (x, y) and size (w, h), copied by value.", + "kind": "type", + "page": "type-rect.html", + "section": "Types" + }, + "type-void": { + "name": "void", + "sig": "void", + "tip": "The absence of a value — the return type of a function that returns nothing.", + "kind": "type", + "page": "type-void.html", + "section": "Types" + }, + "dict-new": { + "name": "Dict.new", + "sig": "Dict.new() -> Dict", + "tip": "Create an empty string-keyed map.", + "kind": "namespace-method", + "page": "dict-new.html", + "section": "Dict" + }, + "dict-set": { + "name": "Dict.set", + "sig": "Dict.set(d, key, value)", + "tip": "Insert or update a key.", + "kind": "namespace-method", + "page": "dict-set.html", + "section": "Dict" + }, + "dict-get": { + "name": "Dict.get", + "sig": "Dict.get(d, key) -> int", + "tip": "Look up a key (0 if absent).", + "kind": "namespace-method", + "page": "dict-get.html", + "section": "Dict" + }, + "dict-get_or": { + "name": "Dict.get_or", + "sig": "Dict.get_or(d, key, fallback) -> int", + "tip": "Look up a key with an explicit default.", + "kind": "namespace-method", + "page": "dict-get_or.html", + "section": "Dict" + }, + "dict-has": { + "name": "Dict.has", + "sig": "Dict.has(d, key) -> bool", + "tip": "Is a key present?", + "kind": "namespace-method", + "page": "dict-has.html", + "section": "Dict" + }, + "dict-remove": { + "name": "Dict.remove", + "sig": "Dict.remove(d, key)", + "tip": "Delete a key.", + "kind": "namespace-method", + "page": "dict-remove.html", + "section": "Dict" + }, + "dict-size": { + "name": "Dict.size", + "sig": "Dict.size(d) -> int", + "tip": "How many keys are stored.", + "kind": "namespace-method", + "page": "dict-size.html", + "section": "Dict" + }, + "dict-clear": { + "name": "Dict.clear", + "sig": "Dict.clear(d)", + "tip": "Remove every key.", + "kind": "namespace-method", + "page": "dict-clear.html", + "section": "Dict" + }, + "dict-keys": { + "name": "Dict.keys", + "sig": "Dict.keys(d) -> []pointer", + "tip": "Every key, as a slice of strings.", + "kind": "namespace-method", + "page": "dict-keys.html", + "section": "Dict" + }, + "op-arith": { + "name": "Arithmetic", + "sig": "+ - * / %", + "tip": "Add, subtract, multiply, integer-divide, and remainder — on int and fixed.", + "kind": "operator", + "page": "op-arith.html", + "section": "Operators & tokens" + }, + "op-compare": { + "name": "Comparison", + "sig": "== != < <= > >=", + "tip": "Compare two values and yield a bool; on strings == compares contents.", + "kind": "operator", + "page": "op-compare.html", + "section": "Operators & tokens" + }, + "op-logical": { + "name": "Logical", + "sig": "and or not", + "tip": "The boolean combinators, spelled as words — never && or || or a bare !.", + "kind": "operator", + "page": "op-logical.html", + "section": "Operators & tokens" + }, + "op-bitwise": { + "name": "Bitwise", + "sig": "& | ^ ~ << >>", + "tip": "Bit-level and, or, xor, not, and shifts — with Go-style precedence.", + "kind": "operator", + "page": "op-bitwise.html", + "section": "Operators & tokens" + }, + "op-range": { + "name": "Range", + "sig": "a .. b", + "tip": "A half-open range for numeric for loops — from a up to but not including b.", + "kind": "operator", + "page": "op-range.html", + "section": "Operators & tokens" + }, + "op-assign": { + "name": "Assignment", + "sig": "name = value", + "tip": "Store into a var, a field, or an element — a statement, not an expression.", + "kind": "operator", + "page": "op-assign.html", + "section": "Operators & tokens" + }, + "op-access": { + "name": "Member & index", + "sig": "x.field buffer[i] text[a..b]", + "tip": "Field access, element index, and string slice — usable as value or target.", + "kind": "operator", + "page": "op-access.html", + "section": "Operators & tokens" + }, + "op-interp": { + "name": "String interpolation", + "sig": "`text {expr} more`", + "tip": "A backtick string with {expr} holes, each stringified and concatenated.", + "kind": "operator", + "page": "op-interp.html", + "section": "Operators & tokens" + }, + "op-literals": { + "name": "Literals", + "sig": "42 0x1E90FF 'w' \"text\" true null", + "tip": "Integer, hex, character, string, boolean, and null-pointer literals.", + "kind": "operator", + "page": "op-literals.html", + "section": "Operators & tokens" + }, + "op-comment": { + "name": "Comment", + "sig": "# to end of line", + "tip": "Everything after # on a line is a comment, ignored by the compiler.", + "kind": "operator", + "page": "op-comment.html", + "section": "Operators & tokens" + }, + "fn-print": { + "name": "print", + "sig": "print(x)", + "tip": "Print an int or string followed by a newline — for headless tests and debugging.", + "kind": "builtin", + "page": "fn-print.html", + "section": "Builtin functions" + }, + "fn-string": { + "name": "string", + "sig": "string(x) -> string", + "tip": "Convert an int, bool, or fixed value to text; a string passes through unchanged.", + "kind": "builtin", + "page": "fn-string.html", + "section": "Builtin functions" + }, + "fn-quit": { + "name": "quit", + "sig": "quit()", + "tip": "Stop the game loop cleanly after the current frame finishes.", + "kind": "builtin", + "page": "fn-quit.html", + "section": "Builtin functions" + }, + "fn-save": { + "name": "save", + "sig": "save()", + "tip": "Serialize the whole world — every model instance, property, and program var — in one call.", + "kind": "builtin", + "page": "fn-save.html", + "section": "Builtin functions" + }, + "fn-load": { + "name": "load", + "sig": "load() -> bool", + "tip": "Restore the world from a snapshot previously written by save().", + "kind": "builtin", + "page": "fn-load.html", + "section": "Builtin functions" + }, + "fn-len": { + "name": "len", + "sig": "len(x) -> int", + "tip": "The number of elements in a slice, or the byte length of a string.", + "kind": "builtin", + "page": "fn-len.html", + "section": "Builtin functions" + }, + "fn-push": { + "name": "push", + "sig": "push(slice, x)", + "tip": "Append one element to the end of a growable slice.", + "kind": "builtin", + "page": "fn-push.html", + "section": "Builtin functions" + }, + "fn-abs": { + "name": "abs", + "sig": "abs(a) -> int", + "tip": "The absolute value of an integer (its magnitude, never negative).", + "kind": "builtin", + "page": "fn-abs.html", + "section": "Builtin functions" + }, + "fn-clamp": { + "name": "clamp", + "sig": "clamp(v, lo, hi) -> int", + "tip": "Constrain a value to the inclusive range [lo, hi].", + "kind": "builtin", + "page": "fn-clamp.html", + "section": "Builtin functions" + }, + "fn-panic": { + "name": "panic", + "sig": "panic(msg: string)", + "tip": "Abort with a located error message instead of crashing.", + "kind": "builtin", + "page": "fn-panic.html", + "section": "Builtin functions" + }, + "fn-assert": { + "name": "assert", + "sig": "assert(cond: bool, msg: string)", + "tip": "Abort with a located message when an invariant is false.", + "kind": "builtin", + "page": "fn-assert.html", + "section": "Builtin functions" + }, + "fn-ok": { + "name": "ok", + "sig": "ok(v) -> result", + "tip": "Wrap a success payload in a result — the happy half of try/else.", + "kind": "builtin", + "page": "fn-ok.html", + "section": "Builtin functions" + }, + "fn-err": { + "name": "err", + "sig": "err(msg: string) -> result", + "tip": "Wrap a failure message in a result — the sad half, recovered by try/else.", + "kind": "builtin", + "page": "fn-err.html", + "section": "Builtin functions" + }, + "fn-is_ok": { + "name": "is_ok", + "sig": "is_ok(r: result) -> bool", + "tip": "True when a result carries a success payload.", + "kind": "builtin", + "page": "fn-is_ok.html", + "section": "Builtin functions" + }, + "fn-is_err": { + "name": "is_err", + "sig": "is_err(r: result) -> bool", + "tip": "True when a result carries a failure.", + "kind": "builtin", + "page": "fn-is_err.html", + "section": "Builtin functions" + }, + "fn-some": { + "name": "some", + "sig": "some(v) -> option", + "tip": "Wrap a present value in an option.", + "kind": "builtin", + "page": "fn-some.html", + "section": "Builtin functions" + }, + "fn-none": { + "name": "none", + "sig": "none() -> option", + "tip": "The empty option — a missing value.", + "kind": "builtin", + "page": "fn-none.html", + "section": "Builtin functions" + }, + "fn-is_some": { + "name": "is_some", + "sig": "is_some(o: option) -> bool", + "tip": "True when an option holds a value.", + "kind": "builtin", + "page": "fn-is_some.html", + "section": "Builtin functions" + }, + "fn-is_none": { + "name": "is_none", + "sig": "is_none(o: option) -> bool", + "tip": "True when an option is empty.", + "kind": "builtin", + "page": "fn-is_none.html", + "section": "Builtin functions" + }, + "fn-unwrap_or": { + "name": "unwrap_or", + "sig": "unwrap_or(o: option, fallback) -> int", + "tip": "The option value, or a fallback if empty.", + "kind": "builtin", + "page": "fn-unwrap_or.html", + "section": "Builtin functions" + }, + "fn-exit": { + "name": "exit", + "sig": "exit(code)", + "tip": "Terminate the process immediately with a status code.", + "kind": "builtin", + "page": "fn-exit.html", + "section": "Builtin functions" + }, + "fn-run": { + "name": "run", + "sig": "run(cmd)", + "tip": "Run a string as a shell command.", + "kind": "builtin", + "page": "fn-run.html", + "section": "Builtin functions" + }, + "fn-getenv": { + "name": "getenv", + "sig": "getenv(name) -> string", + "tip": "Read an environment variable, returning empty when it is unset.", + "kind": "builtin", + "page": "fn-getenv.html", + "section": "Builtin functions" + }, + "fn-read_char": { + "name": "read_char", + "sig": "read_char() -> int", + "tip": "Read one byte from standard input, or -1 at end of input.", + "kind": "builtin", + "page": "fn-read_char.html", + "section": "Builtin functions" + }, + "fn-arg": { + "name": "arg", + "sig": "arg(i) -> string", + "tip": "The i-th command-line argument as a string.", + "kind": "builtin", + "page": "fn-arg.html", + "section": "Builtin functions" + }, + "fn-arg_count": { + "name": "arg_count", + "sig": "arg_count() -> int", + "tip": "The number of command-line arguments, counting the program name.", + "kind": "builtin", + "page": "fn-arg_count.html", + "section": "Builtin functions" + }, + "fn-bytes": { + "name": "bytes", + "sig": "bytes(n) -> pointer", + "tip": "Allocate a raw buffer of n bytes and return a pointer to it.", + "kind": "builtin", + "page": "fn-bytes.html", + "section": "Builtin functions" + }, + "fn-file_stderr": { + "name": "file_stderr", + "sig": "file_stderr() -> pointer", + "tip": "The standard-error stream handle for use with file_write.", + "kind": "builtin", + "page": "fn-file_stderr.html", + "section": "Builtin functions" + }, + "fn-file_stdout": { + "name": "file_stdout", + "sig": "file_stdout() -> pointer", + "tip": "The standard-output stream handle for use with file_write.", + "kind": "builtin", + "page": "fn-file_stdout.html", + "section": "Builtin functions" + }, + "fn-file_write": { + "name": "file_write", + "sig": "file_write(handle, buf, len)", + "tip": "Write a run of raw bytes to a stream handle.", + "kind": "builtin", + "page": "fn-file_write.html", + "section": "Builtin functions" + }, + "fn-fixed": { + "name": "fixed", + "sig": "fixed(x) -> fixed", + "tip": "Convert an int (or a float) into a Q16.16 fixed-point value.", + "kind": "builtin", + "page": "fn-fixed.html", + "section": "Builtin functions" + }, + "fn-floor": { + "name": "floor", + "sig": "floor(x) -> int", + "tip": "Floor a fixed-point value down to the nearest integer.", + "kind": "builtin", + "page": "fn-floor.html", + "section": "Builtin functions" + }, + "fn-max": { + "name": "max", + "sig": "max(a, b) -> int", + "tip": "The larger of two integers.", + "kind": "builtin", + "page": "fn-max.html", + "section": "Builtin functions" + }, + "fn-min": { + "name": "min", + "sig": "min(a, b) -> int", + "tip": "The smaller of two integers.", + "kind": "builtin", + "page": "fn-min.html", + "section": "Builtin functions" + }, + "fn-ui_build": { + "name": "ui_build", + "sig": "ui_build()", + "tip": "Build the declared UI tree so it can be opened and rendered.", + "kind": "builtin", + "page": "fn-ui_build.html", + "section": "Builtin functions" + }, + "fn-words": { + "name": "words", + "sig": "words(n) -> words", + "tip": "Allocate a raw buffer of n 32-bit words, indexable with [i].", + "kind": "builtin", + "page": "fn-words.html", + "section": "Builtin functions" + }, + "fn-float": { + "name": "float", + "sig": "float(x) -> float / double(x) -> double", + "tip": "Convert any number to a float (or double).", + "kind": "builtin", + "page": "fn-float.html", + "section": "Builtin functions" + }, + "fn-int": { + "name": "int", + "sig": "int(x) -> int / long(x) -> long", + "tip": "Convert a number to a whole number, truncating toward zero.", + "kind": "builtin", + "page": "fn-int.html", + "section": "Builtin functions" + }, + "fn-float_bits": { + "name": "float_bits", + "sig": "float_bits(x) -> int / float_from_bits(bits) -> float", + "tip": "A float's IEEE bit pattern as an int, and back.", + "kind": "builtin", + "page": "fn-float_bits.html", + "section": "Builtin functions" + }, + "set-new": { + "name": "Set.new", + "sig": "Set.new() -> Set", + "tip": "Create an empty string set.", + "kind": "namespace-method", + "page": "set-new.html", + "section": "Set" + }, + "set-add": { + "name": "Set.add", + "sig": "Set.add(s, key)", + "tip": "Add a member (duplicates ignored).", + "kind": "namespace-method", + "page": "set-add.html", + "section": "Set" + }, + "set-has": { + "name": "Set.has", + "sig": "Set.has(s, key) -> bool", + "tip": "Is a member present?", + "kind": "namespace-method", + "page": "set-has.html", + "section": "Set" + }, + "set-remove": { + "name": "Set.remove", + "sig": "Set.remove(s, key)", + "tip": "Remove a member.", + "kind": "namespace-method", + "page": "set-remove.html", + "section": "Set" + }, + "set-size": { + "name": "Set.size", + "sig": "Set.size(s) -> int", + "tip": "How many members.", + "kind": "namespace-method", + "page": "set-size.html", + "section": "Set" + }, + "set-clear": { + "name": "Set.clear", + "sig": "Set.clear(s)", + "tip": "Remove every member.", + "kind": "namespace-method", + "page": "set-clear.html", + "section": "Set" + }, + "set-members": { + "name": "Set.members", + "sig": "Set.members(s) -> []pointer", + "tip": "Every member, as a slice of strings.", + "kind": "namespace-method", + "page": "set-members.html", + "section": "Set" + }, + "annot-queries": { + "name": "@Queries", + "sig": "@Queries(these: [Position, Velocity])", + "tip": "Declare the properties a handler operates on, binding their fields by name in the body.", + "kind": "annotation", + "page": "annot-queries.html", + "section": "Annotations" + }, + "annot-computed": { + "name": "@Computed", + "sig": "@Computed on a property field", + "tip": "A derived field expanded inline wherever it is read, never stored.", + "kind": "annotation", + "page": "annot-computed.html", + "section": "Annotations" + }, + "annot-on": { + "name": "@On", + "sig": "@On(Event) handler Name { … }", + "tip": "Register a compile-time listener that runs whenever an event is emitted.", + "kind": "annotation", + "page": "annot-on.html", + "section": "Annotations" + }, + "annot-export": { + "name": "@export", + "sig": "@export function name(…) -> R", + "tip": "Expose a function as a native symbol so a host can call it.", + "kind": "annotation", + "page": "annot-export.html", + "section": "Annotations" + }, + "annot-sync": { + "name": "@Sync", + "sig": "@Sync property … / @Owned", + "tip": "Mark fields as replicated and models as owned so the compiler generates networking.", + "kind": "annotation", + "page": "annot-sync.html", + "section": "Annotations" + }, + "annot-handles": { + "name": "@Handles", + "sig": "@Handles(Event) handler Name { … }", + "tip": "Declarative hint naming the event or subsystem a handler is responsible for.", + "kind": "annotation", + "page": "annot-handles.html", + "section": "Annotations" + }, + "annot-onattach": { + "name": "@OnAttach", + "sig": "@OnAttach(Property) handler Name { … }", + "tip": "Run a handler when a property is structurally attached to a live instance.", + "kind": "annotation", + "page": "annot-onattach.html", + "section": "Annotations" + }, + "annot-ondespawn": { + "name": "@OnDespawn", + "sig": "@OnDespawn(Model, reason: r) handler Name { … }", + "tip": "Run a handler when a model instance is torn down, optionally knowing why.", + "kind": "annotation", + "page": "annot-ondespawn.html", + "section": "Annotations" + }, + "annot-ondetach": { + "name": "@OnDetach", + "sig": "@OnDetach(Property) handler Name { … }", + "tip": "Run a handler when a property is structurally detached from a live instance.", + "kind": "annotation", + "page": "annot-ondetach.html", + "section": "Annotations" + }, + "annot-ondisable": { + "name": "@OnDisable", + "sig": "@OnDisable(Property) handler Name { … }", + "tip": "Run a handler when a property is paused (disabled) on an instance.", + "kind": "annotation", + "page": "annot-ondisable.html", + "section": "Annotations" + }, + "annot-onenable": { + "name": "@OnEnable", + "sig": "@OnEnable(Property) handler Name { … }", + "tip": "Run a handler when a paused property is re-enabled on an instance.", + "kind": "annotation", + "page": "annot-onenable.html", + "section": "Annotations" + }, + "annot-onquit": { + "name": "@OnQuit", + "sig": "@OnQuit handler Name { … }", + "tip": "Run a handler once at shutdown, after the last frame.", + "kind": "annotation", + "page": "annot-onquit.html", + "section": "Annotations" + }, + "annot-onspawn": { + "name": "@OnSpawn", + "sig": "@OnSpawn(Model) handler Name { … }", + "tip": "Run a handler once each time an instance of a model is spawned.", + "kind": "annotation", + "page": "annot-onspawn.html", + "section": "Annotations" + }, + "annot-onstart": { + "name": "@OnStart", + "sig": "@OnStart handler Name { … }", + "tip": "Run a handler once at boot instead of assigning it a frame phase.", + "kind": "annotation", + "page": "annot-onstart.html", + "section": "Annotations" + }, + "annot-owned": { + "name": "@Owned", + "sig": "@Owned model Name { … }", + "tip": "Give a model a network owner slot so its instances can be assigned to a peer.", + "kind": "annotation", + "page": "annot-owned.html", + "section": "Annotations" + }, + "annot-predicted": { + "name": "@Predicted", + "sig": "@Predicted handler Name { … }", + "tip": "Run a control handler on the owning client speculatively and on the server authoritatively.", + "kind": "annotation", + "page": "annot-predicted.html", + "section": "Annotations" + }, + "annot-public": { + "name": "@Public", + "sig": "@Public @OnSpawn(Model) handler … { … }", + "tip": "Promote a lifecycle hook to a public event other modules can listen for.", + "kind": "annotation", + "page": "annot-public.html", + "section": "Annotations" + }, + "annot-reads": { + "name": "@Reads", + "sig": "@Reads(Property)", + "tip": "Declare that a handler reads a property — an analysis and scheduling hint.", + "kind": "annotation", + "page": "annot-reads.html", + "section": "Annotations" + }, + "annot-server": { + "name": "@Server", + "sig": "@Server handler Name { … }", + "tip": "Run a handler only on the authority; clients receive the result via @Sync.", + "kind": "annotation", + "page": "annot-server.html", + "section": "Annotations" + }, + "annot-toclients": { + "name": "@ToClients", + "sig": "@ToClients event Name { … }", + "tip": "A remote event broadcast from the server to clients — a server→clients notification.", + "kind": "annotation", + "page": "annot-toclients.html", + "section": "Annotations" + }, + "annot-toserver": { + "name": "@ToServer", + "sig": "@ToServer event Name { … }", + "tip": "A remote event sent from a client to the server — a client→server request.", + "kind": "annotation", + "page": "annot-toserver.html", + "section": "Annotations" + }, + "annot-writes": { + "name": "@Writes", + "sig": "@Writes(Property)", + "tip": "Declare that a handler writes a property — an analysis and scheduling hint.", + "kind": "annotation", + "page": "annot-writes.html", + "section": "Annotations" + }, + "annot-enginesystem": { + "name": "@EngineSystem", + "sig": "\"@EngineSystem(Component, Phase) function esys_name() { … }\"", + "tip": "Register a package's engine-owned system so the frame loop runs it each phase.", + "kind": "annotation", + "page": "annot-enginesystem.html", + "section": "Annotations" + }, + "annot-namespace": { + "name": "@Namespace", + "sig": "\"@Namespace(Name) function name_method(…) -> T { … }\"", + "tip": "Let a package provide a Name.method(…) namespace that dispatches to name_method.", + "kind": "annotation", + "page": "annot-namespace.html", + "section": "Annotations" + }, + "annot-clearcolor": { + "name": "\"@ClearColor\"", + "sig": "\"@ClearColor(0xRRGGBB) handler Draw phase Render { … }\"", + "tip": "Declare a clear colour so the Render phase auto-clears + auto-presents for you.", + "kind": "annotation", + "page": "annot-clearcolor.html", + "section": "Annotations" + }, + "annot-system": { + "name": "@System", + "sig": "\"@System(Phase) function name() { … }\"", + "tip": "Register a prebuilt binary module's system with the host at load, per phase.", + "kind": "annotation", + "page": "annot-system.html", + "section": "Annotations" + }, + "huge-from": { + "name": "Huge.from", + "sig": "Huge.from(value) -> Huge", + "tip": "Turn an int into a Huge.", + "kind": "namespace-method", + "page": "huge-from.html", + "section": "Huge" + }, + "huge-add": { + "name": "Huge.add", + "sig": "Huge.add(a, b) -> Huge", + "tip": "Sum of two big numbers.", + "kind": "namespace-method", + "page": "huge-add.html", + "section": "Huge" + }, + "huge-sub": { + "name": "Huge.sub", + "sig": "Huge.sub(a, b) -> Huge", + "tip": "Difference of two big numbers.", + "kind": "namespace-method", + "page": "huge-sub.html", + "section": "Huge" + }, + "huge-mul": { + "name": "Huge.mul", + "sig": "Huge.mul(a, b) -> Huge", + "tip": "Product of two big numbers.", + "kind": "namespace-method", + "page": "huge-mul.html", + "section": "Huge" + }, + "huge-neg": { + "name": "Huge.neg", + "sig": "Huge.neg(a) -> Huge", + "tip": "Negate a big number.", + "kind": "namespace-method", + "page": "huge-neg.html", + "section": "Huge" + }, + "huge-cmp": { + "name": "Huge.cmp", + "sig": "Huge.cmp(a, b) -> int", + "tip": "Compare two big numbers: -1, 0, or 1.", + "kind": "namespace-method", + "page": "huge-cmp.html", + "section": "Huge" + }, + "huge-sign": { + "name": "Huge.sign", + "sig": "Huge.sign(a) -> int", + "tip": "The sign: -1, 0, or 1.", + "kind": "namespace-method", + "page": "huge-sign.html", + "section": "Huge" + }, + "huge-mantissa": { + "name": "Huge.mantissa", + "sig": "Huge.mantissa(a) -> fixed", + "tip": "The mantissa in [1, 10).", + "kind": "namespace-method", + "page": "huge-mantissa.html", + "section": "Huge" + }, + "huge-exp": { + "name": "Huge.exp", + "sig": "Huge.exp(a) -> int", + "tip": "The base-10 exponent.", + "kind": "namespace-method", + "page": "huge-exp.html", + "section": "Huge" + }, + "huge-str": { + "name": "Huge.str", + "sig": "Huge.str(a) -> string", + "tip": "Render as scientific text like 1.23e45.", + "kind": "namespace-method", + "page": "huge-str.html", + "section": "Huge" + }, + "angle-from_degrees": { + "name": "Angle.from_degrees", + "sig": "Angle.from_degrees(d) -> fixed", + "tip": "Degrees to a wrapped radian angle.", + "kind": "namespace-method", + "page": "angle-from_degrees.html", + "section": "Angle" + }, + "angle-to_degrees": { + "name": "Angle.to_degrees", + "sig": "Angle.to_degrees(a) -> fixed", + "tip": "Radians back to degrees.", + "kind": "namespace-method", + "page": "angle-to_degrees.html", + "section": "Angle" + }, + "angle-wrap": { + "name": "Angle.wrap", + "sig": "Angle.wrap(a) -> fixed", + "tip": "Normalize any radian value to [-pi, pi).", + "kind": "namespace-method", + "page": "angle-wrap.html", + "section": "Angle" + }, + "angle-sin": { + "name": "Angle.sin", + "sig": "Angle.sin(a) -> fixed", + "tip": "Sine of an angle.", + "kind": "namespace-method", + "page": "angle-sin.html", + "section": "Angle" + }, + "angle-cos": { + "name": "Angle.cos", + "sig": "Angle.cos(a) -> fixed", + "tip": "Cosine of an angle.", + "kind": "namespace-method", + "page": "angle-cos.html", + "section": "Angle" + }, + "angle-add": { + "name": "Angle.add", + "sig": "Angle.add(a, b) -> fixed", + "tip": "Add two angles (wrapped).", + "kind": "namespace-method", + "page": "angle-add.html", + "section": "Angle" + }, + "angle-diff": { + "name": "Angle.diff", + "sig": "Angle.diff(a, b) -> fixed", + "tip": "Shortest signed rotation from a to b.", + "kind": "namespace-method", + "page": "angle-diff.html", + "section": "Angle" + }, + "angle-lerp": { + "name": "Angle.lerp", + "sig": "Angle.lerp(a, b, t) -> fixed", + "tip": "Interpolate along the shortest arc.", + "kind": "namespace-method", + "page": "angle-lerp.html", + "section": "Angle" + }, + "angle-diff_degrees": { + "name": "Angle.diff_degrees", + "sig": "Angle.diff_degrees(a: int, b: int) -> int", + "tip": "The signed difference between two headings in whole degrees.", + "kind": "namespace-method", + "page": "angle-diff_degrees.html", + "section": "Angle" + }, + "percent-clamp": { + "name": "Percent.clamp", + "sig": "Percent.clamp(v) -> fixed", + "tip": "Clamp any fixed into [0, 1].", + "kind": "namespace-method", + "page": "percent-clamp.html", + "section": "Percent" + }, + "percent-of": { + "name": "Percent.of", + "sig": "Percent.of(num, den) -> fixed", + "tip": "Clamped ratio num / den.", + "kind": "namespace-method", + "page": "percent-of.html", + "section": "Percent" + }, + "percent-lerp": { + "name": "Percent.lerp", + "sig": "Percent.lerp(a, b, t) -> fixed", + "tip": "Interpolate a..b by a clamped t.", + "kind": "namespace-method", + "page": "percent-lerp.html", + "section": "Percent" + }, + "percent-apply": { + "name": "Percent.apply", + "sig": "Percent.apply(value, p) -> fixed", + "tip": "Scale a value by a clamped percent.", + "kind": "namespace-method", + "page": "percent-apply.html", + "section": "Percent" + }, + "regex-compile": { + "name": "Regex.compile", + "sig": "Regex.compile(pattern) -> Regex", + "tip": "Compile a pattern once for reuse; returns an error value (null) if it is invalid.", + "kind": "namespace-method", + "page": "regex-compile.html", + "section": "Regex" + }, + "regex-valid": { + "name": "Regex.valid", + "sig": "Regex.valid(pattern) -> bool", + "tip": "True if the pattern is well-formed (compiles without error).", + "kind": "namespace-method", + "page": "regex-valid.html", + "section": "Regex" + }, + "regex-matches": { + "name": "Regex.matches", + "sig": "Regex.matches(text, pattern) -> bool", + "tip": "True if the pattern matches anywhere in the text (a search).", + "kind": "namespace-method", + "page": "regex-matches.html", + "section": "Regex" + }, + "regex-test": { + "name": "Regex.test", + "sig": "Regex.test(text, re) -> bool", + "tip": "Like matches, but reuses an already-compiled Regex.", + "kind": "namespace-method", + "page": "regex-test.html", + "section": "Regex" + }, + "regex-find": { + "name": "Regex.find", + "sig": "Regex.find(text, pattern) -> Match", + "tip": "The first match of the pattern in the text (a Match, or null if none).", + "kind": "namespace-method", + "page": "regex-find.html", + "section": "Regex" + }, + "regex-exec": { + "name": "Regex.exec", + "sig": "Regex.exec(text, re) -> Match", + "tip": "Like find, but with an already-compiled Regex.", + "kind": "namespace-method", + "page": "regex-exec.html", + "section": "Regex" + }, + "regex-next": { + "name": "Regex.next", + "sig": "Regex.next(text, re, from) -> Match", + "tip": "The next match at or after byte offset from — the basis of a find-all loop.", + "kind": "namespace-method", + "page": "regex-next.html", + "section": "Regex" + }, + "regex-replace": { + "name": "Regex.replace", + "sig": "Regex.replace(text, pattern, replacement) -> string", + "tip": "Replace every match; the replacement expands \\0..\\9 group references.", + "kind": "namespace-method", + "page": "regex-replace.html", + "section": "Regex" + }, + "regex-group": { + "name": "Regex.group", + "sig": "Regex.group(match, n) -> string", + "tip": "The text of capture group n (group 0 is the whole match); empty if unset.", + "kind": "namespace-method", + "page": "regex-group.html", + "section": "Regex" + }, + "regex-group_count": { + "name": "Regex.group_count", + "sig": "Regex.group_count(match) -> int", + "tip": "How many capture groups the match's pattern has.", + "kind": "namespace-method", + "page": "regex-group_count.html", + "section": "Regex" + }, + "regex-start": { + "name": "Regex.start", + "sig": "Regex.start(match, n) -> int", + "tip": "The start byte offset of group n, or -1 if it did not participate.", + "kind": "namespace-method", + "page": "regex-start.html", + "section": "Regex" + }, + "regex-end": { + "name": "Regex.end", + "sig": "Regex.end(match, n) -> int", + "tip": "The end byte offset of group n, or -1 if it did not participate.", + "kind": "namespace-method", + "page": "regex-end.html", + "section": "Regex" + }, + "regex-ok": { + "name": "Regex.ok", + "sig": "Regex.ok(match) -> bool", + "tip": "True if the match succeeded (the Match is non-null).", + "kind": "namespace-method", + "page": "regex-ok.html", + "section": "Regex" + }, + "grid-line": { + "name": "Grid.line", + "sig": "Grid.line(x0, y0, x1, y1) -> []Cell", + "tip": "Every cell a straight line from (x0,y0) to (x1,y1) crosses (Bresenham).", + "kind": "namespace-method", + "page": "grid-line.html", + "section": "Grid" + }, + "grid-blocked": { + "name": "Grid.blocked", + "sig": "Grid.blocked(x, y, wall) -> bool", + "tip": "True if the cell is out of bounds or holds the wall tile.", + "kind": "namespace-method", + "page": "grid-blocked.html", + "section": "Grid" + }, + "grid-line_of_sight": { + "name": "Grid.line_of_sight", + "sig": "Grid.line_of_sight(x0, y0, x1, y1, wall) -> bool", + "tip": "True if the straight line between two cells crosses no wall.", + "kind": "namespace-method", + "page": "grid-line_of_sight.html", + "section": "Grid" + }, + "grid-flood": { + "name": "Grid.flood", + "sig": "Grid.flood(x, y, wall) -> []Cell", + "tip": "Every passable cell reachable from (x,y), 4-connected, in BFS order.", + "kind": "namespace-method", + "page": "grid-flood.html", + "section": "Grid" + }, + "grid-a_star": { + "name": "Grid.a_star", + "sig": "Grid.a_star(x0, y0, x1, y1, wall) -> []Cell", + "tip": "The shortest 4-connected path between two cells (A*), or an empty list.", + "kind": "namespace-method", + "page": "grid-a_star.html", + "section": "Grid" + }, + "anim-frame": { + "name": "Anim.frame", + "sig": "Anim.frame(timer, fps, count) -> int", + "tip": "The looping frame index for an elapsed timer at a given fps.", + "kind": "namespace-method", + "page": "anim-frame.html", + "section": "Anim" + }, + "anim-once": { + "name": "Anim.once", + "sig": "Anim.once(timer, fps, count) -> int", + "tip": "A non-looping frame index that clamps on the last frame.", + "kind": "namespace-method", + "page": "anim-once.html", + "section": "Anim" + }, + "anim-pingpong": { + "name": "Anim.pingpong", + "sig": "Anim.pingpong(timer, fps, count) -> int", + "tip": "A frame index that bounces 0..count-1..0 and repeats.", + "kind": "namespace-method", + "page": "anim-pingpong.html", + "section": "Anim" + }, + "anim-finished": { + "name": "Anim.finished", + "sig": "Anim.finished(timer, fps, count) -> bool", + "tip": "True once a one-shot clip has run past its last frame.", + "kind": "namespace-method", + "page": "anim-finished.html", + "section": "Anim" + }, + "anim-duration": { + "name": "Anim.duration", + "sig": "Anim.duration(fps, count) -> fixed", + "tip": "Seconds for one full cycle of a clip: count / fps.", + "kind": "namespace-method", + "page": "anim-duration.html", + "section": "Anim" + }, + "anim-cell_x": { + "name": "Anim.cell_x", + "sig": "Anim.cell_x(frame, cols, cell_w) -> int", + "tip": "The source x (pixels) of a frame on a grid spritesheet.", + "kind": "namespace-method", + "page": "anim-cell_x.html", + "section": "Anim" + }, + "anim-cell_y": { + "name": "Anim.cell_y", + "sig": "Anim.cell_y(frame, cols, cell_h) -> int", + "tip": "The source y (pixels) of a frame on a grid spritesheet.", + "kind": "namespace-method", + "page": "anim-cell_y.html", + "section": "Anim" + }, + "anim-play": { + "name": "Anim.play", + "sig": "Anim.play(entity, clip) | Anim.play(entity, fps, frames, mode)", + "tip": "Start (or restart) a spritesheet clip on an entity in one call.", + "kind": "namespace-method", + "page": "anim-play.html", + "section": "Anim" + }, + "anim-clip": { + "name": "Anim.clip", + "sig": "Anim.clip(name, frames, fps, mode)", + "tip": "Register a named spritesheet clip so Anim.play can play it by name.", + "kind": "namespace-method", + "page": "anim-clip.html", + "section": "Anim" + }, + "anim-on_frame": { + "name": "Anim.on_frame", + "sig": "Anim.on_frame(entity, frame)", + "tip": "Arm a frame event — the engine flags the tick a clip lands on this frame.", + "kind": "namespace-method", + "page": "anim-on_frame.html", + "section": "Anim" + }, + "anim-fired": { + "name": "Anim.fired", + "sig": "Anim.fired(entity) -> bool", + "tip": "Did the entity's clip land on its armed frame event this tick?", + "kind": "namespace-method", + "page": "anim-fired.html", + "section": "Anim" + }, + "motion-to": { + "name": "Motion.to", + "sig": "Motion.to(entity, from, to, dur, ease)", + "tip": "Start a value tween on an entity's Motion component in one call.", + "kind": "namespace-method", + "page": "motion-to.html", + "section": "Motion" + }, + "tween-progress": { + "name": "Tween.progress", + "sig": "Tween.progress(timer, duration) -> fixed", + "tip": "A one-shot 0..1 amount, clamped, for timer over duration.", + "kind": "namespace-method", + "page": "tween-progress.html", + "section": "Tween" + }, + "tween-loop": { + "name": "Tween.loop", + "sig": "Tween.loop(timer, duration) -> fixed", + "tip": "A repeating 0..1 sawtooth amount over duration.", + "kind": "namespace-method", + "page": "tween-loop.html", + "section": "Tween" + }, + "tween-yoyo": { + "name": "Tween.yoyo", + "sig": "Tween.yoyo(timer, duration) -> fixed", + "tip": "A repeating 0..1..0 triangle amount over duration.", + "kind": "namespace-method", + "page": "tween-yoyo.html", + "section": "Tween" + }, + "tween-done": { + "name": "Tween.done", + "sig": "Tween.done(timer, duration) -> bool", + "tip": "True once a one-shot tween's timer reaches its duration.", + "kind": "namespace-method", + "page": "tween-done.html", + "section": "Tween" + }, + "tween-ease": { + "name": "Tween.ease", + "sig": "Tween.ease(t, mode) -> fixed", + "tip": "Shape a 0..1 amount through an easing curve chosen by a literal mode.", + "kind": "namespace-method", + "page": "tween-ease.html", + "section": "Tween" + }, + "tween-number": { + "name": "Tween.number", + "sig": "Tween.number(from, to, t) -> fixed", + "tip": "Linear blend of two fixeds by amount t.", + "kind": "namespace-method", + "page": "tween-number.html", + "section": "Tween" + }, + "tween-round": { + "name": "Tween.round", + "sig": "Tween.round(from, to, t) -> int", + "tip": "Linear blend of two ints by amount t, rounded to the nearest int.", + "kind": "namespace-method", + "page": "tween-round.html", + "section": "Tween" + }, + "tween-point": { + "name": "Tween.point", + "sig": "Tween.point(from, to, t) -> Vector", + "tip": "Component-wise blend of two Vectors by amount t.", + "kind": "namespace-method", + "page": "tween-point.html", + "section": "Tween" + }, + "tween-tint": { + "name": "Tween.tint", + "sig": "Tween.tint(from, to, t) -> int", + "tip": "Per-channel blend of two colors by amount t.", + "kind": "namespace-method", + "page": "tween-tint.html", + "section": "Tween" + }, + "tween-to": { + "name": "Tween.to", + "sig": "Tween.to(from, to, dur, ease) -> handle", + "tip": "Start a fluent, engine-advanced tween and return a handle.", + "kind": "namespace-method", + "page": "tween-to.html", + "section": "Tween" + }, + "tween-chain": { + "name": "Tween.chain", + "sig": "Tween.chain(handle, to, dur, ease) -> handle", + "tip": "Append a tween segment that runs after the handle's current queue.", + "kind": "namespace-method", + "page": "tween-chain.html", + "section": "Tween" + }, + "tween-delay": { + "name": "Tween.delay", + "sig": "Tween.delay(handle, ticks) -> handle", + "tip": "Append a pause to a tween handle's sequence.", + "kind": "namespace-method", + "page": "tween-delay.html", + "section": "Tween" + }, + "tween-value": { + "name": "Tween.value", + "sig": "Tween.value(handle) -> int", + "tip": "The current value of a fluent tween handle this frame.", + "kind": "namespace-method", + "page": "tween-value.html", + "section": "Tween" + }, + "tween-stop": { + "name": "Tween.stop", + "sig": "Tween.stop(handle)", + "tip": "Stop and dispose a tween handle, freezing its value.", + "kind": "namespace-method", + "page": "tween-stop.html", + "section": "Tween" + }, + "tween-parallel": { + "name": "Tween.parallel", + "sig": "Tween.parallel(a, b) -> bool", + "tip": "Are two tween handles both finished? — a parallel completion query.", + "kind": "namespace-method", + "page": "tween-parallel.html", + "section": "Tween" + }, + "query-count": { + "name": "Query.count", + "sig": "Query.count(prop) -> int", + "tip": "How many live entities carry a property.", + "kind": "namespace-method", + "page": "query-count.html", + "section": "Query" + }, + "query-first": { + "name": "Query.first", + "sig": "Query.first(prop) -> int", + "tip": "The lowest-id live entity carrying a property, or -1.", + "kind": "namespace-method", + "page": "query-first.html", + "section": "Query" + }, + "query-nearest": { + "name": "Query.nearest", + "sig": "Query.nearest(prop, pos, x_field, y_field, x, y) -> int", + "tip": "The bearer of prop closest to a point, by squared distance.", + "kind": "namespace-method", + "page": "query-nearest.html", + "section": "Query" + }, + "query-within": { + "name": "Query.within", + "sig": "Query.within(prop, pos, x, y, radius, x_field, y_field) -> []int", + "tip": "Every bearer of prop inside a radius of a point.", + "kind": "namespace-method", + "page": "query-within.html", + "section": "Query" + }, + "reflect-prop": { + "name": "Reflect.prop", + "sig": "Reflect.prop(name) -> int", + "tip": "The property id for a property name, or -1 if unknown.", + "kind": "namespace-method", + "page": "reflect-prop.html", + "section": "Reflect" + }, + "reflect-field": { + "name": "Reflect.field", + "sig": "Reflect.field(prop, name) -> int", + "tip": "The field id of a named field within a property, or -1.", + "kind": "namespace-method", + "page": "reflect-field.html", + "section": "Reflect" + }, + "reflect-prop_count": { + "name": "Reflect.prop_count", + "sig": "Reflect.prop_count() -> int", + "tip": "How many properties the world schema defines.", + "kind": "namespace-method", + "page": "reflect-prop_count.html", + "section": "Reflect" + }, + "reflect-prop_name": { + "name": "Reflect.prop_name", + "sig": "Reflect.prop_name(index) -> string", + "tip": "The name of the property at an index (or \"\").", + "kind": "namespace-method", + "page": "reflect-prop_name.html", + "section": "Reflect" + }, + "reflect-field_count": { + "name": "Reflect.field_count", + "sig": "Reflect.field_count(prop) -> int", + "tip": "How many fields a property has.", + "kind": "namespace-method", + "page": "reflect-field_count.html", + "section": "Reflect" + }, + "reflect-field_name": { + "name": "Reflect.field_name", + "sig": "Reflect.field_name(prop, index) -> string", + "tip": "The name of the field at an index within a property.", + "kind": "namespace-method", + "page": "reflect-field_name.html", + "section": "Reflect" + }, + "reflect-field_type": { + "name": "Reflect.field_type", + "sig": "Reflect.field_type(prop, index) -> string", + "tip": "The type name of the field at an index within a property.", + "kind": "namespace-method", + "page": "reflect-field_type.html", + "section": "Reflect" + }, + "reflect-get": { + "name": "Reflect.get", + "sig": "Reflect.get(entity, prop, field) -> int", + "tip": "Read one field of an entity by (prop, field) id.", + "kind": "namespace-method", + "page": "reflect-get.html", + "section": "Reflect" + }, + "reflect-set": { + "name": "Reflect.set", + "sig": "Reflect.set(entity, prop, field, value)", + "tip": "Write one field of an entity by (prop, field) id.", + "kind": "namespace-method", + "page": "reflect-set.html", + "section": "Reflect" + }, + "reflect-has": { + "name": "Reflect.has", + "sig": "Reflect.has(entity, prop) -> bool", + "tip": "Does an entity carry a property?", + "kind": "namespace-method", + "page": "reflect-has.html", + "section": "Reflect" + }, + "reflect-kind": { + "name": "Reflect.kind", + "sig": "Reflect.kind(entity) -> int", + "tip": "The model id of an entity.", + "kind": "namespace-method", + "page": "reflect-kind.html", + "section": "Reflect" + }, + "reflect-model": { + "name": "Reflect.model", + "sig": "Reflect.model(name) -> int", + "tip": "The model id for a model name, or -1 if unknown.", + "kind": "namespace-method", + "page": "reflect-model.html", + "section": "Reflect" + }, + "reflect-serialize": { + "name": "Reflect.serialize", + "sig": "Reflect.serialize(entity) -> value", + "tip": "Walk an entity's components into a value tree.", + "kind": "namespace-method", + "page": "reflect-serialize.html", + "section": "Reflect" + }, + "reflect-apply": { + "name": "Reflect.apply", + "sig": "Reflect.apply(entity, value) -> int", + "tip": "Write a value tree's fields back into an entity.", + "kind": "namespace-method", + "page": "reflect-apply.html", + "section": "Reflect" + }, + "camera-set": { + "name": "Camera.set", + "sig": "Camera.set(x, y)", + "tip": "Place the camera at a world-space offset.", + "kind": "namespace-method", + "page": "camera-set.html", + "section": "Camera" + }, + "camera-follow": { + "name": "Camera.follow", + "sig": "Camera.follow(x, y, lerp)", + "tip": "Ease the camera toward centring a target point.", + "kind": "namespace-method", + "page": "camera-follow.html", + "section": "Camera" + }, + "camera-shake": { + "name": "Camera.shake", + "sig": "Camera.shake(amount)", + "tip": "Jitter the camera by up to +/- amount pixels (seeded RNG).", + "kind": "namespace-method", + "page": "camera-shake.html", + "section": "Camera" + }, + "camera-zoom": { + "name": "Camera.zoom", + "sig": "Camera.zoom(scale)", + "tip": "Scale the whole view about the screen centre by a Q16.16 factor (1.0 = none).", + "kind": "namespace-method", + "page": "camera-zoom.html", + "section": "Camera" + }, + "camera-shake_for": { + "name": "Camera.shake_for", + "sig": "Camera.shake_for(amount: px, frames: n)", + "tip": "Shake for a number of frames, then stop, with no bookkeeping.", + "kind": "namespace-method", + "page": "camera-shake_for.html", + "section": "Camera" + }, + "light-ambient": { + "name": "Light.ambient", + "sig": "Light.ambient(color)", + "tip": "Multiply the whole scene by a tint — the night/cave modulate.", + "kind": "namespace-method", + "page": "light-ambient.html", + "section": "Light" + }, + "light-point": { + "name": "Light.point", + "sig": "Light.point(x, y, radius, color, energy)", + "tip": "Add a radial glow that falls off with distance.", + "kind": "namespace-method", + "page": "light-point.html", + "section": "Light" + }, + "light-occlude": { + "name": "Light.occlude", + "sig": "Light.occlude(x, y, width, height)", + "tip": "Register a rectangle that blocks light — a hard shadow caster.", + "kind": "namespace-method", + "page": "light-occlude.html", + "section": "Light" + }, + "light-clear_occluders": { + "name": "Light.clear_occluders", + "sig": "Light.clear_occluders()", + "tip": "Forget every occluder — call once per frame before re-registering.", + "kind": "namespace-method", + "page": "light-clear_occluders.html", + "section": "Light" + }, + "light-spot": { + "name": "Light.spot", + "sig": "Light.spot(x, y, radius, color, energy, direction, spread)", + "tip": "A cone / flashlight light aimed at a direction with a half-angle spread.", + "kind": "namespace-method", + "page": "light-spot.html", + "section": "Light" + }, + "light-falloff": { + "name": "Light.falloff", + "sig": "Light.falloff(exponent)", + "tip": "Set the brightness-ramp exponent for later lights (1 linear, 2 quadratic…).", + "kind": "namespace-method", + "page": "light-falloff.html", + "section": "Light" + }, + "light-soft": { + "name": "Light.soft", + "sig": "Light.soft(radius)", + "tip": "Soft shadows — an occluder edge fades through a penumbra of this radius.", + "kind": "namespace-method", + "page": "light-soft.html", + "section": "Light" + }, + "light-gel": { + "name": "Light.gel", + "sig": "Light.gel(color)", + "tip": "A colour cookie — later lights gel from their centre colour to this rim colour.", + "kind": "namespace-method", + "page": "light-gel.html", + "section": "Light" + }, + "light-clear_gel": { + "name": "Light.clear_gel", + "sig": "Light.clear_gel()", + "tip": "Clear the gel — later lights are a flat single colour again.", + "kind": "namespace-method", + "page": "light-clear_gel.html", + "section": "Light" + }, + "light-normal": { + "name": "Light.normal", + "sig": "Light.normal(x, y, width, height, nx, ny)", + "tip": "Stamp a surface normal over a rectangle so lights shade it by facing (N·L).", + "kind": "namespace-method", + "page": "light-normal.html", + "section": "Light" + }, + "light-clear_normals": { + "name": "Light.clear_normals", + "sig": "Light.clear_normals()", + "tip": "Forget every stamped normal — call once per frame before re-stamping.", + "kind": "namespace-method", + "page": "light-clear_normals.html", + "section": "Light" + }, + "light-height": { + "name": "Light.height", + "sig": "Light.height(height)", + "tip": "Set the virtual height of lights above the surface, for normal-map shading.", + "kind": "namespace-method", + "page": "light-height.html", + "section": "Light" + }, + "light-time_of_day": { + "name": "Light.time_of_day", + "sig": "Light.time_of_day(t)", + "tip": "Set the ambient tint from a 0..1 time-of-day — a day/night cycle in one value.", + "kind": "namespace-method", + "page": "light-time_of_day.html", + "section": "Light" + }, + "sprite-sheet": { + "name": "Sprite.sheet", + "sig": "Sprite.sheet(path, cellw, cellh) -> int", + "tip": "Load a spritesheet and remember its cell grid; returns a sheet handle.", + "kind": "namespace-method", + "page": "sprite-sheet.html", + "section": "Sprite" + }, + "sprite-cell": { + "name": "Sprite.cell", + "sig": "Sprite.cell(sheet, col, row) -> int", + "tip": "One cell of a sheet, by grid coords; returns a sprite id.", + "kind": "namespace-method", + "page": "sprite-cell.html", + "section": "Sprite" + }, + "sprite-cell_span": { + "name": "Sprite.cell_span", + "sig": "Sprite.cell_span(sheet, col, row, cols, rows) -> int", + "tip": "A sprite spanning cols x rows cells (tall/wide art).", + "kind": "namespace-method", + "page": "sprite-cell_span.html", + "section": "Sprite" + }, + "sprite-define": { + "name": "Sprite.define", + "sig": "Sprite.define(name, sheet, col, row) -> int", + "tip": "Name a cell for later lookup; returns its sprite id.", + "kind": "namespace-method", + "page": "sprite-define.html", + "section": "Sprite" + }, + "sprite-named": { + "name": "Sprite.named", + "sig": "Sprite.named(name) -> int", + "tip": "Look up a named sprite's id, or -1.", + "kind": "namespace-method", + "page": "sprite-named.html", + "section": "Sprite" + }, + "sprite-draw": { + "name": "Sprite.draw", + "sig": "Sprite.draw(id, x, y)", + "tip": "Blit an atlas sprite at (x, y) through the camera/zoom/clip.", + "kind": "namespace-method", + "page": "sprite-draw.html", + "section": "Sprite" + }, + "sprite-draw_scaled": { + "name": "Sprite.draw_scaled", + "sig": "Sprite.draw_scaled(id, x, y, scale)", + "tip": "Blit an atlas sprite scaled by an integer factor.", + "kind": "namespace-method", + "page": "sprite-draw_scaled.html", + "section": "Sprite" + }, + "sprite-width": { + "name": "Sprite.width", + "sig": "Sprite.width(id) -> int", + "tip": "The atlas sprite's width in pixels.", + "kind": "namespace-method", + "page": "sprite-width.html", + "section": "Sprite" + }, + "sprite-height": { + "name": "Sprite.height", + "sig": "Sprite.height(id) -> int", + "tip": "The atlas sprite's height in pixels.", + "kind": "namespace-method", + "page": "sprite-height.html", + "section": "Sprite" + }, + "sprite-strip": { + "name": "Sprite.strip", + "sig": "Sprite.strip(sheet, col, row, count, rows) -> int", + "tip": "A run of animation frames as consecutive sprite ids.", + "kind": "namespace-method", + "page": "sprite-strip.html", + "section": "Sprite" + }, + "sprite-draw_meter": { + "name": "Sprite.draw_meter", + "sig": "Sprite.draw_meter(x:, y:, value:, max:, per_icon:, spacing:, full:, half:, empty:)", + "tip": "A value as a row of full / half / empty icons.", + "kind": "namespace-method", + "page": "sprite-draw_meter.html", + "section": "Sprite" + }, + "assets-image": { + "name": "Assets.image", + "sig": "Assets.image(path) -> int", + "tip": "Load a whole image file as one sprite; returns its id.", + "kind": "namespace-method", + "page": "assets-image.html", + "section": "Assets" + }, + "assets-load": { + "name": "Assets.load", + "sig": "Assets.load(path) -> int", + "tip": "Alias of Assets.image.", + "kind": "namespace-method", + "page": "assets-load.html", + "section": "Assets" + }, + "assets-get": { + "name": "Assets.get", + "sig": "Assets.get(name) -> int", + "tip": "Look up a named sprite's id (alias of Sprite.named).", + "kind": "namespace-method", + "page": "assets-get.html", + "section": "Assets" + }, + "assets-enqueue": { + "name": "Assets.enqueue", + "sig": "Assets.enqueue(name, path)", + "tip": "Queue a named image file to preload later.", + "kind": "namespace-method", + "page": "assets-enqueue.html", + "section": "Assets" + }, + "assets-pump": { + "name": "Assets.pump", + "sig": "Assets.pump(max) -> int", + "tip": "Load up to max queued assets this frame; returns how many it loaded.", + "kind": "namespace-method", + "page": "assets-pump.html", + "section": "Assets" + }, + "assets-total": { + "name": "Assets.total", + "sig": "Assets.total() -> int", + "tip": "How many assets are enqueued.", + "kind": "namespace-method", + "page": "assets-total.html", + "section": "Assets" + }, + "assets-loaded": { + "name": "Assets.loaded", + "sig": "Assets.loaded() -> int", + "tip": "How many enqueued assets have loaded so far.", + "kind": "namespace-method", + "page": "assets-loaded.html", + "section": "Assets" + }, + "assets-ready": { + "name": "Assets.ready", + "sig": "Assets.ready() -> int", + "tip": "1 once every enqueued asset has loaded.", + "kind": "namespace-method", + "page": "assets-ready.html", + "section": "Assets" + }, + "assets-font": { + "name": "Assets.font", + "sig": "Assets.font(name: s) -> int", + "tip": "A font loaded through the preload queue, by name.", + "kind": "namespace-method", + "page": "assets-font.html", + "section": "Assets" + }, + "assets-progress": { + "name": "Assets.progress", + "sig": "Assets.progress() -> int", + "tip": "Loading progress as a 0..100 percent.", + "kind": "namespace-method", + "page": "assets-progress.html", + "section": "Assets" + }, + "assets-enqueue_dir": { + "name": "Assets.enqueue_dir", + "sig": "Assets.enqueue_dir(path: dir)", + "tip": "Enqueue every file of a directory, named by its file name.", + "kind": "namespace-method", + "page": "assets-enqueue_dir.html", + "section": "Assets" + }, + "kw-test": { + "name": "test", + "sig": "test \"name\" { … }", + "tip": "A named test block, run automatically with pass/fail reporting.", + "kind": "keyword", + "page": "kw-test.html", + "section": "Testing" + }, + "pool-live": { + "name": "Pool.live", + "sig": "Pool.live() -> int", + "tip": "How many entities are currently alive.", + "kind": "namespace-method", + "page": "pool-live.html", + "section": "Pool" + }, + "pool-free": { + "name": "Pool.free", + "sig": "Pool.free() -> int", + "tip": "How many freed slots are waiting to be reused.", + "kind": "namespace-method", + "page": "pool-free.html", + "section": "Pool" + }, + "pool-reserved": { + "name": "Pool.reserved", + "sig": "Pool.reserved() -> int", + "tip": "High-water: how many slots have ever been allocated.", + "kind": "namespace-method", + "page": "pool-reserved.html", + "section": "Pool" + }, + "pool-capacity": { + "name": "Pool.capacity", + "sig": "Pool.capacity() -> int", + "tip": "The maximum number of entities.", + "kind": "namespace-method", + "page": "pool-capacity.html", + "section": "Pool" + }, + "value-null": { + "name": "Value.null", + "sig": "Value.null() -> value", + "tip": "An empty null node.", + "kind": "namespace-method", + "page": "value-null.html", + "section": "Value" + }, + "value-int": { + "name": "Value.int", + "sig": "Value.int(n: int) -> value", + "tip": "Wrap an int in a value node.", + "kind": "namespace-method", + "page": "value-int.html", + "section": "Value" + }, + "value-fixed": { + "name": "Value.fixed", + "sig": "Value.fixed(f: fixed) -> value", + "tip": "Wrap a fixed in a value node.", + "kind": "namespace-method", + "page": "value-fixed.html", + "section": "Value" + }, + "value-bool": { + "name": "Value.bool", + "sig": "Value.bool(b: bool) -> value", + "tip": "Wrap a bool in a value node.", + "kind": "namespace-method", + "page": "value-bool.html", + "section": "Value" + }, + "value-str": { + "name": "Value.str", + "sig": "Value.str(s: string) -> value", + "tip": "Wrap a string in a value node.", + "kind": "namespace-method", + "page": "value-str.html", + "section": "Value" + }, + "value-list": { + "name": "Value.list", + "sig": "Value.list() -> value", + "tip": "An empty list node.", + "kind": "namespace-method", + "page": "value-list.html", + "section": "Value" + }, + "value-object": { + "name": "Value.object", + "sig": "Value.object() -> value", + "tip": "An empty object node.", + "kind": "namespace-method", + "page": "value-object.html", + "section": "Value" + }, + "value-add": { + "name": "Value.add", + "sig": "Value.add(list, item) -> value", + "tip": "Append an item to a list; returns the list.", + "kind": "namespace-method", + "page": "value-add.html", + "section": "Value" + }, + "value-put": { + "name": "Value.put", + "sig": "Value.put(obj, key: string, item) -> value", + "tip": "Set a key on an object; returns the object.", + "kind": "namespace-method", + "page": "value-put.html", + "section": "Value" + }, + "value-get": { + "name": "Value.get", + "sig": "Value.get(obj, key: string) -> value", + "tip": "Read a member of an object by key.", + "kind": "namespace-method", + "page": "value-get.html", + "section": "Value" + }, + "value-has": { + "name": "Value.has", + "sig": "Value.has(obj, key: string) -> bool", + "tip": "Whether an object has a member.", + "kind": "namespace-method", + "page": "value-has.html", + "section": "Value" + }, + "value-at": { + "name": "Value.at", + "sig": "Value.at(list, index: int) -> value", + "tip": "Read a list item by index.", + "kind": "namespace-method", + "page": "value-at.html", + "section": "Value" + }, + "value-key_at": { + "name": "Value.key_at", + "sig": "Value.key_at(obj, index: int) -> string", + "tip": "The key of an object member by position.", + "kind": "namespace-method", + "page": "value-key_at.html", + "section": "Value" + }, + "value-count": { + "name": "Value.count", + "sig": "Value.count(value) -> int", + "tip": "Number of items/members in a list or object.", + "kind": "namespace-method", + "page": "value-count.html", + "section": "Value" + }, + "value-kind": { + "name": "Value.kind", + "sig": "Value.kind(value) -> int", + "tip": "The node's kind tag.", + "kind": "namespace-method", + "page": "value-kind.html", + "section": "Value" + }, + "value-as_int": { + "name": "Value.as_int", + "sig": "Value.as_int(value) -> int", + "tip": "Read a scalar node as an int.", + "kind": "namespace-method", + "page": "value-as_int.html", + "section": "Value" + }, + "value-as_str": { + "name": "Value.as_str", + "sig": "Value.as_str(value) -> string", + "tip": "Read a str node's text.", + "kind": "namespace-method", + "page": "value-as_str.html", + "section": "Value" + }, + "json-encode": { + "name": "Json.encode", + "sig": "Json.encode(value) -> string", + "tip": "Serialize a value tree to compact JSON text.", + "kind": "namespace-method", + "page": "json-encode.html", + "section": "Json" + }, + "json-parse": { + "name": "Json.parse", + "sig": "Json.parse(text: string) -> value", + "tip": "Parse JSON text into a value tree.", + "kind": "namespace-method", + "page": "json-parse.html", + "section": "Json" + }, + "job-defer": { + "name": "Job.defer", + "sig": "Job.defer() -> Job", + "tip": "A future you resolve yourself later.", + "kind": "namespace-method", + "page": "job-defer.html", + "section": "Job" + }, + "job-run": { + "name": "Job.run", + "sig": "Job.run(kind, arg) -> Job", + "tip": "Start a background compute job.", + "kind": "namespace-method", + "page": "job-run.html", + "section": "Job" + }, + "job-fulfill": { + "name": "Job.fulfill", + "sig": "Job.fulfill(handle, value) -> void", + "tip": "Resolve a pending job with a value.", + "kind": "namespace-method", + "page": "job-fulfill.html", + "section": "Job" + }, + "job-fail": { + "name": "Job.fail", + "sig": "Job.fail(handle, error) -> void", + "tip": "Resolve a pending job as failed.", + "kind": "namespace-method", + "page": "job-fail.html", + "section": "Job" + }, + "job-cancel": { + "name": "Job.cancel", + "sig": "Job.cancel(handle) -> void", + "tip": "Cancel a job before it finishes.", + "kind": "namespace-method", + "page": "job-cancel.html", + "section": "Job" + }, + "job-pump": { + "name": "Job.pump", + "sig": "Job.pump(budget) -> int", + "tip": "Advance background jobs; collect results.", + "kind": "namespace-method", + "page": "job-pump.html", + "section": "Job" + }, + "job-done": { + "name": "Job.done", + "sig": "Job.done(handle) -> bool", + "tip": "Has the job resolved (any outcome)?", + "kind": "namespace-method", + "page": "job-done.html", + "section": "Job" + }, + "job-ok": { + "name": "Job.ok", + "sig": "Job.ok(handle) -> bool", + "tip": "Did the job succeed?", + "kind": "namespace-method", + "page": "job-ok.html", + "section": "Job" + }, + "job-failed": { + "name": "Job.failed", + "sig": "Job.failed(handle) -> bool", + "tip": "Did the job fail?", + "kind": "namespace-method", + "page": "job-failed.html", + "section": "Job" + }, + "job-cancelled": { + "name": "Job.cancelled", + "sig": "Job.cancelled(handle) -> bool", + "tip": "Was the job cancelled?", + "kind": "namespace-method", + "page": "job-cancelled.html", + "section": "Job" + }, + "job-result": { + "name": "Job.result", + "sig": "Job.result(handle) -> int", + "tip": "The success value of a done job.", + "kind": "namespace-method", + "page": "job-result.html", + "section": "Job" + }, + "job-error": { + "name": "Job.error", + "sig": "Job.error(handle) -> int", + "tip": "The error code of a failed job.", + "kind": "namespace-method", + "page": "job-error.html", + "section": "Job" + }, + "job-pending": { + "name": "Job.pending", + "sig": "Job.pending() -> int", + "tip": "How many jobs are still unresolved.", + "kind": "namespace-method", + "page": "job-pending.html", + "section": "Job" + }, + "job-free": { + "name": "Job.free", + "sig": "Job.free(handle) -> void", + "tip": "Release a job slot back to the pool.", + "kind": "namespace-method", + "page": "job-free.html", + "section": "Job" + }, + "job-parallel_for": { + "name": "Job.parallel_for", + "sig": "Job.parallel_for(count, work, ctx) -> void", + "tip": "Run work(i, ctx) for every i in [0, count) across all cores; returns when every call is done.", + "kind": "namespace-method", + "page": "job-parallel_for.html", + "section": "Job" + }, + "job-is_worker": { + "name": "Job.is_worker", + "sig": "Job.is_worker() -> bool", + "tip": "True on a Job.parallel_for pool thread, false on the main thread.", + "kind": "namespace-method", + "page": "job-is_worker.html", + "section": "Job" + }, + "promise-all": { + "name": "Promise.all", + "sig": "Promise.all(handles) -> Job", + "tip": "Succeeds when every member succeeds.", + "kind": "namespace-method", + "page": "promise-all.html", + "section": "Promise" + }, + "promise-race": { + "name": "Promise.race", + "sig": "Promise.race(handles) -> Job", + "tip": "Succeeds when the first member does.", + "kind": "namespace-method", + "page": "promise-race.html", + "section": "Promise" + }, + "promise-count_done": { + "name": "Promise.count_done", + "sig": "Promise.count_done(handles) -> int", + "tip": "How many members have resolved.", + "kind": "namespace-method", + "page": "promise-count_done.html", + "section": "Promise" + }, + "promise-all_done": { + "name": "Promise.all_done", + "sig": "Promise.all_done(handles) -> bool", + "tip": "Have all members resolved?", + "kind": "namespace-method", + "page": "promise-all_done.html", + "section": "Promise" + }, + "sync-mutex": { + "name": "Sync.mutex", + "sig": "Sync.mutex() -> int", + "tip": "Create a cooperative lock.", + "kind": "namespace-method", + "page": "sync-mutex.html", + "section": "Sync" + }, + "sync-lock": { + "name": "Sync.lock", + "sig": "Sync.lock(mutex) -> void", + "tip": "Take the lock.", + "kind": "namespace-method", + "page": "sync-lock.html", + "section": "Sync" + }, + "sync-unlock": { + "name": "Sync.unlock", + "sig": "Sync.unlock(mutex) -> void", + "tip": "Release the lock.", + "kind": "namespace-method", + "page": "sync-unlock.html", + "section": "Sync" + }, + "sync-try_lock": { + "name": "Sync.try_lock", + "sig": "Sync.try_lock(mutex) -> bool", + "tip": "Take the lock only if it is free.", + "kind": "namespace-method", + "page": "sync-try_lock.html", + "section": "Sync" + }, + "sync-atomic": { + "name": "Sync.atomic", + "sig": "Sync.atomic() -> int", + "tip": "Create an atomic counter (starts at 0).", + "kind": "namespace-method", + "page": "sync-atomic.html", + "section": "Sync" + }, + "sync-get": { + "name": "Sync.get", + "sig": "Sync.get(atomic) -> int", + "tip": "Read the counter.", + "kind": "namespace-method", + "page": "sync-get.html", + "section": "Sync" + }, + "sync-set": { + "name": "Sync.set", + "sig": "Sync.set(atomic, value) -> void", + "tip": "Store a value in the counter.", + "kind": "namespace-method", + "page": "sync-set.html", + "section": "Sync" + }, + "sync-add": { + "name": "Sync.add", + "sig": "Sync.add(atomic, delta) -> int", + "tip": "Add to the counter; return the new value.", + "kind": "namespace-method", + "page": "sync-add.html", + "section": "Sync" + }, + "sync-cas": { + "name": "Sync.cas", + "sig": "Sync.cas(atomic, expect, next) -> bool", + "tip": "Compare-and-set the counter.", + "kind": "namespace-method", + "page": "sync-cas.html", + "section": "Sync" + }, + "sync-channel": { + "name": "Sync.channel", + "sig": "Sync.channel() -> int", + "tip": "Create a bounded int FIFO channel.", + "kind": "namespace-method", + "page": "sync-channel.html", + "section": "Sync" + }, + "sync-send": { + "name": "Sync.send", + "sig": "Sync.send(channel, value) -> bool", + "tip": "Enqueue a value (false if full).", + "kind": "namespace-method", + "page": "sync-send.html", + "section": "Sync" + }, + "sync-recv": { + "name": "Sync.recv", + "sig": "Sync.recv(channel) -> int", + "tip": "Dequeue the oldest value.", + "kind": "namespace-method", + "page": "sync-recv.html", + "section": "Sync" + }, + "sync-can_recv": { + "name": "Sync.can_recv", + "sig": "Sync.can_recv(channel) -> bool", + "tip": "Is there a value waiting?", + "kind": "namespace-method", + "page": "sync-can_recv.html", + "section": "Sync" + }, + "sync-len": { + "name": "Sync.len", + "sig": "Sync.len(channel) -> int", + "tip": "How many values are queued.", + "kind": "namespace-method", + "page": "sync-len.html", + "section": "Sync" + }, + "sync-cpu_count": { + "name": "Sync.cpu_count", + "sig": "Sync.cpu_count() -> int", + "tip": "The machine's logical cores (1 to 64); Job.parallel_for uses one thread per core.", + "kind": "namespace-method", + "page": "sync-cpu_count.html", + "section": "Sync" + }, + "xml-parse": { + "name": "Xml.parse", + "sig": "Xml.parse(text: string) -> xml", + "tip": "Parse XML text into an element tree.", + "kind": "namespace-method", + "page": "xml-parse.html", + "section": "Xml" + }, + "xml-tag": { + "name": "Xml.tag", + "sig": "Xml.tag(node) -> string", + "tip": "The element's tag name.", + "kind": "namespace-method", + "page": "xml-tag.html", + "section": "Xml" + }, + "xml-text": { + "name": "Xml.text", + "sig": "Xml.text(node) -> string", + "tip": "The element's character data.", + "kind": "namespace-method", + "page": "xml-text.html", + "section": "Xml" + }, + "xml-attr": { + "name": "Xml.attr", + "sig": "Xml.attr(node, key: string) -> string", + "tip": "An attribute's value by name.", + "kind": "namespace-method", + "page": "xml-attr.html", + "section": "Xml" + }, + "xml-attr_int": { + "name": "Xml.attr_int", + "sig": "Xml.attr_int(node, key: string, dflt: int) -> int", + "tip": "An attribute parsed as an integer.", + "kind": "namespace-method", + "page": "xml-attr_int.html", + "section": "Xml" + }, + "xml-has": { + "name": "Xml.has", + "sig": "Xml.has(node, key: string) -> int", + "tip": "Whether an attribute is present.", + "kind": "namespace-method", + "page": "xml-has.html", + "section": "Xml" + }, + "xml-attr_count": { + "name": "Xml.attr_count", + "sig": "Xml.attr_count(node) -> int", + "tip": "How many attributes the element has.", + "kind": "namespace-method", + "page": "xml-attr_count.html", + "section": "Xml" + }, + "xml-child_count": { + "name": "Xml.child_count", + "sig": "Xml.child_count(node) -> int", + "tip": "How many child elements.", + "kind": "namespace-method", + "page": "xml-child_count.html", + "section": "Xml" + }, + "xml-child": { + "name": "Xml.child", + "sig": "Xml.child(node, index: int) -> xml", + "tip": "A child element by index.", + "kind": "namespace-method", + "page": "xml-child.html", + "section": "Xml" + }, + "xml-find": { + "name": "Xml.find", + "sig": "Xml.find(node, tag: string) -> xml", + "tip": "The first child with a given tag.", + "kind": "namespace-method", + "page": "xml-find.html", + "section": "Xml" + }, + "xml-count": { + "name": "Xml.count", + "sig": "Xml.count(node, tag: string) -> int", + "tip": "How many children have a given tag.", + "kind": "namespace-method", + "page": "xml-count.html", + "section": "Xml" + }, + "base64-decode": { + "name": "Base64.decode", + "sig": "Base64.decode(src: string) -> string", + "tip": "Decode standard base64 text to bytes.", + "kind": "namespace-method", + "page": "base64-decode.html", + "section": "Base64" + }, + "base64-encode": { + "name": "Base64.encode", + "sig": "Base64.encode(src: string) -> string", + "tip": "Encode bytes as standard base64 text.", + "kind": "namespace-method", + "page": "base64-encode.html", + "section": "Base64" + }, + "tiled-read": { + "name": "Tiled.read", + "sig": "Tiled.read(path: string) -> value", + "tip": "Read a map file into the intermediate value tree.", + "kind": "namespace-method", + "page": "tiled-read.html", + "section": "Tiled" + }, + "tiled-read_tsx": { + "name": "Tiled.read_tsx", + "sig": "Tiled.read_tsx(path: string) -> value", + "tip": "Read a tileset file into a value object.", + "kind": "namespace-method", + "page": "tiled-read_tsx.html", + "section": "Tiled" + }, + "tiled-load": { + "name": "Tiled.load", + "sig": "Tiled.load(path: string) -> map", + "tip": "Load a map file into the runtime map model.", + "kind": "namespace-method", + "page": "tiled-load.html", + "section": "Tiled" + }, + "tiled-gid": { + "name": "Tiled.gid", + "sig": "Tiled.gid(map, layer: int, x: int, y: int) -> int", + "tip": "The raw GID at a cell in a tile layer.", + "kind": "namespace-method", + "page": "tiled-gid.html", + "section": "Tiled" + }, + "tiled-resolve": { + "name": "Tiled.resolve", + "sig": "Tiled.resolve(map, gid: int) -> gidinfo", + "tip": "Decode a GID into tileset, local id and flip flags.", + "kind": "namespace-method", + "page": "tiled-resolve.html", + "section": "Tiled" + }, + "tiled-width": { + "name": "Tiled.width", + "sig": "Tiled.width(map) -> int", + "tip": "The map width in tiles.", + "kind": "namespace-method", + "page": "tiled-width.html", + "section": "Tiled" + }, + "tiled-height": { + "name": "Tiled.height", + "sig": "Tiled.height(map) -> int", + "tip": "The map height in tiles.", + "kind": "namespace-method", + "page": "tiled-height.html", + "section": "Tiled" + }, + "tiled-layer_count": { + "name": "Tiled.layer_count", + "sig": "Tiled.layer_count(map) -> int", + "tip": "How many layers the map has.", + "kind": "namespace-method", + "page": "tiled-layer_count.html", + "section": "Tiled" + }, + "tiled-layer_name": { + "name": "Tiled.layer_name", + "sig": "Tiled.layer_name(map, index: int) -> string", + "tip": "A layer's name by index.", + "kind": "namespace-method", + "page": "tiled-layer_name.html", + "section": "Tiled" + }, + "tiled-draw": { + "name": "Tiled.draw", + "sig": "Tiled.draw(map, camx: int, camy: int) -> void", + "tip": "Draw every visible tile layer.", + "kind": "namespace-method", + "page": "tiled-draw.html", + "section": "Tiled" + }, + "tiled-project": { + "name": "Tiled.project", + "sig": "Tiled.project(map, layer: int) -> void", + "tip": "Project a collision layer to the byte tilemap.", + "kind": "namespace-method", + "page": "tiled-project.html", + "section": "Tiled" + }, + "tiled-tree": { + "name": "Tiled.tree", + "sig": "Tiled.tree(map) -> value", + "tip": "The underlying intermediate value tree.", + "kind": "namespace-method", + "page": "tiled-tree.html", + "section": "Tiled" + }, + "tiled-tile_prop": { + "name": "Tiled.tile_prop", + "sig": "Tiled.tile_prop(map, gid: int, name: string) -> int", + "tip": "Whether a GID's tile carries a bool property.", + "kind": "namespace-method", + "page": "tiled-tile_prop.html", + "section": "Tiled" + }, + "tiled-collide": { + "name": "Tiled.collide", + "sig": "Tiled.collide(map, layer: int) -> void", + "tip": "Drive collision from per-tile metadata.", + "kind": "namespace-method", + "page": "tiled-collide.html", + "section": "Tiled" + }, + "tiled-collision_kind": { + "name": "Tiled.collision_kind", + "sig": "Tiled.collision_kind(map, gid: int) -> int", + "tip": "Classify a GID's collision from its tile metadata.", + "kind": "namespace-method", + "page": "tiled-collision_kind.html", + "section": "Tiled" + }, + "tiled-tile_shapes": { + "name": "Tiled.tile_shapes", + "sig": "Tiled.tile_shapes(map, gid: int) -> int", + "tip": "Whether a GID's tile has a collision shape.", + "kind": "namespace-method", + "page": "tiled-tile_shapes.html", + "section": "Tiled" + }, + "tiled-draw_anim": { + "name": "Tiled.draw_anim", + "sig": "Tiled.draw_anim(map, camx: int, camy: int, frame: int) -> void", + "tip": "Draw with animated tiles advanced to a frame.", + "kind": "namespace-method", + "page": "tiled-draw_anim.html", + "section": "Tiled" + }, + "tiled-frame_gid": { + "name": "Tiled.frame_gid", + "sig": "Tiled.frame_gid(map, gid: int, frame: int) -> int", + "tip": "The current GID of an animated tile at a frame.", + "kind": "namespace-method", + "page": "tiled-frame_gid.html", + "section": "Tiled" + }, + "tiled-animated": { + "name": "Tiled.animated", + "sig": "Tiled.animated(map, gid: int) -> int", + "tip": "Whether a GID's tile is animated.", + "kind": "namespace-method", + "page": "tiled-animated.html", + "section": "Tiled" + }, + "tiled-object_count": { + "name": "Tiled.object_count", + "sig": "Tiled.object_count(map, layer: int) -> int", + "tip": "How many objects an object layer holds.", + "kind": "namespace-method", + "page": "tiled-object_count.html", + "section": "Tiled" + }, + "tiled-object": { + "name": "Tiled.object", + "sig": "Tiled.object(map, layer: int, index: int) -> value", + "tip": "An object on an object layer by index.", + "kind": "namespace-method", + "page": "tiled-object.html", + "section": "Tiled" + }, + "tiled-object_shape": { + "name": "Tiled.object_shape", + "sig": "Tiled.object_shape(object) -> string", + "tip": "Classify an object's shape.", + "kind": "namespace-method", + "page": "tiled-object_shape.html", + "section": "Tiled" + }, + "tiled-prop": { + "name": "Tiled.prop", + "sig": "Tiled.prop(container, name: string) -> string", + "tip": "A custom property's value, with class-default fallback.", + "kind": "namespace-method", + "page": "tiled-prop.html", + "section": "Tiled" + }, + "tiled-prop_int": { + "name": "Tiled.prop_int", + "sig": "Tiled.prop_int(container, name: string) -> int", + "tip": "A custom property parsed as an integer.", + "kind": "namespace-method", + "page": "tiled-prop_int.html", + "section": "Tiled" + }, + "tiled-prop_type": { + "name": "Tiled.prop_type", + "sig": "Tiled.prop_type(container, name: string) -> string", + "tip": "A custom property's declared type.", + "kind": "namespace-method", + "page": "tiled-prop_type.html", + "section": "Tiled" + }, + "tiled-load_types": { + "name": "Tiled.load_types", + "sig": "Tiled.load_types(path: string) -> int", + "tip": "Load a project custom-type table.", + "kind": "namespace-method", + "page": "tiled-load_types.html", + "section": "Tiled" + }, + "tiled-template": { + "name": "Tiled.template", + "sig": "Tiled.template(path: string) -> value", + "tip": "Read a template file into an object value.", + "kind": "namespace-method", + "page": "tiled-template.html", + "section": "Tiled" + }, + "tiled-spawn": { + "name": "Tiled.spawn", + "sig": "Tiled.spawn(map, object, model: string) -> int", + "tip": "Spawn a Ludic entity from an object.", + "kind": "namespace-method", + "page": "tiled-spawn.html", + "section": "Tiled" + }, + "tiled-spawn_layer": { + "name": "Tiled.spawn_layer", + "sig": "Tiled.spawn_layer(map, layer: int, model: string) -> int", + "tip": "Spawn every object on a layer.", + "kind": "namespace-method", + "page": "tiled-spawn_layer.html", + "section": "Tiled" + }, + "tiled-cell_x": { + "name": "Tiled.cell_x", + "sig": "Tiled.cell_x(map, x: int, y: int) -> int", + "tip": "The screen x of a tile cell for the map orientation.", + "kind": "namespace-method", + "page": "tiled-cell_x.html", + "section": "Tiled" + }, + "tiled-cell_y": { + "name": "Tiled.cell_y", + "sig": "Tiled.cell_y(map, x: int, y: int) -> int", + "tip": "The screen y of a tile cell for the map orientation.", + "kind": "namespace-method", + "page": "tiled-cell_y.html", + "section": "Tiled" + }, + "tiled-layer_kind": { + "name": "Tiled.layer_kind", + "sig": "Tiled.layer_kind(map, layer: int) -> int", + "tip": "A layer's kind.", + "kind": "namespace-method", + "page": "tiled-layer_kind.html", + "section": "Tiled" + }, + "tiled-layer_opacity": { + "name": "Tiled.layer_opacity", + "sig": "Tiled.layer_opacity(map, layer: int) -> string", + "tip": "A layer's opacity.", + "kind": "namespace-method", + "page": "tiled-layer_opacity.html", + "section": "Tiled" + }, + "tiled-layer_tint": { + "name": "Tiled.layer_tint", + "sig": "Tiled.layer_tint(map, layer: int) -> string", + "tip": "A layer's tint colour.", + "kind": "namespace-method", + "page": "tiled-layer_tint.html", + "section": "Tiled" + }, + "tiled-layer_offsetx": { + "name": "Tiled.layer_offsetx", + "sig": "Tiled.layer_offsetx(map, layer: int) -> int", + "tip": "A layer's horizontal offset.", + "kind": "namespace-method", + "page": "tiled-layer_offsetx.html", + "section": "Tiled" + }, + "tiled-layer_offsety": { + "name": "Tiled.layer_offsety", + "sig": "Tiled.layer_offsety(map, layer: int) -> int", + "tip": "A layer's vertical offset.", + "kind": "namespace-method", + "page": "tiled-layer_offsety.html", + "section": "Tiled" + }, + "tiled-world": { + "name": "Tiled.world", + "sig": "Tiled.world(path: string) -> value", + "tip": "Read a .world file.", + "kind": "namespace-method", + "page": "tiled-world.html", + "section": "Tiled" + }, + "tiled-world_count": { + "name": "Tiled.world_count", + "sig": "Tiled.world_count(world) -> int", + "tip": "How many maps a world stitches.", + "kind": "namespace-method", + "page": "tiled-world_count.html", + "section": "Tiled" + }, + "tiled-world_map": { + "name": "Tiled.world_map", + "sig": "Tiled.world_map(world, index: int) -> value", + "tip": "A world member map by index.", + "kind": "namespace-method", + "page": "tiled-world_map.html", + "section": "Tiled" + }, + "ui-build": { + "name": "Ui.build", + "sig": "Ui.build()", + "tip": "Construct every declared ui block (loads skins, measures fonts).", + "kind": "namespace-method", + "page": "ui-build.html", + "section": "Ui" + }, + "ui-open": { + "name": "Ui.open", + "sig": "Ui.open(id: UI_Name)", + "tip": "Make one menu active and focus its first button.", + "kind": "namespace-method", + "page": "ui-open.html", + "section": "Ui" + }, + "ui-close": { + "name": "Ui.close", + "sig": "Ui.close()", + "tip": "Deactivate the menu: no menu is open.", + "kind": "namespace-method", + "page": "ui-close.html", + "section": "Ui" + }, + "ui-tick": { + "name": "Ui.tick", + "sig": "Ui.tick(key: k)", + "tip": "Advance navigation by one key (the frame loop does this for you).", + "kind": "namespace-method", + "page": "ui-tick.html", + "section": "Ui" + }, + "ui-clicked": { + "name": "Ui.clicked", + "sig": "Ui.clicked(id: UI_Name) -> bool", + "tip": "Was this control activated this frame? (polled form of UiClicked)", + "kind": "namespace-method", + "page": "ui-clicked.html", + "section": "Ui" + }, + "ui-set_text": { + "name": "Ui.set_text", + "sig": "Ui.set_text(id: UI_Name, text: s)", + "tip": "Replace a label's or button's text.", + "kind": "namespace-method", + "page": "ui-set_text.html", + "section": "Ui" + }, + "ui-render": { + "name": "Ui.render", + "sig": "Ui.render()", + "tip": "Draw the active menu.", + "kind": "namespace-method", + "page": "ui-render.html", + "section": "Ui" + }, + "file-open": { + "name": "File.open", + "sig": "File.open(path: p, mode: m) -> pointer", + "tip": "Open a file (fopen); null when it cannot be opened.", + "kind": "namespace-method", + "page": "file-open.html", + "section": "File" + }, + "file-read": { + "name": "File.read", + "sig": "File.read(file: f, buffer: b, count: n) -> int", + "tip": "Read up to n bytes into a buffer; returns the bytes read.", + "kind": "namespace-method", + "page": "file-read.html", + "section": "File" + }, + "file-write": { + "name": "File.write", + "sig": "File.write(file: f, buffer: b, count: n) -> int", + "tip": "Write n bytes from a buffer; returns the bytes written.", + "kind": "namespace-method", + "page": "file-write.html", + "section": "File" + }, + "file-seek": { + "name": "File.seek", + "sig": "File.seek(file: f, offset: o, whence: w)", + "tip": "Move the file cursor (0 start, 1 current, 2 end).", + "kind": "namespace-method", + "page": "file-seek.html", + "section": "File" + }, + "file-tell": { + "name": "File.tell", + "sig": "File.tell(file: f) -> int", + "tip": "The cursor position in bytes.", + "kind": "namespace-method", + "page": "file-tell.html", + "section": "File" + }, + "file-close": { + "name": "File.close", + "sig": "File.close(file: f)", + "tip": "Close a handle from File.open.", + "kind": "namespace-method", + "page": "file-close.html", + "section": "File" + }, + "font-load": { + "name": "Font.load", + "sig": "Font.load(path: p) -> int", + "tip": "Load a TrueType font; returns a font handle.", + "kind": "namespace-method", + "page": "font-load.html", + "section": "Font" + }, + "fx-sparks": { + "name": "Fx.sparks", + "sig": "Fx.sparks(x: px, y: px, color: c, count: n)", + "tip": "A burst of sparks flying out of a point.", + "kind": "namespace-method", + "page": "fx-sparks.html", + "section": "Fx" + }, + "fx-number": { + "name": "Fx.number", + "sig": "Fx.number(x: px, y: px, value: n, color: c)", + "tip": "A number that floats up from a point and fades.", + "kind": "namespace-method", + "page": "fx-number.html", + "section": "Fx" + }, + "fx-clear": { + "name": "Fx.clear", + "sig": "Fx.clear()", + "tip": "Drop every spark and number at once.", + "kind": "namespace-method", + "page": "fx-clear.html", + "section": "Fx" + }, + "prefab-spawn": { + "name": "Prefab.spawn", + "sig": "Prefab.spawn(name: s) -> entity", + "tip": "Spawn a prefab chosen by name at runtime; -1 if none has that name.", + "kind": "namespace-method", + "page": "prefab-spawn.html", + "section": "Prefab" + }, + "prefab-spawn_at": { + "name": "Prefab.spawn_at", + "sig": "Prefab.spawn_at(name: s, at: IVec2) -> entity", + "tip": "Spawn a prefab by name and place it.", + "kind": "namespace-method", + "page": "prefab-spawn_at.html", + "section": "Prefab" + }, + "app-splash_hide": { + "name": "App.splash_hide", + "sig": "App.splash_hide() -> void", + "tip": "Dismiss the boot splash.", + "kind": "namespace-method", + "page": "app-splash_hide.html", + "section": "App" + }, + "app-set_icon": { + "name": "App.set_icon", + "sig": "App.set_icon(path) -> void", + "tip": "Set the Dock icon from an image the game ships.", + "kind": "namespace-method", + "page": "app-set_icon.html", + "section": "App" + }, + "app-window_hide": { + "name": "App.window_hide", + "sig": "App.window_hide() -> void", + "tip": "Take the game's window off the screen.", + "kind": "namespace-method", + "page": "app-window_hide.html", + "section": "App" + }, + "app-window_show": { + "name": "App.window_show", + "sig": "App.window_show() -> void", + "tip": "Bring the game's window back.", + "kind": "namespace-method", + "page": "app-window_show.html", + "section": "App" + }, + "phase-start": { + "name": "Start", + "sig": "phase Start", + "tip": "Runs once at boot, before the game loop — the place to set up initial state.", + "kind": "phase", + "page": "phase-start.html", + "section": "Phases" + }, + "phase-input": { + "name": "Input", + "sig": "phase Input", + "tip": "The first per-frame phase — read the keyboard and record the player's intent.", + "kind": "phase", + "page": "phase-input.html", + "section": "Phases" + }, + "phase-fixedupdate": { + "name": "FixedUpdate", + "sig": "phase FixedUpdate", + "tip": "The deterministic simulation step — physics and gameplay meant to be reproducible.", + "kind": "phase", + "page": "phase-fixedupdate.html", + "section": "Phases" + }, + "phase-update": { + "name": "Update", + "sig": "phase Update", + "tip": "The ordinary per-frame game-logic step, run after Input and FixedUpdate.", + "kind": "phase", + "page": "phase-update.html", + "section": "Phases" + }, + "phase-lateupdate": { + "name": "LateUpdate", + "sig": "phase LateUpdate", + "tip": "Runs each frame after Update and before Render.", + "kind": "phase", + "page": "phase-lateupdate.html", + "section": "Phases" + }, + "phase-render": { + "name": "Render", + "sig": "phase Render", + "tip": "The last per-frame phase — draw the world, then call Screen.show() once.", + "kind": "phase", + "page": "phase-render.html", + "section": "Phases" + }, + "phase-overlay": { + "name": "Overlay", + "sig": "phase Overlay", + "tip": "The HUD pass — runs after Render and after every engine-owned drawing system.", + "kind": "phase", + "page": "phase-overlay.html", + "section": "Phases" + }, + "ns-angle": { + "name": "Angle", + "sig": "Angle.*", + "kind": "namespace", + "tip": "An auto-wrapping angle in radians, so you never juggle % TAU by hand.", + "page": "ns-angle.html", + "section": "Namespaces" + }, + "ns-anim": { + "name": "Anim", + "sig": "Anim.*", + "kind": "namespace", + "tip": "Spritesheet frame animation off the fixed frame clock.", + "page": "ns-anim.html", + "section": "Namespaces" + }, + "ns-app": { + "name": "App", + "sig": "App.*", + "kind": "namespace", + "tip": "The running application, as distinct from its window.", + "page": "ns-app.html", + "section": "Namespaces" + }, + "ns-assets": { + "name": "Assets", + "sig": "Assets.*", + "kind": "namespace", + "tip": "Loading and lookup for whole-file assets, paired with the Sprite.* atlas API.", + "page": "ns-assets.html", + "section": "Namespaces" + }, + "ns-audio": { + "name": "Audio", + "sig": "Audio.*", + "kind": "namespace", + "tip": "Sound effects and music.", + "page": "ns-audio.html", + "section": "Namespaces" + }, + "ns-base64": { + "name": "Base64", + "sig": "Base64.*", + "kind": "namespace", + "tip": "Standard base64 (RFC 4648) — Base64.decode reads base64 text into the bytes it stands for, and Base64.encode is the inverse.", + "page": "ns-base64.html", + "section": "Namespaces" + }, + "ns-bigint": { + "name": "BigInt", + "sig": "BigInt.*", + "kind": "namespace", + "tip": "Arbitrary-precision integers with no upper bound, for idle/incremental counters, exact huge currencies, and score arithmetic that a 32- or 64-bit int would…", + "page": "ns-bigint.html", + "section": "Namespaces" + }, + "ns-camera": { + "name": "Camera", + "sig": "Camera.*", + "kind": "namespace", + "tip": "A world-space camera — a draw offset threaded through the render path (the same offset Screen.camera sets).", + "page": "ns-camera.html", + "section": "Namespaces" + }, + "ns-clock": { + "name": "Clock", + "sig": "Clock.*", + "kind": "namespace", + "tip": "A game-controlled simulated clock: a single seconds counter the game owns.", + "page": "ns-clock.html", + "section": "Namespaces" + }, + "ns-collision": { + "name": "Collision", + "sig": "Collision.*", + "kind": "namespace", + "tip": "2D overlap tests on integer coordinates (pixels or tiles).", + "page": "ns-collision.html", + "section": "Namespaces" + }, + "ns-color": { + "name": "Color", + "sig": "Color.*", + "kind": "namespace", + "tip": "The named color palette.", + "page": "ns-color.html", + "section": "Namespaces" + }, + "ns-crypto": { + "name": "Crypto", + "sig": "Crypto.*", + "kind": "namespace", + "tip": "Secure, test-vector-backed hashing for the handful of security-sensitive things a game actually does: signing a save or leaderboard payload so casual tampering…", + "page": "ns-crypto.html", + "section": "Namespaces" + }, + "ns-date": { + "name": "Date", + "sig": "Date.*", + "kind": "namespace", + "tip": "Calendar days on the proleptic Gregorian calendar (UTC).", + "page": "ns-date.html", + "section": "Namespaces" + }, + "ns-datetime": { + "name": "DateTime", + "sig": "DateTime.*", + "kind": "namespace", + "tip": "Instants on the wall-clock timeline (UTC), stored as a plain int: the count of seconds since 1970-01-01, matching Time.now.", + "page": "ns-datetime.html", + "section": "Namespaces" + }, + "ns-decimal": { + "name": "Decimal", + "sig": "Decimal.*", + "kind": "namespace", + "tip": "Exact base-10 fixed-point numbers for game economies — prices, balances and taxes on values like 0.10 that binary floating point cannot represent, so they…", + "page": "ns-decimal.html", + "section": "Namespaces" + }, + "ns-dict": { + "name": "Dict", + "sig": "Dict.*", + "kind": "namespace", + "tip": "A hash map from string keys to int values — the everyday lookup a game needs, like resource counts or an id registry by name.", + "page": "ns-dict.html", + "section": "Namespaces" + }, + "ns-duration": { + "name": "Duration", + "sig": "Duration.*", + "kind": "namespace", + "tip": "Spans of real time, measured in whole seconds and carried in a plain int — so a duration adds, subtracts and compares with the ordinary operators…", + "page": "ns-duration.html", + "section": "Namespaces" + }, + "ns-ease": { + "name": "Ease", + "sig": "Ease.*", + "kind": "namespace", + "tip": "Tween curves — the \"juice\" layer.", + "page": "ns-ease.html", + "section": "Namespaces" + }, + "ns-file": { + "name": "File", + "sig": "File.*", + "kind": "namespace", + "tip": "Raw file access over the C stdio calls, for programs that read and write their own formats.", + "page": "ns-file.html", + "section": "Namespaces" + }, + "ns-font": { + "name": "Font", + "sig": "Font.*", + "kind": "namespace", + "tip": "TrueType fonts for the retained UI and for Screen.draw_text-style drawing.", + "page": "ns-font.html", + "section": "Namespaces" + }, + "ns-fs": { + "name": "Fs", + "sig": "Fs.*", + "kind": "namespace", + "tip": "The filesystem, wrapped into one safe, ergonomic API — no file descriptors, no byte buffers, no half-written files.", + "page": "ns-fs.html", + "section": "Namespaces" + }, + "ns-fx": { + "name": "Fx", + "sig": "Fx.*", + "kind": "namespace", + "tip": "Engine-owned transient effects.", + "page": "ns-fx.html", + "section": "Namespaces" + }, + "ns-grid": { + "name": "Grid", + "sig": "Grid.*", + "kind": "namespace", + "tip": "Tile geometry and pathfinding over the Map tilemap.", + "page": "ns-grid.html", + "section": "Namespaces" + }, + "ns-hash": { + "name": "Hash", + "sig": "Hash.*", + "kind": "namespace", + "tip": "Fast, non-cryptographic hashing for everyday game needs: turning string IDs into integer handles, mixing a few numbers into one deterministic seed, and…", + "page": "ns-hash.html", + "section": "Namespaces" + }, + "ns-http": { + "name": "Http", + "sig": "Http.*", + "kind": "namespace", + "tip": "A poll-based HTTP / HTTPS client for out-of-band data — leaderboards, cloud saves, remote config, telemetry, downloads.", + "page": "ns-http.html", + "section": "Namespaces" + }, + "ns-huge": { + "name": "Huge", + "sig": "Huge.*", + "kind": "namespace", + "tip": "Idle/incremental big numbers — magnitudes far past what a 32- or 64-bit integer can hold, for prestige currencies and exponential growth.", + "page": "ns-huge.html", + "section": "Namespaces" + }, + "ns-ivec2": { + "name": "IVec2", + "sig": "IVec2.*", + "kind": "namespace", + "tip": "Integer 2D vector math for tile and grid coordinates, cell offsets, and integer sizes.", + "page": "ns-ivec2.html", + "section": "Namespaces" + }, + "ns-input": { + "name": "Input", + "sig": "Input.*", + "kind": "namespace", + "tip": "Reading input.", + "page": "ns-input.html", + "section": "Namespaces" + }, + "ns-job": { + "name": "Job", + "sig": "Job.*", + "kind": "namespace", + "tip": "Background work that stays out of the frame.", + "page": "ns-job.html", + "section": "Namespaces" + }, + "ns-json": { + "name": "Json", + "sig": "Json.*", + "kind": "namespace", + "tip": "The text bridge over the Value tree: Json.encode turns a value tree into compact, stable JSON text and Json.parse reads it back.", + "page": "ns-json.html", + "section": "Namespaces" + }, + "ns-light": { + "name": "Light", + "sig": "Light.*", + "kind": "namespace", + "tip": "A software 2D light-accumulation pass over the framebuffer, run in a render phase after drawing the scene and before Screen.show.", + "page": "ns-light.html", + "section": "Namespaces" + }, + "ns-list": { + "name": "List", + "sig": "List.*", + "kind": "namespace", + "tip": "Operations over []T slices — length, ends access, push/pop, swap, search, and in-place reverse.", + "page": "ns-list.html", + "section": "Namespaces" + }, + "ns-log": { + "name": "Log", + "sig": "Log.*", + "kind": "namespace", + "tip": "Levelled, structured logging — the default way to answer \"what is my game doing?\" and \"why did that break?\", and a real step up from scattering print calls…", + "page": "ns-log.html", + "section": "Namespaces" + }, + "ns-map": { + "name": "Map", + "sig": "Map.*", + "kind": "namespace", + "tip": "A character grid the game paints and reads.", + "page": "ns-map.html", + "section": "Namespaces" + }, + "ns-math": { + "name": "Math", + "sig": "Math.*", + "kind": "namespace", + "tip": "Deterministic fixed-point math.", + "page": "ns-math.html", + "section": "Namespaces" + }, + "ns-memory": { + "name": "Memory", + "sig": "Memory.*", + "kind": "namespace", + "tip": "Raw memory — allocate byte and word buffers, copy and fill regions, and peek/poke individual bytes.", + "page": "ns-memory.html", + "section": "Namespaces" + }, + "ns-mime": { + "name": "Mime", + "sig": "Mime.*", + "kind": "namespace", + "tip": "Content-type detection for assets and downloads: map a filename to its MIME type, or refine the guess by peeking at a file's leading bytes.", + "page": "ns-mime.html", + "section": "Namespaces" + }, + "ns-motion": { + "name": "Motion", + "sig": "Motion.*", + "kind": "namespace", + "tip": "Ergonomic control of the engine-owned Motion component — the value tween the engine advances each tick (see the ECS engine-systems).", + "page": "ns-motion.html", + "section": "Namespaces" + }, + "ns-network": { + "name": "Network", + "sig": "Network.*", + "kind": "namespace", + "tip": "The low-level networking seam — send and poll datagrams, serialize and apply entity state, and read ownership and role.", + "page": "ns-network.html", + "section": "Namespaces" + }, + "ns-noise": { + "name": "Noise", + "sig": "Noise.*", + "kind": "namespace", + "tip": "Procedural noise — the primitive that terrain, caves, biomes, textures, clouds, wind, and object placement are built on.", + "page": "ns-noise.html", + "section": "Namespaces" + }, + "ns-os": { + "name": "Os", + "sig": "Os.*", + "kind": "namespace", + "tip": "The operating-system interface — the environment *around* the game rather than the game itself: the command line, environment variables, the standard streams,…", + "page": "ns-os.html", + "section": "Namespaces" + }, + "ns-path": { + "name": "Path", + "sig": "Path.*", + "kind": "namespace", + "tip": "Path manipulation as pure string operations — no disk access, no allocation surprises, just the joining and splitting every save system, mod loader, and asset…", + "page": "ns-path.html", + "section": "Namespaces" + }, + "ns-percent": { + "name": "Percent", + "sig": "Percent.*", + "kind": "namespace", + "tip": "A value clamped to [0, 1] — health fractions, volumes, and the t of an interpolation, without stray values slipping below 0 or above 1.", + "page": "ns-percent.html", + "section": "Namespaces" + }, + "ns-pool": { + "name": "Pool", + "sig": "Pool.*", + "kind": "namespace", + "tip": "Entity-pool statistics.", + "page": "ns-pool.html", + "section": "Namespaces" + }, + "ns-prefab": { + "name": "Prefab", + "sig": "Prefab.*", + "kind": "namespace", + "tip": "A prefab is a model with preset component fields — prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } } — spawned with spawn Grunt { Position { x:…", + "page": "ns-prefab.html", + "section": "Namespaces" + }, + "ns-process": { + "name": "Process", + "sig": "Process.*", + "kind": "namespace", + "tip": "Child processes, started and polled — what a launcher is made of.", + "page": "ns-process.html", + "section": "Namespaces" + }, + "ns-promise": { + "name": "Promise", + "sig": "Promise.*", + "kind": "namespace", + "tip": "Combine several Job futures and resolve the group on the main thread.", + "page": "ns-promise.html", + "section": "Namespaces" + }, + "ns-query": { + "name": "Query", + "sig": "Query.*", + "kind": "namespace", + "tip": "Spatial and set queries over the live entities that carry a property, built on the World reflection ABI.", + "page": "ns-query.html", + "section": "Namespaces" + }, + "ns-random": { + "name": "Random", + "sig": "Random.*", + "kind": "namespace", + "tip": "A seeded, deterministic RNG — same seed, same sequence, every run and every platform.", + "page": "ns-random.html", + "section": "Namespaces" + }, + "ns-rect": { + "name": "Rect", + "sig": "Rect.*", + "kind": "namespace", + "tip": "Axis-aligned rectangles for HUD layout boxes, hitboxes, and camera regions.", + "page": "ns-rect.html", + "section": "Namespaces" + }, + "ns-reflect": { + "name": "Reflect", + "sig": "Reflect.*", + "kind": "namespace", + "tip": "Runtime reflection over the world schema — inspect properties, fields, and entity state by name and by index.", + "page": "ns-reflect.html", + "section": "Namespaces" + }, + "ns-regex": { + "name": "Regex", + "sig": "Regex.*", + "kind": "namespace", + "tip": "Regular expressions with PCRE/PECL-compatible syntax over a **linear-time** Thompson NFA (a Pike VM), so a bad pattern from a modder can never trigger…", + "page": "ns-regex.html", + "section": "Namespaces" + }, + "ns-save": { + "name": "Save", + "sig": "Save.*", + "kind": "namespace", + "tip": "Whole-world save and restore — write a binary snapshot of the ECS world and read it back.", + "page": "ns-save.html", + "section": "Namespaces" + }, + "ns-screen": { + "name": "Screen", + "sig": "Screen.*", + "kind": "namespace", + "tip": "The 2D drawing surface.", + "page": "ns-screen.html", + "section": "Namespaces" + }, + "ns-set": { + "name": "Set", + "sig": "Set.*", + "kind": "namespace", + "tip": "A set of string members — membership tests for tags, unlocked achievements, or visited tiles.", + "page": "ns-set.html", + "section": "Namespaces" + }, + "ns-sprite": { + "name": "Sprite", + "sig": "Sprite.*", + "kind": "namespace", + "tip": "A namespaced spritesheet / atlas API.", + "page": "ns-sprite.html", + "section": "Namespaces" + }, + "ns-sync": { + "name": "Sync", + "sig": "Sync.*", + "kind": "namespace", + "tip": "The advanced, opt-in tier — here be dragons.", + "page": "ns-sync.html", + "section": "Namespaces" + }, + "ns-system": { + "name": "System", + "sig": "System.*", + "kind": "namespace", + "tip": "Low-level file and process access — raw file handles, reading a character, and running a shell command.", + "page": "ns-system.html", + "section": "Namespaces" + }, + "ns-text": { + "name": "Text", + "sig": "Text.*", + "kind": "namespace", + "tip": "String queries and building over string values — length, character access, substrings, search, prefix/suffix tests, and int conversion.", + "page": "ns-text.html", + "section": "Namespaces" + }, + "ns-tiled": { + "name": "Tiled", + "sig": "Tiled.*", + "kind": "namespace", + "tip": "Load and draw Tiled maps.", + "page": "ns-tiled.html", + "section": "Namespaces" + }, + "ns-time": { + "name": "Time", + "sig": "Time.*", + "kind": "namespace", + "tip": "The frame clock.", + "page": "ns-time.html", + "section": "Namespaces" + }, + "ns-tween": { + "name": "Tween", + "sig": "Tween.*", + "kind": "namespace", + "tip": "Value interpolation over a timeline, off the fixed frame clock.", + "page": "ns-tween.html", + "section": "Namespaces" + }, + "ns-udp": { + "name": "Udp", + "sig": "Udp.*", + "kind": "namespace", + "tip": "Plain IPv4 datagrams, polled — the transport under a game's own netcode: peer-to-peer play, a LAN lobby, a STUN query.", + "page": "ns-udp.html", + "section": "Namespaces" + }, + "ns-ui": { + "name": "Ui", + "sig": "Ui.*", + "kind": "namespace", + "tip": "The retained-mode menu API over a ui block.", + "page": "ns-ui.html", + "section": "Namespaces" + }, + "ns-unicode": { + "name": "Unicode", + "sig": "Unicode.*", + "kind": "namespace", + "tip": "Correct-by-default text over UTF-8.", + "page": "ns-unicode.html", + "section": "Namespaces" + }, + "ns-uuid": { + "name": "Uuid", + "sig": "Uuid.*", + "kind": "namespace", + "tip": "Universally-unique identifiers — stable IDs that don't collide, generated locally with no central authority handing out numbers.", + "page": "ns-uuid.html", + "section": "Namespaces" + }, + "ns-value": { + "name": "Value", + "sig": "Value.*", + "kind": "namespace", + "tip": "A generic, self-describing value tree — the node type reflection serializes into and JSON round-trips through.", + "page": "ns-value.html", + "section": "Namespaces" + }, + "ns-vector": { + "name": "Vector", + "sig": "Vector.*", + "kind": "namespace", + "tip": "2D vector math for positions, velocities, and directions.", + "page": "ns-vector.html", + "section": "Namespaces" + }, + "ns-world": { + "name": "World", + "sig": "World.*", + "kind": "namespace", + "tip": "Runtime reflection over the ECS world — read and write component fields by id, spawn and inspect entities, and register components at runtime.", + "page": "ns-world.html", + "section": "Namespaces" + }, + "ns-xml": { + "name": "Xml", + "sig": "Xml.*", + "kind": "namespace", + "tip": "A minimal, deterministic XML reader for the element/attribute/CDATA subset the native Tiled formats (TMX/TSX/TX) use.", + "page": "ns-xml.html", + "section": "Namespaces" + } + }, + "colors_page": "ns-color" + } +} \ No newline at end of file diff --git a/sync-add.html b/sync-add.html new file mode 100644 index 00000000..8d22d144 --- /dev/null +++ b/sync-add.html @@ -0,0 +1,29 @@ + + + + + +Sync.add — Ludic + + + + + + +
+
API Reference › Sync › Sync.add
+
+ method +

Sync.add

+
+ Sync.add(atomic, delta) -> int +

Add to the counter; return the new value.

+ +

Example

let a = Sync.atomic()
+let total = Sync.add(a, 1)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-atomic.html b/sync-atomic.html new file mode 100644 index 00000000..62b4d6e7 --- /dev/null +++ b/sync-atomic.html @@ -0,0 +1,28 @@ + + + + + +Sync.atomic — Ludic + + + + + + +
+
API Reference › Sync › Sync.atomic
+
+ method +

Sync.atomic

+
+ Sync.atomic() -> int +

Create an atomic counter (starts at 0).

Returns an atomic-counter handle. Read it with Sync.get, write with Sync.set, accumulate with Sync.add, and swap conditionally with Sync.cas.

+ +

Example

let a = Sync.atomic()
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-can_recv.html b/sync-can_recv.html new file mode 100644 index 00000000..ca4c2bc3 --- /dev/null +++ b/sync-can_recv.html @@ -0,0 +1,29 @@ + + + + + +Sync.can_recv — Ludic + + + + + + +
+
API Reference › Sync › Sync.can_recv
+
+ method +

Sync.can_recv

+
+ Sync.can_recv(channel) -> bool +

Is there a value waiting?

+ +

Example

let ch = Sync.channel()
+let has = Sync.can_recv(ch)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-cas.html b/sync-cas.html new file mode 100644 index 00000000..aeb923af --- /dev/null +++ b/sync-cas.html @@ -0,0 +1,29 @@ + + + + + +Sync.cas — Ludic + + + + + + +
+
API Reference › Sync › Sync.cas
+
+ method +

Sync.cas

+
+ Sync.cas(atomic, expect, next) -> bool +

Compare-and-set the counter.

+ +

Example

let a = Sync.atomic()
+let swapped = Sync.cas(a, 0, 1)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-channel.html b/sync-channel.html new file mode 100644 index 00000000..3e2c8773 --- /dev/null +++ b/sync-channel.html @@ -0,0 +1,28 @@ + + + + + +Sync.channel — Ludic + + + + + + +
+
API Reference › Sync › Sync.channel
+
+ method +

Sync.channel

+
+ Sync.channel() -> int +

Create a bounded int FIFO channel.

Returns a channel handle — a fixed-capacity queue of ints for handing values between a producer and a consumer. Push with Sync.send, pull the oldest with Sync.recv, and check with Sync.can_recv / Sync.len.

+ +

Example

let ch = Sync.channel()
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-cpu_count.html b/sync-cpu_count.html new file mode 100644 index 00000000..61b76dc4 --- /dev/null +++ b/sync-cpu_count.html @@ -0,0 +1,29 @@ + + + + + +Sync.cpu_count — Ludic + + + + + + +
+
API Reference › Sync › Sync.cpu_count
+
+ method +

Sync.cpu_count

+
+ Sync.cpu_count() -> int +

The machine's logical cores, from 1 to 64. Job.parallel_for runs on that many threads: a pool of +one per core but one, plus the caller.

+ +

Example

let lanes = Sync.cpu_count()
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-get.html b/sync-get.html new file mode 100644 index 00000000..32335793 --- /dev/null +++ b/sync-get.html @@ -0,0 +1,29 @@ + + + + + +Sync.get — Ludic + + + + + + +
+
API Reference › Sync › Sync.get
+
+ method +

Sync.get

+
+ Sync.get(atomic) -> int +

Read the counter.

+ +

Example

let a = Sync.atomic()
+let v = Sync.get(a)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-len.html b/sync-len.html new file mode 100644 index 00000000..9108e25a --- /dev/null +++ b/sync-len.html @@ -0,0 +1,29 @@ + + + + + +Sync.len — Ludic + + + + + + +
+
API Reference › Sync › Sync.len
+
+ method +

Sync.len

+
+ Sync.len(channel) -> int +

How many values are queued.

+ +

Example

let ch = Sync.channel()
+let n = Sync.len(ch)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-lock.html b/sync-lock.html new file mode 100644 index 00000000..0b8ba514 --- /dev/null +++ b/sync-lock.html @@ -0,0 +1,29 @@ + + + + + +Sync.lock — Ludic + + + + + + +
+
API Reference › Sync › Sync.lock
+
+ method +

Sync.lock

+
+ Sync.lock(mutex) -> void +

Take the lock.

+ +

Example

let m = Sync.mutex()
+Sync.lock(m)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-mutex.html b/sync-mutex.html new file mode 100644 index 00000000..adcc1444 --- /dev/null +++ b/sync-mutex.html @@ -0,0 +1,28 @@ + + + + + +Sync.mutex — Ludic + + + + + + +
+
API Reference › Sync › Sync.mutex
+
+ method +

Sync.mutex

+
+ Sync.mutex() -> int +

Create a cooperative lock.

Creates a mutex handle for guarding a critical section. On the deterministic single-threaded runtime it never blocks — pair Sync.lock / Sync.unlock around the section, or probe with Sync.try_lock.

+ +

Example

let m = Sync.mutex()
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-recv.html b/sync-recv.html new file mode 100644 index 00000000..03ab1859 --- /dev/null +++ b/sync-recv.html @@ -0,0 +1,30 @@ + + + + + +Sync.recv — Ludic + + + + + + +
+
API Reference › Sync › Sync.recv
+
+ method +

Sync.recv

+
+ Sync.recv(channel) -> int +

Dequeue the oldest value.

+ +

Example

let ch = Sync.channel()
+Sync.send(ch, 42)
+let v = Sync.recv(ch)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-send.html b/sync-send.html new file mode 100644 index 00000000..2e02f5e4 --- /dev/null +++ b/sync-send.html @@ -0,0 +1,29 @@ + + + + + +Sync.send — Ludic + + + + + + +
+
API Reference › Sync › Sync.send
+
+ method +

Sync.send

+
+ Sync.send(channel, value) -> bool +

Enqueue a value (false if full).

+ +

Example

let ch = Sync.channel()
+let sent = Sync.send(ch, 42)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-set.html b/sync-set.html new file mode 100644 index 00000000..e9763cc3 --- /dev/null +++ b/sync-set.html @@ -0,0 +1,29 @@ + + + + + +Sync.set — Ludic + + + + + + +
+
API Reference › Sync › Sync.set
+
+ method +

Sync.set

+
+ Sync.set(atomic, value) -> void +

Store a value in the counter.

+ +

Example

let a = Sync.atomic()
+Sync.set(a, 10)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-try_lock.html b/sync-try_lock.html new file mode 100644 index 00000000..2547a7bc --- /dev/null +++ b/sync-try_lock.html @@ -0,0 +1,29 @@ + + + + + +Sync.try_lock — Ludic + + + + + + +
+
API Reference › Sync › Sync.try_lock
+
+ method +

Sync.try_lock

+
+ Sync.try_lock(mutex) -> bool +

Take the lock only if it is free.

+ +

Example

let m = Sync.mutex()
+let got = Sync.try_lock(m)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/sync-unlock.html b/sync-unlock.html new file mode 100644 index 00000000..715baa05 --- /dev/null +++ b/sync-unlock.html @@ -0,0 +1,30 @@ + + + + + +Sync.unlock — Ludic + + + + + + +
+
API Reference › Sync › Sync.unlock
+
+ method +

Sync.unlock

+
+ Sync.unlock(mutex) -> void +

Release the lock.

+ +

Example

let m = Sync.mutex()
+Sync.lock(m)
+Sync.unlock(m)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_close.html b/system-file_close.html new file mode 100644 index 00000000..c8df9085 --- /dev/null +++ b/system-file_close.html @@ -0,0 +1,28 @@ + + + + + +System.file_close — Ludic + + + + + + +
+
API Reference › System › System.file_close
+
+ method +

System.file_close

+
+ System.file_close(file) -> void +

Closes file, flushing any buffered writes.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_open.html b/system-file_open.html new file mode 100644 index 00000000..f3cfe219 --- /dev/null +++ b/system-file_open.html @@ -0,0 +1,28 @@ + + + + + +System.file_open — Ludic + + + + + + +
+
API Reference › System › System.file_open
+
+ method +

System.file_open

+
+ System.file_open(path, mode) -> pointer +

Opens path with the given mode (as C fopen) and returns a file handle, or null on failure.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_read.html b/system-file_read.html new file mode 100644 index 00000000..00ccaf8b --- /dev/null +++ b/system-file_read.html @@ -0,0 +1,28 @@ + + + + + +System.file_read — Ludic + + + + + + +
+
API Reference › System › System.file_read
+
+ method +

System.file_read

+
+ System.file_read(file, buf, n) -> int +

Reads up to n bytes from file into buf and returns the number read.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_seek.html b/system-file_seek.html new file mode 100644 index 00000000..afffc322 --- /dev/null +++ b/system-file_seek.html @@ -0,0 +1,28 @@ + + + + + +System.file_seek — Ludic + + + + + + +
+
API Reference › System › System.file_seek
+
+ method +

System.file_seek

+
+ System.file_seek(file, offset, whence) -> int +

Seeks file to offset relative to whence (as C fseek).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_tell.html b/system-file_tell.html new file mode 100644 index 00000000..9a8ff0b5 --- /dev/null +++ b/system-file_tell.html @@ -0,0 +1,28 @@ + + + + + +System.file_tell — Ludic + + + + + + +
+
API Reference › System › System.file_tell
+
+ method +

System.file_tell

+
+ System.file_tell(file) -> int +

Returns the current read/write offset of file.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-file_write.html b/system-file_write.html new file mode 100644 index 00000000..f187f825 --- /dev/null +++ b/system-file_write.html @@ -0,0 +1,28 @@ + + + + + +System.file_write — Ludic + + + + + + +
+
API Reference › System › System.file_write
+
+ method +

System.file_write

+
+ System.file_write(file, buf, n) -> int +

Writes n bytes from buf to file and returns the number written.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-read_char.html b/system-read_char.html new file mode 100644 index 00000000..8a83471a --- /dev/null +++ b/system-read_char.html @@ -0,0 +1,28 @@ + + + + + +System.read_char — Ludic + + + + + + +
+
API Reference › System › System.read_char
+
+ method +

System.read_char

+
+ System.read_char() -> int +

Reads and returns the next byte from standard input, or a negative value at end of input.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/system-run.html b/system-run.html new file mode 100644 index 00000000..c5112c2a --- /dev/null +++ b/system-run.html @@ -0,0 +1,28 @@ + + + + + +System.run — Ludic + + + + + + +
+
API Reference › System › System.run
+
+ method +

System.run

+
+ System.run(command) -> int +

Runs command through the system shell and returns its exit status. Blocks until it finishes.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-char_at.html b/text-char_at.html new file mode 100644 index 00000000..24d916b5 --- /dev/null +++ b/text-char_at.html @@ -0,0 +1,35 @@ + + + + + +Text.char_at — Ludic + + + + + + +
+
API Reference › Text › Text.char_at
+
+ method +

Text.char_at

+
+ Text.char_at(s, i) -> int +

Returns the byte at position i in s as an int in the range 0..255 — for example Text.char_at("hello", 0) is 104, the code for h. Indices are zero-based and count bytes. Use it to inspect or classify individual characters, such as testing whether a key of input is a digit.

+

Parameters

sthe string to read from
ithe zero-based byte index
+

Example

program FirstIsDigit {
+  var entry: string = "7up"
+
+  handler Check phase Update {
+    let c = Text.char_at(entry, 0)
+    let is_digit = c >= 48 and c <= 57   # true: '7'
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-concat.html b/text-concat.html new file mode 100644 index 00000000..ca9cd88d --- /dev/null +++ b/text-concat.html @@ -0,0 +1,34 @@ + + + + + +Text.concat — Ludic + + + + + + +
+
API Reference › Text › Text.concat
+
+ method +

Text.concat

+
+ Text.concat(a, b) -> string +

Returns a fresh string that is a followed by b. It is the call form of the a + b string operator and allocates a new string, leaving the inputs untouched. For building a line from several pieces, backtick interpolation such as score: {n} is usually clearer; reach for Text.concat when joining two values you already hold.

+

Parameters

athe left string
bthe right string
+

Example

program Greeting {
+  var name: string = "world"
+
+  handler Build phase Update {
+    let line = Text.concat("hello ", name)   # "hello world"
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-contains.html b/text-contains.html new file mode 100644 index 00000000..10319df0 --- /dev/null +++ b/text-contains.html @@ -0,0 +1,36 @@ + + + + + +Text.contains — Ludic + + + + + + +
+
API Reference › Text › Text.contains
+
+ method +

Text.contains

+
+ Text.contains(s, sub) -> bool +

Returns true when sub occurs anywhere inside s. An empty substring is always considered present. Use it for a quick membership test — filtering a log line, checking for a forbidden word, or seeing whether a path includes a folder — when you do not need the position. Reach for Text.index_of when you also want where it matched.

+

Parameters

sthe string to search
subthe substring to look for
+

Example

program Filter {
+  var line: string = "error: disk full"
+
+  handler Scan phase Update {
+    if Text.contains(line, "error") {
+      # flag it
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-ends_with.html b/text-ends_with.html new file mode 100644 index 00000000..85e3ce7c --- /dev/null +++ b/text-ends_with.html @@ -0,0 +1,34 @@ + + + + + +Text.ends_with — Ludic + + + + + + +
+
API Reference › Text › Text.ends_with
+
+ method +

Text.ends_with

+
+ Text.ends_with(s, suffix) -> bool +

Returns true when s ends with suffix, comparing the tail of the string over the suffix's length. If the suffix is longer than the string it simply returns false. Use it to detect file types by extension, recognize a trailing marker, or validate that input closes as expected.

+

Parameters

sthe string to test
suffixthe trailing substring to look for
+

Example

program IsImage {
+  var filename: string = "hero.png"
+
+  handler Classify phase Update {
+    let png = Text.ends_with(filename, ".png")   # true
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-equals.html b/text-equals.html new file mode 100644 index 00000000..cfec6553 --- /dev/null +++ b/text-equals.html @@ -0,0 +1,37 @@ + + + + + +Text.equals — Ludic + + + + + + +
+
API Reference › Text › Text.equals
+
+ method +

Text.equals

+
+ Text.equals(a, b) -> bool +

Returns true when a and b hold the same bytes in the same order. This is a value comparison, not a pointer check, and is exactly what the == operator does between two strings — Text.equals is the named form for readability. Use it to match input against a keyword or compare a slice with an expected token.

+

Parameters

athe first string
bthe second string
+

Example

program Guess {
+  var answer: string = "swordfish"
+  var attempt: string = "swordfish"
+
+  handler Check phase Update {
+    if Text.equals(attempt, answer) {
+      # correct
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-from_int.html b/text-from_int.html new file mode 100644 index 00000000..da79d047 --- /dev/null +++ b/text-from_int.html @@ -0,0 +1,34 @@ + + + + + +Text.from_int — Ludic + + + + + + +
+
API Reference › Text › Text.from_int
+
+ method +

Text.from_int

+
+ Text.from_int(n) -> string +

Returns the decimal text of the integer n — Text.from_int(1234) is "1234", and negatives keep their sign. It is the same conversion as the bare string(n) and the inverse of Text.to_int. Use it when you need a number as characters — to measure its width, concatenate it, or store it — though drawing a number to the screen is better done with Screen.draw_number.

+

Parameters

nthe integer to render
+

Example

program ScoreLabel {
+  var score: int = 1500
+
+  handler Build phase Update {
+    let label = Text.concat("score: ", Text.from_int(score))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-index_of.html b/text-index_of.html new file mode 100644 index 00000000..e690aca1 --- /dev/null +++ b/text-index_of.html @@ -0,0 +1,36 @@ + + + + + +Text.index_of — Ludic + + + + + + +
+
API Reference › Text › Text.index_of
+
+ method +

Text.index_of

+
+ Text.index_of(s, sub) -> int +

Returns the zero-based byte index where sub first appears in s, or -1 if it never does — so Text.index_of("hello world", "world") is 6. Use it to locate a separator before slicing, or as a present-and-where test in one call: guard with if idx >= 0. It reports the first match only.

+

Parameters

sthe string to search
subthe substring to locate
+

Example

program KeyValue {
+  var pair: string = "hp=42"
+
+  handler Parse phase Update {
+    let eq = Text.index_of(pair, "=")            # 2
+    let key = Text.slice(pair, 0, eq)            # "hp"
+    let value = Text.to_int(Text.slice(pair, eq + 1, Text.length(pair)))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-join.html b/text-join.html new file mode 100644 index 00000000..7b3894b5 --- /dev/null +++ b/text-join.html @@ -0,0 +1,32 @@ + + + + + +Text.join — Ludic + + + + + + +
+
API Reference › Text › Text.join
+
+ method +

Text.join

+
+ Text.join(parts, sep) -> string +

Concatenates the strings in the slice parts into one string, placing sep between each — the inverse of Text.split. With an empty separator it simply concatenates. Use it to build a CSV row, a path, or a display list from pieces.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let line = Text.join(fields, ",")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-length.html b/text-length.html new file mode 100644 index 00000000..98094c54 --- /dev/null +++ b/text-length.html @@ -0,0 +1,34 @@ + + + + + +Text.length — Ludic + + + + + + +
+
API Reference › Text › Text.length
+
+ method +

Text.length

+
+ Text.length(s) -> int +

Returns how many bytes are in s, not counting the terminating zero. It is the same count as the bare len(s), exposed under the Text namespace for discoverability. Use it to size a HUD field, walk a string by index, or check for emptiness with Text.length(s) == 0.

+

Parameters

sthe string to measure
+

Example

program NameWidth {
+  var label: string = "player one"
+
+  handler Draw phase Render {
+    let width = Text.length(label) * 6   # 6 px per glyph
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-lower.html b/text-lower.html new file mode 100644 index 00000000..40438b67 --- /dev/null +++ b/text-lower.html @@ -0,0 +1,32 @@ + + + + + +Text.lower — Ludic + + + + + + +
+
API Reference › Text › Text.lower
+
+ method +

Text.lower

+
+ Text.lower(s) -> string +

Returns a fresh string with every ASCII letter A-Z turned to lowercase; other bytes pass through unchanged. Pair it with Text.equals for case-insensitive matching by lowercasing both sides. This is ASCII-only — accented and non-Latin letters are left as-is; for full Unicode case mapping use Unicode.lower.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Text.equals(Text.lower(cmd), "quit") { stop() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-pad_left.html b/text-pad_left.html new file mode 100644 index 00000000..2c9cb803 --- /dev/null +++ b/text-pad_left.html @@ -0,0 +1,32 @@ + + + + + +Text.pad_left — Ludic + + + + + + +
+
API Reference › Text › Text.pad_left
+
+ method +

Text.pad_left

+
+ Text.pad_left(s, width) -> string +

Returns s padded with leading spaces until it is at least width bytes long; a string already that long is returned unchanged (never truncated). Right-aligns numbers and labels in a fixed-width HUD column.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let cell = Text.pad_left(Text.from_int(score), 6)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-pad_right.html b/text-pad_right.html new file mode 100644 index 00000000..4c7868f4 --- /dev/null +++ b/text-pad_right.html @@ -0,0 +1,32 @@ + + + + + +Text.pad_right — Ludic + + + + + + +
+
API Reference › Text › Text.pad_right
+
+ method +

Text.pad_right

+
+ Text.pad_right(s, width) -> string +

Returns s padded with trailing spaces until it is at least width bytes long; a string already that long is returned unchanged. Left-aligns text into fixed-width columns for tables and menus.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let cell = Text.pad_right(label, 12)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-repeat.html b/text-repeat.html new file mode 100644 index 00000000..c56c8594 --- /dev/null +++ b/text-repeat.html @@ -0,0 +1,32 @@ + + + + + +Text.repeat — Ludic + + + + + + +
+
API Reference › Text › Text.repeat
+
+ method +

Text.repeat

+
+ Text.repeat(s, n) -> string +

Returns s concatenated with itself n times; n <= 0 yields the empty string. Handy for drawing a text bar, an indent, or a separator line without a loop.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let bar = Text.repeat("#", filled)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-replace.html b/text-replace.html new file mode 100644 index 00000000..b36deb49 --- /dev/null +++ b/text-replace.html @@ -0,0 +1,32 @@ + + + + + +Text.replace — Ludic + + + + + + +
+
API Reference › Text › Text.replace
+
+ method +

Text.replace

+
+ Text.replace(s, from, to) -> string +

Returns a fresh string with every occurrence of from in s replaced by to; if from never appears the string is returned unchanged. Use it to substitute tokens in a template, swap a delimiter, or sanitize input.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let path = Text.replace(name, " ", "_")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-slice.html b/text-slice.html new file mode 100644 index 00000000..acdacd21 --- /dev/null +++ b/text-slice.html @@ -0,0 +1,35 @@ + + + + + +Text.slice — Ludic + + + + + + +
+
API Reference › Text › Text.slice
+
+ method +

Text.slice

+
+ Text.slice(s, a, b) -> string +

Returns a fresh string of the bytes from index a up to but not including b — the half-open range [a, b), so Text.slice("hello world", 0, 5) is "hello". It is the call form of the s[a..b] slice syntax and allocates a new string. Use it to cut a fixed-width field, drop a prefix, or take the first few characters of a name.

+

Parameters

sthe source string
athe start byte index (inclusive)
bthe end byte index (exclusive)
+

Example

program FirstWord {
+  var line: string = "hello world"
+
+  handler Split phase Update {
+    let space = Text.index_of(line, " ")   # 5
+    let word = Text.slice(line, 0, space)  # "hello"
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-split.html b/text-split.html new file mode 100644 index 00000000..b95e345d --- /dev/null +++ b/text-split.html @@ -0,0 +1,32 @@ + + + + + +Text.split — Ludic + + + + + + +
+
API Reference › Text › Text.split
+
+ method +

Text.split

+
+ Text.split(s, sep) -> []string +

Splits s at every occurrence of the non-empty separator sep and returns the pieces as a []string — the inverse of Text.join. Adjacent separators yield empty pieces, and a string with no separator returns a one-element list holding the whole string. Use it to parse CSV rows, command arguments, or delimited save data.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let fields = Text.split(line, ",")
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-starts_with.html b/text-starts_with.html new file mode 100644 index 00000000..bc292edc --- /dev/null +++ b/text-starts_with.html @@ -0,0 +1,36 @@ + + + + + +Text.starts_with — Ludic + + + + + + +
+
API Reference › Text › Text.starts_with
+
+ method +

Text.starts_with

+
+ Text.starts_with(s, prefix) -> bool +

Returns true when s begins with prefix, comparing byte for byte over the length of the prefix. An empty prefix always matches. Use it to route commands ("/help ..."), recognize a tag, or branch on a filename extension without slicing first.

+

Parameters

sthe string to test
prefixthe leading substring to look for
+

Example

program Command {
+  var input: string = "/say hello"
+
+  handler Parse phase Update {
+    if Text.starts_with(input, "/say ") {
+      let msg = Text.slice(input, 5, Text.length(input))
+    }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-to_int.html b/text-to_int.html new file mode 100644 index 00000000..358da6b8 --- /dev/null +++ b/text-to_int.html @@ -0,0 +1,34 @@ + + + + + +Text.to_int — Ludic + + + + + + +
+
API Reference › Text › Text.to_int
+
+ method +

Text.to_int

+
+ Text.to_int(s) -> int +

Reads an optional sign and the leading decimal digits of s and returns them as an int, stopping at the first non-digit — so Text.to_int("42abc") is 42. A string with no leading number yields 0. Use it to turn typed input, a config value, or a parsed field into a number. It is the inverse of Text.from_int.

+

Parameters

sthe string to parse
+

Example

program ParseAmount {
+  var typed: string = "250"
+
+  handler Apply phase Update {
+    let gold = Text.to_int(typed)   # 250
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-trim.html b/text-trim.html new file mode 100644 index 00000000..0a2e5de4 --- /dev/null +++ b/text-trim.html @@ -0,0 +1,32 @@ + + + + + +Text.trim — Ludic + + + + + + +
+
API Reference › Text › Text.trim
+
+ method +

Text.trim

+
+ Text.trim(s) -> string +

Returns a fresh string with leading and trailing whitespace (spaces, tabs, newlines, carriage returns) removed; inner spacing is kept. Use it to clean up typed input or a parsed field before comparing or storing it.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let name = Text.trim(raw_input)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/text-upper.html b/text-upper.html new file mode 100644 index 00000000..fd7bb361 --- /dev/null +++ b/text-upper.html @@ -0,0 +1,32 @@ + + + + + +Text.upper — Ludic + + + + + + +
+
API Reference › Text › Text.upper
+
+ method +

Text.upper

+
+ Text.upper(s) -> string +

Returns a fresh string with every ASCII letter a-z turned to uppercase; other bytes pass through unchanged. The input is left untouched. Use it to normalize input before comparing, or to shout a label. This is ASCII-only — accented and non-Latin letters are left as-is; for full Unicode case mapping use Unicode.upper.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let shout = Text.upper("ready")   # "READY"
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-animated.html b/tiled-animated.html new file mode 100644 index 00000000..ea753927 --- /dev/null +++ b/tiled-animated.html @@ -0,0 +1,28 @@ + + + + + +Tiled.animated — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.animated
+
+ method +

Tiled.animated

+
+ Tiled.animated(map, gid: int) -> int +

Returns 1 if the tile a GID resolves to carries a non-empty <animation>, else 0.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-cell_x.html b/tiled-cell_x.html new file mode 100644 index 00000000..2c55c7a3 --- /dev/null +++ b/tiled-cell_x.html @@ -0,0 +1,28 @@ + + + + + +Tiled.cell_x — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.cell_x
+
+ method +

Tiled.cell_x

+
+ Tiled.cell_x(map, x: int, y: int) -> int +

Returns the screen x (before the camera) of tile cell (x,y) under the map's orientation: orthogonal x·tilew, isometric (x−y)·tilew/2, staggered/hexagonal x·tilew with alternate rows shoved half a tile per staggerindex.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-cell_y.html b/tiled-cell_y.html new file mode 100644 index 00000000..321afc61 --- /dev/null +++ b/tiled-cell_y.html @@ -0,0 +1,28 @@ + + + + + +Tiled.cell_y — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.cell_y
+
+ method +

Tiled.cell_y

+
+ Tiled.cell_y(map, x: int, y: int) -> int +

Returns the screen y of tile cell (x,y): orthogonal y·tileh, isometric (x+y)·tileh/2, staggered y·tileh/2, hexagonal rows stepping by (tileh+hexsidelength)/2.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-collide.html b/tiled-collide.html new file mode 100644 index 00000000..ed6640a6 --- /dev/null +++ b/tiled-collide.html @@ -0,0 +1,28 @@ + + + + + +Tiled.collide — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.collide
+
+ method +

Tiled.collide

+
+ Tiled.collide(map, layer: int) -> void +

Projects a layer to the byte tilemap driving collision from per-tile metadata alone — a tile's <objectgroup> hitbox or its solid/oneway/trigger property — so a tile with no collision metadata stays passable. Use this to drive collision from a visual layer; Tiled.project is the designated-collision-layer form where any non-zero GID is solid.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-collision_kind.html b/tiled-collision_kind.html new file mode 100644 index 00000000..3769bb1b --- /dev/null +++ b/tiled-collision_kind.html @@ -0,0 +1,28 @@ + + + + + +Tiled.collision_kind — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.collision_kind
+
+ method +

Tiled.collision_kind

+
+ Tiled.collision_kind(map, gid: int) -> int +

Returns the collision kind a GID resolves to from its tile metadata, in the design's priority order (per-tile <objectgroup> hitboxes, then the property convention): 0 none, 1 solid, 2 one-way, 3 trigger. The collision-layer fallback (any non-zero GID solid) is applied by Tiled.project, not here.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-draw.html b/tiled-draw.html new file mode 100644 index 00000000..4d195683 --- /dev/null +++ b/tiled-draw.html @@ -0,0 +1,28 @@ + + + + + +Tiled.draw — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.draw
+
+ method +

Tiled.draw

+
+ Tiled.draw(map, camx: int, camy: int) -> void +

Draws every visible tile layer in file order to the framebuffer, offset by the camera (camx, camy), blitting each tile from its tileset image and applying the GID's flip flags at draw time.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-draw_anim.html b/tiled-draw_anim.html new file mode 100644 index 00000000..208ca58f --- /dev/null +++ b/tiled-draw_anim.html @@ -0,0 +1,28 @@ + + + + + +Tiled.draw_anim — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.draw_anim
+
+ method +

Tiled.draw_anim

+
+ Tiled.draw_anim(map, camx: int, camy: int, frame: int) -> void +

Like Tiled.draw, but animated tiles are advanced to the engine frame (pass Time.frame) and every tile object on the object layers is drawn too. The animation frame is a pure function of the fixed 60/s clock, so it reproduces frame-for-frame across runs and lockstep replays.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-frame_gid.html b/tiled-frame_gid.html new file mode 100644 index 00000000..71b09ab0 --- /dev/null +++ b/tiled-frame_gid.html @@ -0,0 +1,28 @@ + + + + + +Tiled.frame_gid — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.frame_gid
+
+ method +

Tiled.frame_gid

+
+ Tiled.frame_gid(map, gid: int, frame: int) -> int +

Returns the GID an (animated) gid resolves to at engine frame: elapsed ms = frame * 1000 / 60, walked across the tileset <animation>'s frame durations to the current frame's tile — its flip flags preserved. A GID with no animation passes through unchanged.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-gid.html b/tiled-gid.html new file mode 100644 index 00000000..f9a811a5 --- /dev/null +++ b/tiled-gid.html @@ -0,0 +1,28 @@ + + + + + +Tiled.gid — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.gid
+
+ method +

Tiled.gid

+
+ Tiled.gid(map, layer: int, x: int, y: int) -> int +

Returns the raw global tile id (with its flip flags still set) at cell (x,y) in tile layer, or 0 (empty) out of bounds or for a non-tile layer.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-height.html b/tiled-height.html new file mode 100644 index 00000000..c9f91fa4 --- /dev/null +++ b/tiled-height.html @@ -0,0 +1,28 @@ + + + + + +Tiled.height — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.height
+
+ method +

Tiled.height

+
+ Tiled.height(map) -> int +

Returns the map's height in tiles.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_count.html b/tiled-layer_count.html new file mode 100644 index 00000000..15fa57e0 --- /dev/null +++ b/tiled-layer_count.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_count — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_count
+
+ method +

Tiled.layer_count

+
+ Tiled.layer_count(map) -> int +

Returns the number of layers (tile, object, image and group layers are all counted; a group's children are flattened in after it).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_kind.html b/tiled-layer_kind.html new file mode 100644 index 00000000..d3dddcc5 --- /dev/null +++ b/tiled-layer_kind.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_kind — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_kind
+
+ method +

Tiled.layer_kind

+
+ Tiled.layer_kind(map, layer: int) -> int +

Returns a layer's kind: 0 tile layer, 1 object layer, 2 image layer, 3 group (its children are flattened in after it), or -1 out of range.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_name.html b/tiled-layer_name.html new file mode 100644 index 00000000..25b588f3 --- /dev/null +++ b/tiled-layer_name.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_name — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_name
+
+ method +

Tiled.layer_name

+
+ Tiled.layer_name(map, index: int) -> string +

Returns the name of the layer at index, or "" if the index is out of range.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_offsetx.html b/tiled-layer_offsetx.html new file mode 100644 index 00000000..5a6d2af0 --- /dev/null +++ b/tiled-layer_offsetx.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_offsetx — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_offsetx
+
+ method +

Tiled.layer_offsetx

+
+ Tiled.layer_offsetx(map, layer: int) -> int +

Returns a layer's horizontal draw offset in pixels (0 when unset).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_offsety.html b/tiled-layer_offsety.html new file mode 100644 index 00000000..553bc6a0 --- /dev/null +++ b/tiled-layer_offsety.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_offsety — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_offsety
+
+ method +

Tiled.layer_offsety

+
+ Tiled.layer_offsety(map, layer: int) -> int +

Returns a layer's vertical draw offset in pixels (0 when unset).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_opacity.html b/tiled-layer_opacity.html new file mode 100644 index 00000000..f35c60bc --- /dev/null +++ b/tiled-layer_opacity.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_opacity — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_opacity
+
+ method +

Tiled.layer_opacity

+
+ Tiled.layer_opacity(map, layer: int) -> string +

Returns a layer's opacity as a string in [0,1] ("1" when unset).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-layer_tint.html b/tiled-layer_tint.html new file mode 100644 index 00000000..eea116cf --- /dev/null +++ b/tiled-layer_tint.html @@ -0,0 +1,28 @@ + + + + + +Tiled.layer_tint — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.layer_tint
+
+ method +

Tiled.layer_tint

+
+ Tiled.layer_tint(map, layer: int) -> string +

Returns a layer's tintcolor (e.g. #ff0000), or "" if none — a group's tint applies recursively to its children.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-load.html b/tiled-load.html new file mode 100644 index 00000000..a24adc29 --- /dev/null +++ b/tiled-load.html @@ -0,0 +1,28 @@ + + + + + +Tiled.load — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.load
+
+ method +

Tiled.load

+
+ Tiled.load(path: string) -> map +

Reads a map file (TMX or TMJ), builds the runtime rt_tmap model — resolving external tilesets and loading their images relative to the map file — and projects the designated collision layer (a tile layer named collision/solids/walls) down to the legacy byte tilemap so Grid/Path and esys_move keep working unchanged. Returns the loaded map.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-load_types.html b/tiled-load_types.html new file mode 100644 index 00000000..8ca244a6 --- /dev/null +++ b/tiled-load_types.html @@ -0,0 +1,28 @@ + + + + + +Tiled.load_types — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.load_types
+
+ method +

Tiled.load_types

+
+ Tiled.load_types(path: string) -> int +

Loads an objecttypes.xml custom-type table so class/enum properties resolve their defaults; returns the number of types loaded. Each <objecttype> maps to its default <property> list.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-object.html b/tiled-object.html new file mode 100644 index 00000000..f89efde3 --- /dev/null +++ b/tiled-object.html @@ -0,0 +1,28 @@ + + + + + +Tiled.object — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.object
+
+ method +

Tiled.object

+
+ Tiled.object(map, layer: int, index: int) -> value +

Returns the object Value at index on object layer — its x/y/width/height, shape, gid, type (class) and properties.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-object_count.html b/tiled-object_count.html new file mode 100644 index 00000000..d64c3ca9 --- /dev/null +++ b/tiled-object_count.html @@ -0,0 +1,28 @@ + + + + + +Tiled.object_count — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.object_count
+
+ method +

Tiled.object_count

+
+ Tiled.object_count(map, layer: int) -> int +

Returns the number of objects on object layer (0 for a non-object layer).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-object_shape.html b/tiled-object_shape.html new file mode 100644 index 00000000..f7ba0a09 --- /dev/null +++ b/tiled-object_shape.html @@ -0,0 +1,28 @@ + + + + + +Tiled.object_shape — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.object_shape
+
+ method +

Tiled.object_shape

+
+ Tiled.object_shape(object) -> string +

Returns an object's shape: tile (a gid object), point, ellipse, polygon, polyline, text, or rectangle (the default).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-project.html b/tiled-project.html new file mode 100644 index 00000000..290a2f25 --- /dev/null +++ b/tiled-project.html @@ -0,0 +1,28 @@ + + + + + +Tiled.project — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.project
+
+ method +

Tiled.project

+
+ Tiled.project(map, layer: int) -> void +

Designates tile layer as the collision layer and projects it onto the legacy byte tilemap: an empty cell is a space (passable), a GID whose tile carries the bool oneway property is '=', any other non-zero GID is '#' (solid). Tiled.load calls this automatically for a layer named collision/solids/walls.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-prop.html b/tiled-prop.html new file mode 100644 index 00000000..5043671d --- /dev/null +++ b/tiled-prop.html @@ -0,0 +1,28 @@ + + + + + +Tiled.prop — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.prop
+
+ method +

Tiled.prop

+
+ Tiled.prop(container, name: string) -> string +

Returns the string value of custom property name on container (an object / tile / layer / map), falling back to the container class's default from the project custom-type table (Tiled.load_types). "" when absent everywhere.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-prop_int.html b/tiled-prop_int.html new file mode 100644 index 00000000..de4884a0 --- /dev/null +++ b/tiled-prop_int.html @@ -0,0 +1,28 @@ + + + + + +Tiled.prop_int — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.prop_int
+
+ method +

Tiled.prop_int

+
+ Tiled.prop_int(container, name: string) -> int +

Like Tiled.prop but parsed as an integer (true/false → 1/0), with the same class-default fallback.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-prop_type.html b/tiled-prop_type.html new file mode 100644 index 00000000..578c9687 --- /dev/null +++ b/tiled-prop_type.html @@ -0,0 +1,28 @@ + + + + + +Tiled.prop_type — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.prop_type
+
+ method +

Tiled.prop_type

+
+ Tiled.prop_type(container, name: string) -> string +

Returns the declared type of custom property name (string/int/float/bool/color/file/object/class/an enum), with the class-default fallback.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-read.html b/tiled-read.html new file mode 100644 index 00000000..25746388 --- /dev/null +++ b/tiled-read.html @@ -0,0 +1,28 @@ + + + + + +Tiled.read — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.read
+
+ method +

Tiled.read

+
+ Tiled.read(path: string) -> value +

Reads a Tiled map file at path — auto-detecting TMX (XML) vs TMJ (JSON) by its first byte — and returns the intermediate map Value tree in Tiled's JSON schema: orientation, width/height, tilewidth/tileheight, a tilesets list and a layers list. Every tile layer's data is decoded to a dense GID int list (CSV split, or base64 → zlib/gzip inflate → little-endian u32s), so the two formats yield structurally identical trees.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-read_tsx.html b/tiled-read_tsx.html new file mode 100644 index 00000000..febf3740 --- /dev/null +++ b/tiled-read_tsx.html @@ -0,0 +1,28 @@ + + + + + +Tiled.read_tsx — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.read_tsx
+
+ method +

Tiled.read_tsx

+
+ Tiled.read_tsx(path: string) -> value +

Reads a standalone Tiled tileset file at path (TSX or TSJ) and returns a tileset Value object: name, tilewidth/tileheight, spacing, margin, columns, tilecount, the image and its size, and a tiles list of per-tile metadata (animation frames, collision shapes, properties). This is what a map's external {firstgid, source} tileset reference resolves to.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-resolve.html b/tiled-resolve.html new file mode 100644 index 00000000..8f6cd2e0 --- /dev/null +++ b/tiled-resolve.html @@ -0,0 +1,28 @@ + + + + + +Tiled.resolve — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.resolve
+
+ method +

Tiled.resolve

+
+ Tiled.resolve(map, gid: int) -> gidinfo +

Decomposes a raw GID: strips the three flip flags (0x80000000 horizontal, 0x40000000 vertical, 0x20000000 anti-diagonal), keeps the low 29 bits as the global tile id, and finds the owning tileset. Returns a value with fields tileset (index, -1 if none), local (id within that tileset), fh/fv/fd (the flip flags) and empty (1 when the GID was 0).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-spawn.html b/tiled-spawn.html new file mode 100644 index 00000000..ffc65612 --- /dev/null +++ b/tiled-spawn.html @@ -0,0 +1,28 @@ + + + + + +Tiled.spawn — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.spawn
+
+ method +

Tiled.spawn

+
+ Tiled.spawn(map, object, model: string) -> int +

Opt-in: spawns an entity of model from an object — its Position (if the model has one) takes the object's x/y, and each object custom property sets a like-named int/bool component field through the reflection ABI. Returns the entity, or -1 if the model is unknown. The core load never calls this.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-spawn_layer.html b/tiled-spawn_layer.html new file mode 100644 index 00000000..1b74b4ed --- /dev/null +++ b/tiled-spawn_layer.html @@ -0,0 +1,28 @@ + + + + + +Tiled.spawn_layer — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.spawn_layer
+
+ method +

Tiled.spawn_layer

+
+ Tiled.spawn_layer(map, layer: int, model: string) -> int +

Spawns every object on object layer as model (see Tiled.spawn); returns how many were spawned.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-template.html b/tiled-template.html new file mode 100644 index 00000000..0d36531a --- /dev/null +++ b/tiled-template.html @@ -0,0 +1,28 @@ + + + + + +Tiled.template — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.template
+
+ method +

Tiled.template

+
+ Tiled.template(path: string) -> value +

Reads a .tx/.tj template file and returns its object Value — the reusable object definition an instance inherits. Tiled.load resolves each object's template reference automatically, merging the template's fields under the instance's overrides.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-tile_prop.html b/tiled-tile_prop.html new file mode 100644 index 00000000..1237ee18 --- /dev/null +++ b/tiled-tile_prop.html @@ -0,0 +1,28 @@ + + + + + +Tiled.tile_prop — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.tile_prop
+
+ method +

Tiled.tile_prop

+
+ Tiled.tile_prop(map, gid: int, name: string) -> int +

Returns 1 if the tile the GID resolves to carries a custom bool property name set to true, else 0 — the property convention (solid/oneway/trigger) the collision projection reads.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-tile_shapes.html b/tiled-tile_shapes.html new file mode 100644 index 00000000..abad9ceb --- /dev/null +++ b/tiled-tile_shapes.html @@ -0,0 +1,28 @@ + + + + + +Tiled.tile_shapes — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.tile_shapes
+
+ method +

Tiled.tile_shapes

+
+ Tiled.tile_shapes(map, gid: int) -> int +

Returns 1 if the tile a GID resolves to carries a per-tile <objectgroup> with at least one collision shape (rectangle/ellipse/polygon/point), else 0 — the precise collision source in the P2 priority order.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-tree.html b/tiled-tree.html new file mode 100644 index 00000000..3363cfd6 --- /dev/null +++ b/tiled-tree.html @@ -0,0 +1,28 @@ + + + + + +Tiled.tree — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.tree
+
+ method +

Tiled.tree

+
+ Tiled.tree(map) -> value +

Returns the intermediate Value tree the map was built from — the full Tiled JSON schema (layers, objects, tilesets, custom properties), for queries the typed accessors don't cover.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-width.html b/tiled-width.html new file mode 100644 index 00000000..37d5a96c --- /dev/null +++ b/tiled-width.html @@ -0,0 +1,28 @@ + + + + + +Tiled.width — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.width
+
+ method +

Tiled.width

+
+ Tiled.width(map) -> int +

Returns the map's width in tiles.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-world.html b/tiled-world.html new file mode 100644 index 00000000..8758cf6f --- /dev/null +++ b/tiled-world.html @@ -0,0 +1,28 @@ + + + + + +Tiled.world — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.world
+
+ method +

Tiled.world

+
+ Tiled.world(path: string) -> value +

Reads a Tiled .world file (JSON) — a set of member maps stitched at world offsets — and returns its Value tree (maps, patterns, onlyShowAdjacentMaps). Reuses the JSON reader.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-world_count.html b/tiled-world_count.html new file mode 100644 index 00000000..b565c10e --- /dev/null +++ b/tiled-world_count.html @@ -0,0 +1,28 @@ + + + + + +Tiled.world_count — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.world_count
+
+ method +

Tiled.world_count

+
+ Tiled.world_count(world) -> int +

Returns the number of member maps in a loaded .world.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/tiled-world_map.html b/tiled-world_map.html new file mode 100644 index 00000000..8671baa2 --- /dev/null +++ b/tiled-world_map.html @@ -0,0 +1,28 @@ + + + + + +Tiled.world_map — Ludic + + + + + + +
+
API Reference › Tiled › Tiled.world_map
+
+ method +

Tiled.world_map

+
+ Tiled.world_map(world, index: int) -> value +

Returns member map index of a world as a Value object — its fileName and world x/y offset — so the caller can load each map and draw it at its place.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/time-delta.html b/time-delta.html new file mode 100644 index 00000000..3fa7cef2 --- /dev/null +++ b/time-delta.html @@ -0,0 +1,32 @@ + + + + + +Time.delta — Ludic + + + + + + +
+
API Reference › Time › Time.delta
+
+ method +

Time.delta

+
+ Time.delta() -> fixed +

Returns the duration of one frame in seconds as a fixed — a constant 1/60, since the frame clock is fixed-step. Multiply a per-second rate by it to advance movement or timers by one frame's worth.

+ +

Example

program Demo {
+  handler Step phase Update {
+    x = x + speed_per_second * Time.delta()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/time-elapsed.html b/time-elapsed.html new file mode 100644 index 00000000..6fdfc620 --- /dev/null +++ b/time-elapsed.html @@ -0,0 +1,32 @@ + + + + + +Time.elapsed — Ludic + + + + + + +
+
API Reference › Time › Time.elapsed
+
+ method +

Time.elapsed

+
+ Time.elapsed() -> fixed +

Returns the time since the program started, in seconds, as a fixed — exactly Time.frame() divided by 60. Deterministic and replay-safe. Use it for wave timers, day/night cycles, and anything paced in seconds.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Time.elapsed() > 10.0 { start_wave() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/time-frame.html b/time-frame.html new file mode 100644 index 00000000..dc7e312b --- /dev/null +++ b/time-frame.html @@ -0,0 +1,32 @@ + + + + + +Time.frame — Ludic + + + + + + +
+
API Reference › Time › Time.frame
+
+ method +

Time.frame

+
+ Time.frame() -> int +

Returns how many frames have completed since the program started, incrementing once per frame. Deterministic — the same replay reaches the same frame count. Use it to time events (if Time.frame() % 30 == 0), drive animations, or seed per-frame variation.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Time.frame() % 60 == 0 { tick() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/time-now.html b/time-now.html new file mode 100644 index 00000000..c61f4d4c --- /dev/null +++ b/time-now.html @@ -0,0 +1,32 @@ + + + + + +Time.now — Ludic + + + + + + +
+
API Reference › Time › Time.now
+
+ method +

Time.now

+
+ Time.now() -> int +

Returns the real-world time in seconds since the Unix epoch. This is the wall clock and is non-deterministic — never feed it into the lockstep simulation or a replay. Use it only for seeding Random.seed or for telemetry.

+ +

Example

program Demo {
+  handler Step phase Update {
+    Random.seed(value: Time.now())
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/time-since.html b/time-since.html new file mode 100644 index 00000000..11a75a2e --- /dev/null +++ b/time-since.html @@ -0,0 +1,34 @@ + + + + + +Time.since — Ludic + + + + + + +
+
API Reference › Time › Time.since
+
+ method +

Time.since

+
+ Time.since(past) -> int +

The number of seconds elapsed from the DateTime past to now (Time.now() - past). Because it reads the wall clock it is non-deterministic — use it for offline-progress and "time away" checks, never inside the lockstep simulation. The result is a Duration in seconds, ready for the Duration.as_* readers.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let last_seen = DateTime.from(2026, 8, 30, 9, 0, 0)
+    let away = Time.since(last_seen)
+    let hours_away = Duration.as_hours(away)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-chain.html b/tween-chain.html new file mode 100644 index 00000000..8325e5dc --- /dev/null +++ b/tween-chain.html @@ -0,0 +1,36 @@ + + + + + +Tween.chain — Ludic + + + + + + +
+
API Reference › Tween › Tween.chain
+
+ method +

Tween.chain

+
+ Tween.chain(handle, to, dur, ease) -> handle +

Appends a tween segment to a handle that runs after its current segments finish, continuing from where the previous one ended — so the game only names the new target. Returns the same handle, so calls chain fluently. A handle holds up to eight segments; combine with Tween.delay for pauses. The engine advances the whole sequence one segment at a time.

+

Parameters

handlethe tween handle to extend
tothe target value for this segment
durthe segment length in engine ticks
ease0 linear, 1 in, 2 out, 3 in-out
+

Example

program Demo {
+  property Marker { n: int = 0 }
+  model M { Marker }
+  entry {
+    var h = Tween.to(0, 100, 20, 2)     # rise
+    h = Tween.chain(h, 0, 20, 1)        # then fall back
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-delay.html b/tween-delay.html new file mode 100644 index 00000000..486935b2 --- /dev/null +++ b/tween-delay.html @@ -0,0 +1,37 @@ + + + + + +Tween.delay — Ludic + + + + + + +
+
API Reference › Tween › Tween.delay
+
+ method +

Tween.delay

+
+ Tween.delay(handle, ticks) -> handle +

Appends a pause of ticks ticks to a handle's sequence: the value holds where the previous segment left it, then the next chained segment begins. Returns the handle for fluent chaining. Use it to stagger a multi-step motion — rise, hold, fall — or to offset one handle from another running in parallel.

+

Parameters

handlethe tween handle to extend
tickshow many engine ticks to hold
+

Example

program Demo {
+  property Marker { n: int = 0 }
+  model M { Marker }
+  entry {
+    var h = Tween.to(0, 50, 15, 0)      # move
+    h = Tween.delay(h, 10)              # wait
+    h = Tween.chain(h, 100, 15, 0)      # move again
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-done.html b/tween-done.html new file mode 100644 index 00000000..86448d71 --- /dev/null +++ b/tween-done.html @@ -0,0 +1,35 @@ + + + + + +Tween.done — Ludic + + + + + + +
+
API Reference › Tween › Tween.done
+
+ method +

Tween.done

+
+ Tween.done(timer, duration) -> bool +

Returns true once timer >= duration — the companion to Tween.progress for ending a one-shot tween: kick off the next step, clear a component, or snap the value to its target.

+

Parameters

timerelapsed seconds (a fixed)
durationthe tween's length in seconds (a fixed)
+

Example

program Demo {
+  property Slide { timer: fixed = 0.0 }
+  model Menu { Slide }
+  handler Run phase Update {
+    Slide.timer = Slide.timer + Time.delta()
+    if Tween.done(Slide.timer, fixed(1)) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-ease.html b/tween-ease.html new file mode 100644 index 00000000..bc6dd9b8 --- /dev/null +++ b/tween-ease.html @@ -0,0 +1,41 @@ + + + + + +Tween.ease — Ludic + + + + + + +
+
API Reference › Tween › Tween.ease
+
+ method +

Tween.ease

+
+ Tween.ease(t, mode) -> fixed +

Shapes a normalized amount t (0.0..1.0) through one of the engine's easing curves and returns the eased fixed. mode must be a literal integer selecting the curve — the same curves as the Ease namespace:

- 0 — linear +- 1 — ease-in (quadratic) +- 2 — ease-out (quadratic) +- 3 — smooth ease-in-out +- 4 — ease-in-back (overshoots below 0) +- 5 — ease-out-elastic (springy settle) +- 6 — ease-out-bounce

+

Parameters

tthe amount to shape (a fixed in 0..1, e.g. from Tween.progress)
modea literal int 0..6 selecting the curve
+

Example

program Demo {
+  handler Run phase Update {
+    let t = Tween.progress(fixed(1), fixed(2))
+    let e = Tween.ease(t, 4)          # ease-in-back
+    let x = Tween.round(0, 200, e)
+    print(x)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-loop.html b/tween-loop.html new file mode 100644 index 00000000..96da6d9a --- /dev/null +++ b/tween-loop.html @@ -0,0 +1,36 @@ + + + + + +Tween.loop — Ludic + + + + + + +
+
API Reference › Tween › Tween.loop
+
+ method +

Tween.loop

+
+ Tween.loop(timer, duration) -> fixed +

Returns the fractional part of timer / duration — a sawtooth that ramps 0.0→1.0 then jumps back to 0.0 and repeats. Use it for a continuously cycling value: a scrolling offset, a rotating hue, a repeating pulse.

+

Parameters

timerelapsed seconds (a fixed)
durationseconds per cycle (a fixed)
+

Example

program Demo {
+  property Spin { timer: fixed = 0.0 }
+  model Coin { Spin }
+  handler Run phase Update {
+    Spin.timer = Spin.timer + Time.delta()
+    let t = Tween.loop(Spin.timer, fixed(2))   # a 2-second cycle
+    print(t)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-number.html b/tween-number.html new file mode 100644 index 00000000..c4124d6a --- /dev/null +++ b/tween-number.html @@ -0,0 +1,34 @@ + + + + + +Tween.number — Ludic + + + + + + +
+
API Reference › Tween › Tween.number
+
+ method +

Tween.number

+
+ Tween.number(from, to, t) -> fixed +

Interpolates two fixed values: from + (to - from) * t. Feed it an eased t from Tween.ease to tween any fractional quantity — an alpha, a scale, a fixed-point position.

+

Parameters

fromthe value at t = 0 (a fixed)
tothe value at t = 1 (a fixed)
tthe blend amount (a fixed, usually 0..1)
+

Example

program Demo {
+  handler Run phase Update {
+    let t = Tween.progress(fixed(1), fixed(2))
+    let scale = Tween.number(fixed(1), fixed(2), Tween.ease(t, 2))
+    print(scale)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-parallel.html b/tween-parallel.html new file mode 100644 index 00000000..12e8a87b --- /dev/null +++ b/tween-parallel.html @@ -0,0 +1,37 @@ + + + + + +Tween.parallel — Ludic + + + + + + +
+
API Reference › Tween › Tween.parallel
+
+ method +

Tween.parallel

+
+ Tween.parallel(a, b) -> bool +

Returns whether two handles are both finished. Independent handles are advanced together every frame, so running several at once already runs them in parallel; this is the "wait for all" query over a pair — start a fade and a slide together, then act when both complete. For longer sequences on a single value, chain segments with Tween.chain instead.

+ +

Example

program Demo {
+  property Marker { n: int = 0 }
+  model M { Marker }
+  entry {
+    let fade  = Tween.to(255, 0, 20, 0)
+    let slide = Tween.to(0, 80, 30, 2)
+    if Tween.parallel(fade, slide) { print(1) }   # both done?
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-point.html b/tween-point.html new file mode 100644 index 00000000..60f4b225 --- /dev/null +++ b/tween-point.html @@ -0,0 +1,36 @@ + + + + + +Tween.point — Ludic + + + + + + +
+
API Reference › Tween › Tween.point
+
+ method +

Tween.point

+
+ Tween.point(from, to, t) -> Vector +

Interpolates two Vector endpoints component-wise by amount t — the 2D version of Tween.number. Move an entity along a straight path, or ease a camera toward a target, in one call.

+

Parameters

fromthe point at t = 0 (a Vector)
tothe point at t = 1 (a Vector)
tthe blend amount (a fixed, usually 0..1)
+

Example

program Demo {
+  handler Run phase Update {
+    let a = Vector.make(fixed(0), fixed(0))
+    let b = Vector.make(fixed(100), fixed(40))
+    let t = Tween.progress(fixed(1), fixed(2))
+    let p = Tween.point(a, b, Tween.ease(t, 3))
+    print(Vector.x(p))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-progress.html b/tween-progress.html new file mode 100644 index 00000000..8215850d --- /dev/null +++ b/tween-progress.html @@ -0,0 +1,37 @@ + + + + + +Tween.progress — Ludic + + + + + + +
+
API Reference › Tween › Tween.progress
+
+ method +

Tween.progress

+
+ Tween.progress(timer, duration) -> fixed +

Returns timer / duration clamped to 0.0..1.0 — the normalized progress of a one-shot tween. Feed it to Tween.ease to shape it, then to a typed blend. Once timer reaches duration it pins at 1.0 (a non-positive duration also reads as done).

+

Parameters

timerelapsed seconds (a fixed)
durationthe tween's length in seconds (a fixed)
+

Example

program Demo {
+  property Fade { timer: fixed = 0.0 }
+  model Panel { Fade }
+  handler Run phase Update {
+    Fade.timer = Fade.timer + Time.delta()
+    let t = Tween.progress(Fade.timer, fixed(1))
+    let a = Tween.round(0, 255, Tween.ease(t, 2))
+    print(a)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-round.html b/tween-round.html new file mode 100644 index 00000000..3e248a57 --- /dev/null +++ b/tween-round.html @@ -0,0 +1,37 @@ + + + + + +Tween.round — Ludic + + + + + + +
+
API Reference › Tween › Tween.round
+
+ method +

Tween.round

+
+ Tween.round(from, to, t) -> int +

Interpolates two int endpoints by amount t and rounds to the nearest integer (half up): from + round((to - from) * t). The go-to for tweening a pixel position, a channel byte, or any integer field. Combine with Tween.ease for shaped motion.

+

Parameters

fromthe value at t = 0 (an int)
tothe value at t = 1 (an int)
tthe blend amount (a fixed, usually 0..1)
+

Example

program Demo {
+  property Slide { timer: fixed = 0.0 }
+  model Card { Slide }
+  handler Run phase Update {
+    Slide.timer = Slide.timer + Time.delta()
+    let t = Tween.progress(Slide.timer, fixed(1))
+    let x = Tween.round(0, 320, Tween.ease(t, 2))
+    print(x)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-stop.html b/tween-stop.html new file mode 100644 index 00000000..fbeb5561 --- /dev/null +++ b/tween-stop.html @@ -0,0 +1,36 @@ + + + + + +Tween.stop — Ludic + + + + + + +
+
API Reference › Tween › Tween.stop
+
+ method +

Tween.stop

+
+ Tween.stop(handle) +

Stops a tween handle immediately and frees its slot back to the pool. Its value is frozen where it was, and Tween.done then reports it finished. Use it to cancel a motion early — an interrupted animation, an entity that despawned mid-tween — so long-lived games recycle handles instead of exhausting the pool.

+

Parameters

handlethe tween handle to stop
+

Example

program Demo {
+  property Marker { n: int = 0 }
+  model M { Marker }
+  entry {
+    let h = Tween.to(0, 100, 60, 0)
+    Tween.stop(h)                       # cancel it now
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-tint.html b/tween-tint.html new file mode 100644 index 00000000..817d5073 --- /dev/null +++ b/tween-tint.html @@ -0,0 +1,34 @@ + + + + + +Tween.tint — Ludic + + + + + + +
+
API Reference › Tween › Tween.tint
+
+ method +

Tween.tint

+
+ Tween.tint(from, to, t) -> int +

Blends two colors (0x00RRGGBB ints) channel by channel by amount t — the color equivalent of Tween.number. Fade a sprite to white on hit, cross-fade a sky, or pulse a UI accent. Matches Color.lerp, ready for an eased t from Tween.ease.

+

Parameters

fromthe color at t = 0 (an int, 0x00RRGGBB)
tothe color at t = 1 (an int, 0x00RRGGBB)
tthe blend amount (a fixed, usually 0..1)
+

Example

program Demo {
+  handler Run phase Update {
+    let t = Tween.progress(fixed(1), fixed(2))
+    let c = Tween.tint(Color.rgb(0, 0, 0), Color.rgb(255, 255, 255), t)
+    print(c)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-to.html b/tween-to.html new file mode 100644 index 00000000..3f33fc50 --- /dev/null +++ b/tween-to.html @@ -0,0 +1,39 @@ + + + + + +Tween.to — Ludic + + + + + + +
+
API Reference › Tween › Tween.to
+
+ method +

Tween.to

+
+ Tween.to(from, to, dur, ease) -> handle +

Starts a stateful tween handle — from from to to over dur ticks with easing ease — and returns an integer handle. Unlike the pure Tween.progress interpolators (which the game drives from its own timer), a handle is advanced by the engine one tick per frame: fire it once, then read Tween.value each frame and Tween.done to know when it finishes. Tween.chain and Tween.delay append segments for a sequence. Integer and deterministic, so a sequence plays identically under replay. The handle pool is fixed-size and reuses finished slots.

+

Parameters

durthe segment length in engine ticks
ease0 linear, 1 in, 2 out, 3 in-out
+

Example

program Demo {
+  property SpriteAnim { ticks: int = 0, fps: int = 0, frames: int = 0, mode: int = 0, frame: int = 0 }
+  property Pop { h: int = 0 }
+  model Coin { SpriteAnim, Pop }
+  entry {
+    spawn Coin { Pop { h: 0 } }
+    let e = World.query_next(World.prop_id("Pop"), 0)
+    let f = World.field_id(World.prop_id("Pop"), "h")
+    World.set(e, World.prop_id("Pop"), f, Tween.to(0, 40, 20, 2))   # pop upward
+    quit()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-value.html b/tween-value.html new file mode 100644 index 00000000..05fb957b --- /dev/null +++ b/tween-value.html @@ -0,0 +1,39 @@ + + + + + +Tween.value — Ludic + + + + + + +
+
API Reference › Tween › Tween.value
+
+ method +

Tween.value

+
+ Tween.value(handle) -> int +

Returns the current interpolated value of a tween handle this frame — the number the engine advanced it to. Read it each frame to drive a position, an alpha, a scale. Once every segment has finished the value rests on the final target; Tween.done reports when that has happened. Returns 0 for an invalid handle.

+

Parameters

handlethe tween handle to read
+

Example

program Demo {
+  property Pos { x: int = 0 }
+  property Tw { h: int = 0 }
+  model Mover { Pos, Tw }
+  handler Move phase Update {
+    let p = World.prop_id("Pos")
+    let t = World.prop_id("Tw")
+    let e = World.query_next(t, 0)
+    let h = World.get(e, t, World.field_id(t, "h"))
+    World.set(e, p, World.field_id(p, "x"), Tween.value(h))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/tween-yoyo.html b/tween-yoyo.html new file mode 100644 index 00000000..ef20effe --- /dev/null +++ b/tween-yoyo.html @@ -0,0 +1,37 @@ + + + + + +Tween.yoyo — Ludic + + + + + + +
+
API Reference › Tween › Tween.yoyo
+
+ method +

Tween.yoyo

+
+ Tween.yoyo(timer, duration) -> fixed +

Returns a triangle wave: timer / duration ramps 0.0→1.0 over the first duration, then 1.0→0.0 over the next, and repeats. The smooth back-and-forth for a hover bob, a pulsing highlight, or a breathing scale — no snap at the ends the way Tween.loop has.

+

Parameters

timerelapsed seconds (a fixed)
durationseconds for each leg (a fixed)
+

Example

program Demo {
+  property Bob { timer: fixed = 0.0 }
+  model Pickup { Bob }
+  handler Run phase Update {
+    Bob.timer = Bob.timer + Time.delta()
+    let t = Tween.yoyo(Bob.timer, fixed(1))
+    let y = Tween.round(100, 108, Tween.ease(t, 3))
+    print(y)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-bool.html b/type-bool.html new file mode 100644 index 00000000..ef4401dc --- /dev/null +++ b/type-bool.html @@ -0,0 +1,30 @@ + + + + + +bool — Ludic + + + + + + +
+
API Reference › Types › bool
+
+ type +

bool

+
+ bool +

bool is a truth value, either true or false. It is what a comparison (== != < <= > >=) yields and what the boolean combinators and, or, and not produce and consume. A bool is what an if or while condition tests, and some builtins return one directly — Random.chance(percent:) answers true or false. Ludic spells the combinators as words, not symbols: write and / or / not, never &&, ||, or a bare !.

+ +

Example

let is_alive: bool = current_health > 0
+let should_spawn: bool = Random.chance(percent: 25)
+if is_alive and should_spawn { spawn Enemy { } }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-byte.html b/type-byte.html new file mode 100644 index 00000000..038f89ef --- /dev/null +++ b/type-byte.html @@ -0,0 +1,31 @@ + + + + + +byte — Ludic + + + + + + +
+
API Reference › Types › byte
+
+ type +

byte

+
+ byte +

byte is a raw buffer viewed one byte at a time: indexing buffer[index] reads or writes a single 8-bit value. It is the byte-sized view of the memory a pointer addresses — allocate it with bytes(count) and bind it as byte when you want per-byte access, for example to build up text, decode a file, or pack a compact grid. Each element is an int in the range of a byte. Like the other raw buffers it is unchecked, so you are responsible for staying in bounds.

+ +

Example

let name_buffer: byte = bytes(16)
+name_buffer[0] = 'A'
+name_buffer[1] = 'd'
+name_buffer[2] = 'a'
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-countdown.html b/type-countdown.html new file mode 100644 index 00000000..81d17863 --- /dev/null +++ b/type-countdown.html @@ -0,0 +1,32 @@ + + + + + +countdown — Ludic + + + + + + +
+
API Reference › Types › countdown
+
+ type +

countdown

+
+ countdown +

countdown is an int for a component field that the engine counts down: once per Update, for every live entity carrying the component, a countdown field above 0 loses one (it never goes below 0). It is the timer idiom — roll frames, invulnerability frames, a hit flash, a cooldown — as a type: set the field, then test it, and no handler decrements it. Everywhere else it is an ordinary int.

+ +

Example

# doc-check: skip — illustrative
+property Roll { frames_left: countdown = 0, cooldown: countdown = 0 }
+
+Roll.of(player).cooldown = 35                 # 35 frames later it reads 0
+if Roll.of(player).cooldown == 0 { start_roll() }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-double.html b/type-double.html new file mode 100644 index 00000000..34ac7f21 --- /dev/null +++ b/type-double.html @@ -0,0 +1,35 @@ + + + + + +double — Ludic + + + + + + +
+
API Reference › Types › double
+
+ type +

double

+
+ double +

A double is an IEEE-754 double-precision number: about sixteen significant digits. Use it where a float loses precision — large world coordinates, accumulated time, geodesy — and convert to float at the boundary to the GPU.

It behaves like float: decimal literals take its type from context, int and long promote to it, and a float mixed with a double widens to double. Narrowing back is explicit: float(d).

+ +

Example

program Precise {
+  entry {
+    let total: double = 0.1
+    print(total + 0.2)                 # 0.30000000000000004
+    let small: float = float(total)
+    print(small)                       # 0.1
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-entity.html b/type-entity.html new file mode 100644 index 00000000..cd82322d --- /dev/null +++ b/type-entity.html @@ -0,0 +1,31 @@ + + + + + +entity — Ludic + + + + + + +
+
API Reference › Types › entity
+
+ type +

entity

+
+ entity +

An entity is the id — an integer handle — of a spawned model instance. When you spawn a model, the running thing is a model instance, and its entity is the handle you use to refer to it: self() yields the entity of the instance the current query loop is visiting, and statements like despawn, attach, detach, enable, and disable take an entity to say which instance to act on. You rarely do arithmetic on it; think of it as an opaque reference into the ECS rather than a number. It is stored as an int under the hood and is part of the saved snapshot.

+ +

Example

for (current_position) in query [Position, {Enemy}] {
+  let this_enemy: entity = self()
+  if current_position.row > 14 { despawn this_enemy }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-fixed.html b/type-fixed.html new file mode 100644 index 00000000..7d9efd2c --- /dev/null +++ b/type-fixed.html @@ -0,0 +1,31 @@ + + + + + +fixed — Ludic + + + + + + +
+
API Reference › Types › fixed
+
+ type +

fixed

+
+ fixed +

fixed is Q16.16 fixed-point: a fractional number stored in 32 bits, giving you decimals without floating-point. Ludic uses it precisely because it is deterministic — the same computation gives the same bits on every machine, which is what a reproducible simulation and lockstep networking need. A literal with a decimal point (1.5) is a fixed; arithmetic on two fixed values does fixed-point multiply/divide, and mixing an int with a fixed promotes the int. Lift an int in with fixed(value) and take the floor back out with floor(value).

+ +

Example

const GRAVITY: fixed = 0.5
+var velocity_y: fixed = fixed(0)
+var next_velocity: fixed = velocity_y + GRAVITY
+var pixel_row: int = floor(next_velocity)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-fixeds.html b/type-fixeds.html new file mode 100644 index 00000000..ea3db2c9 --- /dev/null +++ b/type-fixeds.html @@ -0,0 +1,35 @@ + + + + + +fixeds — Ludic + + + + + + +
+
API Reference › Types › fixeds
+
+ type +

fixeds

+
+ fixeds +

A fixeds is a raw buffer whose elements are fixed (Q16.16) values: index it with buffer[i] to read or write a fixed directly, with no bounds checking. You obtain one by allocating raw storage with words(n) — each slot is 32 bits — and typing the binding as fixeds so that indexing uses the fixed-point element size. Reach for it when you want a tight, fixed-size numeric array with deterministic math — a table of speeds, per-tile weights, a heightfield — and a growable []fixed slice would be more than you need.

+ +

Example

program Heights {
+  handler Setup phase Start {
+    let heights: fixeds = words(4)
+    heights[0] = fixed(2)
+    heights[1] = heights[0] + fixed(1)
+    print(floor(heights[1]))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-float.html b/type-float.html new file mode 100644 index 00000000..ea756f3c --- /dev/null +++ b/type-float.html @@ -0,0 +1,38 @@ + + + + + +float — Ludic + + + + + + +
+
API Reference › Types › float
+
+ type +

float

+
+ float +

A float is an IEEE-754 single-precision number: about seven significant digits, a huge range, and ordinary operators (+ - * / %, comparisons). It is the type for rendering and physics that runs on the GPU or talks to it — positions, normals, colours in shaders, matrices — and for any math where fixed's ±32768 range is too small.

A decimal literal takes its type from where it is used: let s: float = 0.5, x * 0.5 with a float x, and a float parameter all read 0.5 exactly as a float, while an untyped let k = 0.5 stays fixed. An int promotes to float automatically; everything else converts explicitly with float(x), int(x), fixed(x). Math.* accepts floats and answers in float.

Floats are not deterministic across machines the way fixed is, so simulation that must replay bit-for-bit stays in fixed.

+ +

Example

program Orbit {
+  function radius(x: float, y: float) -> float { return Math.sqrt(x * x + y * y) }
+
+  entry {
+    let speed: float = 1.5
+    var angle: float = 0.0
+    angle = angle + speed * 2          # the int 2 promotes
+    print(radius(3, 4))                # 5.0
+    print(Math.sin(angle) < 1.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-floats.html b/type-floats.html new file mode 100644 index 00000000..628007bc --- /dev/null +++ b/type-floats.html @@ -0,0 +1,36 @@ + + + + + +floats — Ludic + + + + + + +
+
API Reference › Types › floats
+
+ type +

floats

+
+ floats / doubles +

floats(n) allocates room for n float values and returns a floats buffer; doubles(n) does the same for double. Index it with v[i] to read or write an element, with no bounds checking. The memory is exactly what a GPU vertex or uniform buffer expects, so a floats buffer uploads as it is.

+ +

Example

program Vertices {
+  entry {
+    let v = floats(6)
+    v[0] = 0.0;  v[1] = 0.5
+    v[2] = -0.5; v[3] = -0.5
+    v[4] = 0.5;  v[5] = -0.5
+    print(v[1] - v[3])                 # 1.0
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-int.html b/type-int.html new file mode 100644 index 00000000..35213954 --- /dev/null +++ b/type-int.html @@ -0,0 +1,31 @@ + + + + + +int — Ludic + + + + + + +
+
API Reference › Types › int
+
+ type +

int

+
+ int +

int is a 32-bit signed integer and the default numeric type in Ludic: a literal like 42 or a hex literal like 0x1E90FF is an int. It does far more than counting — colors are int hex values, the key from Input.key() is an int character code, and a tile from Map.tile is an int. Arithmetic (+ - * / %), comparison, and the bitwise operators all work on it, and it is the natural type for a score, a column, or an elapsed_frames counter. When you need fractions, reach for fixed and convert with fx / flr.

+ +

Example

var score: int = 0
+const GRID_WIDTH: int = 20
+var next_column: int = Random.range(low: 0, high: GRID_WIDTH - 1)
+var next_score: int = score + 1
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-ivec2.html b/type-ivec2.html new file mode 100644 index 00000000..a0455675 --- /dev/null +++ b/type-ivec2.html @@ -0,0 +1,33 @@ + + + + + +IVec2 — Ludic + + + + + + +
+
API Reference › Types › IVec2
+
+ type +

IVec2

+
+ IVec2 +

IVec2 is an integer 2D vector: two whole-number int components, x and y, packed into a single value that is copied by value and never heap-allocates. It is the natural type for tile and grid coordinates, cell offsets, and integer sizes — anywhere a fractional part would be meaningless. Build one with IVec2.make(x, y) (or IVec2.zero()), read the parts with IVec2.x / IVec2.y, and combine them with the IVec2.* math — add, sub, scale, dot, and the grid-distance IVec2.manhattan. Every operation is exact integer arithmetic, so results are bit-identical on every platform. Widen to a sub-pixel Vector with IVec2.to_vector when you need fractional math.

+ +

Example

program Demo {
+  handler Step phase Update {
+    var cell: IVec2 = IVec2.make(4, 7)
+    cell = IVec2.add(cell, IVec2.make(1, 0))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-long.html b/type-long.html new file mode 100644 index 00000000..8abe35a0 --- /dev/null +++ b/type-long.html @@ -0,0 +1,31 @@ + + + + + +long — Ludic + + + + + + +
+
API Reference › Types › long
+
+ type +

long

+
+ long +

long is a 64-bit signed integer — reach for it when a value would overflow the 32-bit int: a 64-bit hash or content id, an accumulated playtime in milliseconds, a large score or currency total, packed bit flags. Arithmetic (+ - * / %), comparison, and the bitwise operators all work on it, and mixing an int with a long promotes the int to 64 bits automatically — so big * count where big is a long computes in 64 bits and does not overflow. print and string render all its digits.

One limit to know: a numeric *literal* is still parsed as a 32-bit int, so build large values by widening — assign a smaller literal into a long and compute from there (let m: long = 1000000 then m * m) rather than writing a 10-digit literal directly.

+ +

Example

let big: long = 1000000
+var area: long = big * big        # 10^12, computed in 64 bits
+var total: long = area + 1
+var doubled: long = area * 2
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-pointer.html b/type-pointer.html new file mode 100644 index 00000000..033ed993 --- /dev/null +++ b/type-pointer.html @@ -0,0 +1,30 @@ + + + + + +pointer — Ludic + + + + + + +
+
API Reference › Types › pointer
+
+ type +

pointer

+
+ pointer +

pointer is a raw address into memory — the low-level type for runtime and foreign-function work, not something an everyday game reaches for. Allocate a raw byte buffer with bytes(count), which returns a pointer you index as buffer[index] to read or write one byte; retype the binding as words / fixeds / pointers to index in larger element sizes. A pointer is also how an extern function passes an opaque C handle across the ABI. Test one for emptiness against the null literal.

+ +

Example

let scratch: pointer = bytes(256)
+scratch[0] = 65
+if scratch != null { scratch[1] = 66 }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-pointers.html b/type-pointers.html new file mode 100644 index 00000000..84b315f5 --- /dev/null +++ b/type-pointers.html @@ -0,0 +1,37 @@ + + + + + +pointers — Ludic + + + + + + +
+
API Reference › Types › pointers
+
+ type +

pointers

+
+ pointers +

A pointers is a raw buffer whose elements are pointers: index it with buffer[i] to read or write a pointer, with no bounds checking. Allocate the storage with words(n) (a pointer fits in a slot) and type the binding as pointers so indexing uses the pointer element size. It is the low-level building block for hand-rolled data structures — a table of records made with new, a free list, a bucket array — for when you want manual control instead of a growable []T slice.

+ +

Example

program Records {
+  property Position { column: int = 0, row: int = 0 }
+
+  handler Setup phase Start {
+    let slots: pointers = words(2)
+    slots[0] = new Position
+    slots[1] = new Position
+    print(2)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-rect.html b/type-rect.html new file mode 100644 index 00000000..219e30c1 --- /dev/null +++ b/type-rect.html @@ -0,0 +1,33 @@ + + + + + +Rect — Ludic + + + + + + +
+
API Reference › Types › Rect
+
+ type +

Rect

+
+ Rect +

Rect is an axis-aligned rectangle: four Q16.16 fixed components — the top-left corner (x, y) and the size (w, h) — packed into a single value that is copied by value and never heap-allocates. It is the natural type for HUD layout boxes, hitboxes, and camera or viewport regions. Build one with Rect.make(x, y, w, h), read the parts with Rect.x / Rect.y / Rect.w / Rect.h (or the derived Rect.right / Rect.bottom / Rect.center), and test against it with Rect.contains for a point and Rect.intersects for overlap. Every operation is deterministic fixed-point, so results are bit-identical on every platform.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let button: Rect = Rect.make(8.0, 8.0, 96.0, 16.0)
+    if Rect.contains(button, mx, my) { press() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-slices.html b/type-slices.html new file mode 100644 index 00000000..eefae39a --- /dev/null +++ b/type-slices.html @@ -0,0 +1,30 @@ + + + + + +[]T (slices) — Ludic + + + + + + +
+
API Reference › Types › []T (slices)
+
+ type +

[]T (slices)

+
+ []T +

[]T is a growable slice of T — a pointer to a header that holds the data, its length, and its capacity. Create one with new []T, append with push(slice, value) (the storage doubles when it is full), count elements with len(slice), and read or write an element with slice[index]. Because the header never moves, an append is visible to everything holding that slice, giving it reference semantics. Slices are the everyday dynamic collection for records and other values outside the ECS.

+ +

Example

let route: []Waypoint = new []Waypoint
+push(route, new Waypoint)
+for step in 0 .. len(route) { Screen.put_pixel(x: route[step].column, y: route[step].row, color: Color.Gold) }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-string.html b/type-string.html new file mode 100644 index 00000000..11bf2613 --- /dev/null +++ b/type-string.html @@ -0,0 +1,30 @@ + + + + + +string — Ludic + + + + + + +
+
API Reference › Types › string
+
+ type +

string

+
+ string +

string is an immutable string of bytes. Strings are true values: + concatenates two of them and == / != compare them by content, not by pointer, so "go" + direction == "goleft" behaves as written. Index a single byte with text[index] (an int code point), take a fresh substring with text[start..end], and measure the byte length with len(text). The readable way to build one is backtick interpolation — ` score: {score} — which stringifies each {…}` hole and concatenates.

+ +

Example

let player_name: string = "Ada"
+let greeting: string = `hello {player_name}, score {score}`
+if player_name == "Ada" { Screen.status(greeting) }
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-vector.html b/type-vector.html new file mode 100644 index 00000000..d59e0af6 --- /dev/null +++ b/type-vector.html @@ -0,0 +1,33 @@ + + + + + +Vector — Ludic + + + + + + +
+
API Reference › Types › Vector
+
+ type +

Vector

+
+ Vector +

Vector is a 2D vector: two Q16.16 fixed components, x and y, packed into a single value that is copied by value and never heap-allocates. It is the natural type for positions, velocities, directions, and offsets. Build one with Vector.make(x, y) (or Vector.zero()), read the parts with Vector.x / Vector.y, and combine them with the Vector.* math — add, scale, dot, length, normalize, rotate, and the rest. Every operation is deterministic fixed-point, so results are bit-identical on every platform.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let velocity: Vector = Vector.scale(Vector.from_angle(heading), speed)
+    position = Vector.add(position, velocity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-void.html b/type-void.html new file mode 100644 index 00000000..fbcc72d2 --- /dev/null +++ b/type-void.html @@ -0,0 +1,31 @@ + + + + + +void — Ludic + + + + + + +
+
API Reference › Types › void
+
+ type +

void

+
+ void +

void is the absence of a value. It appears as the return type of a function that runs for its effect and hands nothing back — function reset_score() -> void { … }. Such a function is called as a statement, not used in an expression, and a bare return (with no value) leaves it early. Use void whenever a helper mutates program state, draws, or spawns rather than computing a result to return.

+ +

Example

function reset_score() -> void {
+  score = 0
+  return
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/type-words.html b/type-words.html new file mode 100644 index 00000000..e2ecb90c --- /dev/null +++ b/type-words.html @@ -0,0 +1,30 @@ + + + + + +words — Ludic + + + + + + +
+
API Reference › Types › words
+
+ type +

words

+
+ words +

words is a raw buffer viewed as a sequence of 32-bit words: indexing buffer[index] reads or writes one int. It is the same underlying memory a pointer addresses, retyped so the element size is a word instead of a byte — allocate the storage with words(count) (count 32-bit words) and bind it as words to index it that way. Reach for it when you need a flat integer array outside the ECS — a lookup table, a scratch grid — and want plain integer indexing without the growable-slice header. Like all raw buffers it is unbounded and unchecked, so keep your own length.

+ +

Example

let height_map: words = words(64)
+height_map[0] = 10
+height_map[1] = height_map[0] + 5
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-close.html b/udp-close.html new file mode 100644 index 00000000..68d78123 --- /dev/null +++ b/udp-close.html @@ -0,0 +1,33 @@ + + + + + +Udp.close — Ludic + + + + + + +
+
API Reference › Udp › Udp.close
+
+ method +

Udp.close

+
+ Udp.close(socket) -> void +

Closes the socket and frees its handle.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    Udp.close(socket: s)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-from_ip.html b/udp-from_ip.html new file mode 100644 index 00000000..fb2d2b4a --- /dev/null +++ b/udp-from_ip.html @@ -0,0 +1,33 @@ + + + + + +Udp.from_ip — Ludic + + + + + + +
+
API Reference › Udp › Udp.from_ip
+
+ method +

Udp.from_ip

+
+ Udp.from_ip(socket) -> int +

The address the last datagram read on this socket came from - the one to answer.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    print(Udp.from_ip(socket: s))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-from_port.html b/udp-from_port.html new file mode 100644 index 00000000..e9b0be2a --- /dev/null +++ b/udp-from_port.html @@ -0,0 +1,33 @@ + + + + + +Udp.from_port — Ludic + + + + + + +
+
API Reference › Udp › Udp.from_port
+
+ method +

Udp.from_port

+
+ Udp.from_port(socket) -> int +

The port the last datagram read on this socket came from.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    print(Udp.from_port(socket: s))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-ip.html b/udp-ip.html new file mode 100644 index 00000000..d87a1830 --- /dev/null +++ b/udp-ip.html @@ -0,0 +1,32 @@ + + + + + +Udp.ip — Ludic + + + + + + +
+
API Reference › Udp › Udp.ip
+
+ method +

Udp.ip

+
+ Udp.ip(text) -> int +

Turns "a.b.c.d" into an address, or 0 when the text is not one.

+ +

Example

program Demo {
+  entry {
+    print(Udp.ip(text: "10.0.0.2"))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-ip_text.html b/udp-ip_text.html new file mode 100644 index 00000000..90a3e65a --- /dev/null +++ b/udp-ip_text.html @@ -0,0 +1,32 @@ + + + + + +Udp.ip_text — Ludic + + + + + + +
+
API Reference › Udp › Udp.ip_text
+
+ method +

Udp.ip_text

+
+ Udp.ip_text(ip) -> string +

Turns an address back into "a.b.c.d".

+ +

Example

program Demo {
+  entry {
+    print(Udp.ip_text(ip: Udp.ip(text: "192.168.1.11")))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-local_ip.html b/udp-local_ip.html new file mode 100644 index 00000000..2fdbe94c --- /dev/null +++ b/udp-local_ip.html @@ -0,0 +1,32 @@ + + + + + +Udp.local_ip — Ludic + + + + + + +
+
API Reference › Udp › Udp.local_ip
+
+ method +

Udp.local_ip

+
+ Udp.local_ip() -> int +

The address this machine reaches the internet from - the one a player on the same network would dial. No packet is sent; 0 when there is no route.

+ +

Example

program Demo {
+  entry {
+    print(Udp.ip_text(ip: Udp.local_ip()))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-open.html b/udp-open.html new file mode 100644 index 00000000..ad327b2a --- /dev/null +++ b/udp-open.html @@ -0,0 +1,33 @@ + + + + + +Udp.open — Ludic + + + + + + +
+
API Reference › Udp › Udp.open
+
+ method +

Udp.open

+
+ Udp.open(port) -> int +

Opens a non-blocking IPv4 socket bound to port on every interface and returns its handle, or 0 when it could not. Port 0 lets the system pick a free one; Udp.port says which.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    print(s > 0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-port.html b/udp-port.html new file mode 100644 index 00000000..2f7e310f --- /dev/null +++ b/udp-port.html @@ -0,0 +1,33 @@ + + + + + +Udp.port — Ludic + + + + + + +
+
API Reference › Udp › Udp.port
+
+ method +

Udp.port

+
+ Udp.port(socket) -> int +

Returns the port the socket is bound to - the one the system chose when it was opened on port 0.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    print(Udp.port(socket: s) > 0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-recv.html b/udp-recv.html new file mode 100644 index 00000000..c553d50c --- /dev/null +++ b/udp-recv.html @@ -0,0 +1,34 @@ + + + + + +Udp.recv — Ludic + + + + + + +
+
API Reference › Udp › Udp.recv
+
+ method +

Udp.recv

+
+ Udp.recv(socket, bytes, cap) -> int +

Copies the next waiting datagram, up to cap bytes, into bytes and returns its length, or 0 when nothing is waiting. It never blocks: call it in a loop each frame until it returns 0. The sender is Udp.from_ip / from_port.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    let buf = Memory.bytes(64)
+    print(Udp.recv(socket: s, bytes: buf, cap: 64))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-resolve.html b/udp-resolve.html new file mode 100644 index 00000000..fbd0fb9e --- /dev/null +++ b/udp-resolve.html @@ -0,0 +1,32 @@ + + + + + +Udp.resolve — Ludic + + + + + + +
+
API Reference › Udp › Udp.resolve
+
+ method +

Udp.resolve

+
+ Udp.resolve(name) -> int +

Returns the first IPv4 address of a host name, or 0. It waits for the system's resolver, so call it outside the frame loop.

+ +

Example

program Demo {
+  entry {
+    print(Udp.ip_text(ip: Udp.resolve(name: "localhost")))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/udp-send.html b/udp-send.html new file mode 100644 index 00000000..8c82158b --- /dev/null +++ b/udp-send.html @@ -0,0 +1,35 @@ + + + + + +Udp.send — Ludic + + + + + + +
+
API Reference › Udp › Udp.send
+
+ method +

Udp.send

+
+ Udp.send(socket, ip, port, bytes, len) -> int +

Sends the first len bytes of bytes to ip:port as one datagram and returns the number of bytes sent, or -1.

+ +

Example

program Demo {
+  entry {
+    let s = Udp.open(port: 0)
+    let buf = Memory.bytes(4)
+    Memory.poke(buf, 0, 1)
+    print(Udp.send(socket: s, ip: Udp.ip(text: "127.0.0.1"), port: Udp.port(socket: s), bytes: buf, len: 1))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-build.html b/ui-build.html new file mode 100644 index 00000000..20c184b7 --- /dev/null +++ b/ui-build.html @@ -0,0 +1,31 @@ + + + + + +Ui.build — Ludic + + + + + + +
+
API Reference › Ui › Ui.build
+
+ method +

Ui.build

+
+ Ui.build() +

Builds the widget trees of every ui block. Call it once, after the fonts and images the blocks reference are loaded — a loading scene's last step is the usual place.

+ +

Example

# doc-check: skip — illustrative
+handler Boot phase Start {
+  Ui.build()
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-clicked.html b/ui-clicked.html new file mode 100644 index 00000000..c66ea50f --- /dev/null +++ b/ui-clicked.html @@ -0,0 +1,31 @@ + + + + + +Ui.clicked — Ludic + + + + + + +
+
API Reference › Ui › Ui.clicked
+
+ method +

Ui.clicked

+
+ Ui.clicked(id: UI_Name) -> bool +

True on the frame a focused control with that id was activated (Enter, Space, or pad A). The event form is @On(UiClicked) handler … { if id == UI_Name { … } }; either can become another scene.

+ +

Example

# doc-check: skip — illustrative
+handler Menu phase Update {
+  if Ui.clicked(id: UI_Play) { become Play }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-close.html b/ui-close.html new file mode 100644 index 00000000..0aaa57c4 --- /dev/null +++ b/ui-close.html @@ -0,0 +1,31 @@ + + + + + +Ui.close — Ludic + + + + + + +
+
API Reference › Ui › Ui.close
+
+ method +

Ui.close

+
+ Ui.close() +

Closes whatever menu is active, so nothing is drawn by Ui.render and no click can fire. A play scene opens with it so a menu left over from the title never lingers.

+ +

Example

# doc-check: skip — illustrative
+scene Play {
+  on enter { Ui.close() }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-open.html b/ui-open.html new file mode 100644 index 00000000..b5226e0f --- /dev/null +++ b/ui-open.html @@ -0,0 +1,31 @@ + + + + + +Ui.open — Ludic + + + + + + +
+
API Reference › Ui › Ui.open
+
+ method +

Ui.open

+
+ Ui.open(id: UI_Name) +

Activates the menu whose root has that id and moves keyboard focus to its first focusable control. Only one menu is active at a time; opening another replaces it.

+ +

Example

# doc-check: skip — illustrative
+scene Title {
+  on enter { Ui.open(id: UI_TitleMenu) }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-render.html b/ui-render.html new file mode 100644 index 00000000..d364b3df --- /dev/null +++ b/ui-render.html @@ -0,0 +1,31 @@ + + + + + +Ui.render — Ludic + + + + + + +
+
API Reference › Ui › Ui.render
+
+ method +

Ui.render

+
+ Ui.render() +

Draws the active menu at its laid-out position. Call it from a handler in the Overlay phase so the menu is painted over the engine-drawn world; it draws nothing while no menu is open.

+ +

Example

# doc-check: skip — illustrative
+layer Menu {
+  handler Draw phase Overlay { Ui.render() }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-set_text.html b/ui-set_text.html new file mode 100644 index 00000000..c2ddbae2 --- /dev/null +++ b/ui-set_text.html @@ -0,0 +1,29 @@ + + + + + +Ui.set_text — Ludic + + + + + + +
+
API Reference › Ui › Ui.set_text
+
+ method +

Ui.set_text

+
+ Ui.set_text(id: UI_Name, text: s) +

Sets the text of the label or button with that id; the menu re-lays itself out on its next tick. Interpolated strings make run stats one line.

+ +

Example

# doc-check: skip — illustrative
+Ui.set_text(id: UI_Stats, text: `floor {run.floor}  kills {run.kills}`)
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/ui-tick.html b/ui-tick.html new file mode 100644 index 00000000..779ff96f --- /dev/null +++ b/ui-tick.html @@ -0,0 +1,31 @@ + + + + + +Ui.tick — Ludic + + + + + + +
+
API Reference › Ui › Ui.tick
+
+ method +

Ui.tick

+
+ Ui.tick(key: k) +

Moves focus and activates controls from one key code. The frame loop calls it every frame with the frame key, so a game normally never calls it; it remains for programs that drive their own loop from entry.

+ +

Example

# doc-check: skip — illustrative
+entry {
+  Ui.tick(key: Input.key())
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-byte_len.html b/unicode-byte_len.html new file mode 100644 index 00000000..39e9f50a --- /dev/null +++ b/unicode-byte_len.html @@ -0,0 +1,32 @@ + + + + + +Unicode.byte_len — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.byte_len
+
+ method +

Unicode.byte_len

+
+ Unicode.byte_len(s) -> int +

The number of bytes the string occupies — its storage size. This is what raw byte APIs and buffers care about, and it is greater than len whenever the text contains non-ASCII code points (each of which encodes to 2–4 bytes).

+

Parameters

sthe UTF-8 string
+

Example

program ByteLen {
+  entry {
+    print(Unicode.byte_len("hello"))   # 5 (all ASCII)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-char_at.html b/unicode-char_at.html new file mode 100644 index 00000000..f9360cb9 --- /dev/null +++ b/unicode-char_at.html @@ -0,0 +1,32 @@ + + + + + +Unicode.char_at — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.char_at
+
+ method +

Unicode.char_at

+
+ Unicode.char_at(s, i) -> int +

Returns the code point at code-point index i as an integer (a Unicode scalar value), or -1 when i is out of range. Indexing is by code point, not byte, so it never lands in the middle of a multibyte character.

+

Parameters

sthe UTF-8 string
ithe code-point index (0-based)
+

Example

program CharAt {
+  entry {
+    print(Unicode.char_at("hello", 0))   # 104 = 'h'
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-chars.html b/unicode-chars.html new file mode 100644 index 00000000..7db3b952 --- /dev/null +++ b/unicode-chars.html @@ -0,0 +1,34 @@ + + + + + +Unicode.chars — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.chars
+
+ method +

Unicode.chars

+
+ Unicode.chars(s) -> []int +

Decodes the whole string into a []int of code points, in order — the iteration primitive for laying out glyphs, scanning text, or transforming character by character without byte-fiddling.

+

Parameters

sthe UTF-8 string
+

Example

program Chars {
+  entry {
+    let cs = Unicode.chars("hi")
+    var i = 0
+    while i < len(cs) { print(cs[i]); i = i + 1 }   # 104, 105
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-grapheme_len.html b/unicode-grapheme_len.html new file mode 100644 index 00000000..ee9400a9 --- /dev/null +++ b/unicode-grapheme_len.html @@ -0,0 +1,32 @@ + + + + + +Unicode.grapheme_len — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.grapheme_len
+
+ method +

Unicode.grapheme_len

+
+ Unicode.grapheme_len(s) -> int +

Counts grapheme clusters — what a reader perceives as one character — which is what you want for cursor movement and visual width. A base letter plus a combining accent, a ZWJ emoji sequence (like a family emoji), and a two–regional-indicator flag each count as one, even though they span several code points. This v1 approximates UAX#29 over those common cases; full segmentation is a follow-up.

+

Parameters

sthe UTF-8 string
+

Example

program GraphemeLen {
+  entry {
+    print(Unicode.grapheme_len("hello"))   # 5 (plain ASCII)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-is_valid_utf8.html b/unicode-is_valid_utf8.html new file mode 100644 index 00000000..922ba55a --- /dev/null +++ b/unicode-is_valid_utf8.html @@ -0,0 +1,32 @@ + + + + + +Unicode.is_valid_utf8 — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.is_valid_utf8
+
+ method +

Unicode.is_valid_utf8

+
+ Unicode.is_valid_utf8(s) -> bool +

Strictly validates that the bytes are well-formed UTF-8: correct continuation bytes, no overlong encodings, no surrogate code points, and nothing above U+10FFFF. Use it as a gate on untrusted input — a chat line, a downloaded file, a mod's data — before treating it as text.

+

Parameters

sthe bytes to validate
+

Example

program Valid {
+  entry {
+    if Unicode.is_valid_utf8("safe text") { print(1) } else { print(0) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-len.html b/unicode-len.html new file mode 100644 index 00000000..24dc8c52 --- /dev/null +++ b/unicode-len.html @@ -0,0 +1,32 @@ + + + + + +Unicode.len — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.len
+
+ method +

Unicode.len

+
+ Unicode.len(s) -> int +

Counts the code points in a UTF-8 string — the correct "length" for most text logic, unlike a raw byte count which over-counts anything non-ASCII. Contrast byte_len (storage size) and grapheme_len (user-perceived characters).

+

Parameters

sthe UTF-8 string
+

Example

program Len {
+  entry {
+    print(Unicode.len("hello"))   # 5
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-lower.html b/unicode-lower.html new file mode 100644 index 00000000..43a1ef22 --- /dev/null +++ b/unicode-lower.html @@ -0,0 +1,32 @@ + + + + + +Unicode.lower — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.lower
+
+ method +

Unicode.lower

+
+ Unicode.lower(s) -> string +

Returns a new string with each letter lowercased. v1 maps ASCII and Latin-1 letters; other scripts and locale-specific rules are follow-ups, and unmapped code points pass through unchanged. Useful for case-insensitive comparison of names and commands.

+

Parameters

sthe UTF-8 string
+

Example

program Lower {
+  entry {
+    if Unicode.lower("HELLO") == "hello" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-truncate.html b/unicode-truncate.html new file mode 100644 index 00000000..c02ab60b --- /dev/null +++ b/unicode-truncate.html @@ -0,0 +1,32 @@ + + + + + +Unicode.truncate — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.truncate
+
+ method +

Unicode.truncate

+
+ Unicode.truncate(s, n) -> string +

Returns the first n code points as a new string, never splitting a multibyte character — the safe way to cap a display name or a chat line to a length. If the string has fewer than n code points it is returned whole; n <= 0 yields the empty string.

+

Parameters

sthe UTF-8 string
nthe maximum number of code points to keep
+

Example

program Truncate {
+  entry {
+    if Unicode.truncate("hello", 3) == "hel" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/unicode-upper.html b/unicode-upper.html new file mode 100644 index 00000000..484b8476 --- /dev/null +++ b/unicode-upper.html @@ -0,0 +1,32 @@ + + + + + +Unicode.upper — Ludic + + + + + + +
+
API Reference › Unicode › Unicode.upper
+
+ method +

Unicode.upper

+
+ Unicode.upper(s) -> string +

Returns a new string with each letter uppercased. v1 maps ASCII and Latin-1 letters (correct for Western-European text); other scripts and locale-specific rules are follow-ups, and unmapped code points pass through unchanged.

+

Parameters

sthe UTF-8 string
+

Example

program Upper {
+  entry {
+    if Unicode.upper("hello") == "HELLO" { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-equals.html b/uuid-equals.html new file mode 100644 index 00000000..11b6bb56 --- /dev/null +++ b/uuid-equals.html @@ -0,0 +1,34 @@ + + + + + +Uuid.equals — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.equals
+
+ method +

Uuid.equals

+
+ Uuid.equals(a, b) -> bool +

Compares two UUIDs for equality, ignoring case. UUIDs generated by this library are always lowercase, so a plain == works between them — but a UUID that arrived from another system may be upper- or mixed-case, and equals matches it correctly without you having to normalise first. Values of different length are never equal.

+ +

Example

program Eq {
+  entry {
+    let lo = "550e8400-e29b-41d4-a716-446655440000"
+    let up = "550E8400-E29B-41D4-A716-446655440000"
+    if Uuid.equals(lo, up) { print(1) }   # case-insensitive
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-is_valid.html b/uuid-is_valid.html new file mode 100644 index 00000000..b7cacdb6 --- /dev/null +++ b/uuid-is_valid.html @@ -0,0 +1,33 @@ + + + + + +Uuid.is_valid — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.is_valid
+
+ method +

Uuid.is_valid

+
+ Uuid.is_valid(s) -> bool +

Reports whether s is a well-formed UUID: exactly 36 characters, hyphens at positions 8, 13, 18 and 23, and hexadecimal digits (either case) everywhere else. This is the guard to run on any UUID that came from outside your program — a save file, a network message, a mod, a command-line flag — before you trust it as an identity.

It checks *shape*, not version: both v4 and v7 IDs (and any other conforming UUID) pass. To also fold a malformed value into a safe default in one step, use Uuid.parse instead.

+

Parameters

sthe string to check
+

Example

program Valid {
+  entry {
+    if Uuid.is_valid("550e8400-e29b-41d4-a716-446655440000") { print(1) }
+    if not Uuid.is_valid("not-a-uuid") { print(2) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-new.html b/uuid-new.html new file mode 100644 index 00000000..da320204 --- /dev/null +++ b/uuid-new.html @@ -0,0 +1,33 @@ + + + + + +Uuid.new — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.new
+
+ method +

Uuid.new

+
+ Uuid.new() -> string +

Generates a new **v4** (random) UUID and returns it in canonical lowercase text form, e.g. 550e8400-e29b-41d4-a716-446655440000. A v4 UUID carries 122 bits of entropy drawn from the OS secure random generator, so two independently-generated IDs colliding is not something you will ever observe in practice — which is exactly what makes it a good identity for a player, a session, a networked entity, or a piece of user-created content that has no central authority to number it.

This is the default UUID constructor; Uuid.v4 is an explicit alias. Because the value is random it is **non-deterministic** — do not mint UUIDs inside the lockstep simulation, or replays and peers will diverge.

+ +

Example

program NewId {
+  entry {
+    let id = Uuid.new()
+    print(len(id))   # 36
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-new_v7.html b/uuid-new_v7.html new file mode 100644 index 00000000..3ef349bb --- /dev/null +++ b/uuid-new_v7.html @@ -0,0 +1,33 @@ + + + + + +Uuid.new_v7 — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.new_v7
+
+ method +

Uuid.new_v7

+
+ Uuid.new_v7() -> string +

Generates a new **v7** (time-ordered) UUID. Its leading 48 bits are a Unix-millisecond timestamp, so a batch of v7 IDs sorts lexicographically by creation time — which keeps database indexes and append-only logs tidy in a way random v4 IDs do not. The remaining bits are secure random, so IDs minted in the same millisecond are still distinct, and the RFC 4122 version (7) and variant bits are set.

Sub-second resolution is derived from the wall clock in seconds (multiplied to milliseconds), so ordering is guaranteed at one-second granularity with the random tail breaking ties within a second. Like v4 this reads the non-deterministic wall clock and CSPRNG — generate at the edges, never inside reproducible simulation. Uuid.v7 is an alias.

+ +

Example

program NewV7 {
+  entry {
+    let id = Uuid.new_v7()
+    print(id[14..15])   # 7 — the version nibble
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-nil.html b/uuid-nil.html new file mode 100644 index 00000000..23b8b9e0 --- /dev/null +++ b/uuid-nil.html @@ -0,0 +1,33 @@ + + + + + +Uuid.nil — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.nil
+
+ method +

Uuid.nil

+
+ Uuid.nil() -> string +

Returns the nil UUID — 00000000-0000-0000-0000-000000000000 — the reserved all-zero value that means "no UUID". Use it as a sentinel for an unset or absent identity, and as the value Uuid.parse returns when it is handed something that is not a valid UUID. It is a well-formed UUID string, so it passes Uuid.is_valid; test for "no id" by comparing against Uuid.nil() explicitly.

+ +

Example

program Nil {
+  entry {
+    let none = Uuid.nil()
+    print(none)   # 00000000-0000-0000-0000-000000000000
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-parse.html b/uuid-parse.html new file mode 100644 index 00000000..7778e851 --- /dev/null +++ b/uuid-parse.html @@ -0,0 +1,34 @@ + + + + + +Uuid.parse — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.parse
+
+ method +

Uuid.parse

+
+ Uuid.parse(s) -> string +

Normalises an untrusted string: if s is a well-formed UUID it returns the same value in canonical lowercase form; if it is not, it returns the nil UUID. This never faults on garbage, so it is safe to call on data that arrived from a file, a network peer, or a mod. When you need to *reject* bad input rather than fold it to nil, gate on Uuid.is_valid first.

Parsing accepts either case and yields the lowercase canonical form, which is what the rest of the library produces — so a parsed ID compares equal (with ==) to a freshly generated one of the same value.

+

Parameters

sthe string to validate and normalise
+

Example

program Parse {
+  entry {
+    let id = Uuid.parse("550E8400-E29B-41D4-A716-446655440000")
+    print(id[0..8])                          # 550e8400 — lowercased
+    if Uuid.parse("nope") == Uuid.nil() { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-to_text.html b/uuid-to_text.html new file mode 100644 index 00000000..4d15afa4 --- /dev/null +++ b/uuid-to_text.html @@ -0,0 +1,33 @@ + + + + + +Uuid.to_text — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.to_text
+
+ method +

Uuid.to_text

+
+ Uuid.to_text(id) -> string +

Returns the canonical 36-character text form of id. Because this library already represents every UUID as its canonical lowercase text, to_text is the identity function — it exists so intent is explicit at the point where a UUID is turned into a string for display, storage, or a wire payload, and so code stays correct if the internal representation ever changes to a packed 128-bit value.

+

Parameters

idthe UUID to render
+

Example

program ToText {
+  entry {
+    let id = Uuid.new()
+    print(len(Uuid.to_text(id)))   # 36
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-v4.html b/uuid-v4.html new file mode 100644 index 00000000..0ae61421 --- /dev/null +++ b/uuid-v4.html @@ -0,0 +1,34 @@ + + + + + +Uuid.v4 — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.v4
+
+ method +

Uuid.v4

+
+ Uuid.v4() -> string +

An explicit alias for Uuid.new: generates a **v4** (random) UUID in canonical lowercase text form. Use this spelling when the surrounding code also uses Uuid.v7 and naming both by version reads more clearly than new versus new_v7. The behaviour, entropy, and non-determinism caveat are identical to Uuid.new.

+ +

Example

program V4 {
+  entry {
+    let a = Uuid.v4()
+    let b = Uuid.v4()
+    if a != b { print(1) }   # two draws differ
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/uuid-v7.html b/uuid-v7.html new file mode 100644 index 00000000..798b2ebf --- /dev/null +++ b/uuid-v7.html @@ -0,0 +1,33 @@ + + + + + +Uuid.v7 — Ludic + + + + + + +
+
API Reference › Uuid › Uuid.v7
+
+ method +

Uuid.v7

+
+ Uuid.v7() -> string +

An explicit alias for Uuid.new_v7: generates a **v7** (time-ordered) UUID whose leading bits are a millisecond timestamp so the IDs sort by creation time. Use this spelling to sit symmetrically beside Uuid.v4. Behaviour, timestamp resolution, and the determinism caveat are identical to Uuid.new_v7.

+ +

Example

program V7 {
+  entry {
+    let id = Uuid.v7()
+    if Uuid.is_valid(id) { print(1) }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-add.html b/value-add.html new file mode 100644 index 00000000..e06d8055 --- /dev/null +++ b/value-add.html @@ -0,0 +1,28 @@ + + + + + +Value.add — Ludic + + + + + + +
+
API Reference › Value › Value.add
+
+ method +

Value.add

+
+ Value.add(list, item) -> value +

Appends item to the list node and returns the list, so calls chain. Order is preserved.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-as_int.html b/value-as_int.html new file mode 100644 index 00000000..c6953bdd --- /dev/null +++ b/value-as_int.html @@ -0,0 +1,28 @@ + + + + + +Value.as_int — Ludic + + + + + + +
+
API Reference › Value › Value.as_int
+
+ method +

Value.as_int

+
+ Value.as_int(value) -> int +

Returns the raw scalar an int/fixed/bool node carries (a fixed comes back as its Q16.16 bits). Non-scalar nodes return 0.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-as_str.html b/value-as_str.html new file mode 100644 index 00000000..e030d115 --- /dev/null +++ b/value-as_str.html @@ -0,0 +1,28 @@ + + + + + +Value.as_str — Ludic + + + + + + +
+
API Reference › Value › Value.as_str
+
+ method +

Value.as_str

+
+ Value.as_str(value) -> string +

Returns the text a str node holds, or an empty string for any other kind.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-at.html b/value-at.html new file mode 100644 index 00000000..056c17e9 --- /dev/null +++ b/value-at.html @@ -0,0 +1,28 @@ + + + + + +Value.at — Ludic + + + + + + +
+
API Reference › Value › Value.at
+
+ method +

Value.at

+
+ Value.at(list, index: int) -> value +

Returns the item at index in a list (or object) node, or a null node if the index is out of range.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-bool.html b/value-bool.html new file mode 100644 index 00000000..d0604419 --- /dev/null +++ b/value-bool.html @@ -0,0 +1,28 @@ + + + + + +Value.bool — Ludic + + + + + + +
+
API Reference › Value › Value.bool
+
+ method +

Value.bool

+
+ Value.bool(b: bool) -> value +

Builds a bool node (kind 3). It encodes to JSON true/false. Read it with Value.as_int (0 or 1).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-count.html b/value-count.html new file mode 100644 index 00000000..76315cc9 --- /dev/null +++ b/value-count.html @@ -0,0 +1,28 @@ + + + + + +Value.count — Ludic + + + + + + +
+
API Reference › Value › Value.count
+
+ method +

Value.count

+
+ Value.count(value) -> int +

Returns how many items a list holds, or how many members an object holds. Zero for a scalar node.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-fixed.html b/value-fixed.html new file mode 100644 index 00000000..686596cf --- /dev/null +++ b/value-fixed.html @@ -0,0 +1,28 @@ + + + + + +Value.fixed — Ludic + + + + + + +
+
API Reference › Value › Value.fixed
+
+ method +

Value.fixed

+
+ Value.fixed(f: fixed) -> value +

Builds a fixed node (kind 2) carrying the raw Q16.16 value f. Json.encode writes it as an exact decimal (e.g. 0.5) that Json.parse reads back bit-for-bit.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-get.html b/value-get.html new file mode 100644 index 00000000..d5a972b7 --- /dev/null +++ b/value-get.html @@ -0,0 +1,28 @@ + + + + + +Value.get — Ludic + + + + + + +
+
API Reference › Value › Value.get
+
+ method +

Value.get

+
+ Value.get(obj, key: string) -> value +

Returns the value stored under key in the obj node, or a null node if there is no such member.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-has.html b/value-has.html new file mode 100644 index 00000000..239d200d --- /dev/null +++ b/value-has.html @@ -0,0 +1,28 @@ + + + + + +Value.has — Ludic + + + + + + +
+
API Reference › Value › Value.has
+
+ method +

Value.has

+
+ Value.has(obj, key: string) -> bool +

Returns whether the obj node has a member named key — the way to tell an explicit null from an absent key.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-int.html b/value-int.html new file mode 100644 index 00000000..7b03902c --- /dev/null +++ b/value-int.html @@ -0,0 +1,28 @@ + + + + + +Value.int — Ludic + + + + + + +
+
API Reference › Value › Value.int
+
+ method +

Value.int

+
+ Value.int(n: int) -> value +

Builds an int node (kind 1) carrying n. Read it back with Value.as_int. This is the node Reflect.serialize uses for every non-fixed field.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-key_at.html b/value-key_at.html new file mode 100644 index 00000000..3c3f17bb --- /dev/null +++ b/value-key_at.html @@ -0,0 +1,28 @@ + + + + + +Value.key_at — Ludic + + + + + + +
+
API Reference › Value › Value.key_at
+
+ method +

Value.key_at

+
+ Value.key_at(obj, index: int) -> string +

Returns the key of the member at position index in an object node — pair it with Value.at and Value.count to walk every member in order.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-kind.html b/value-kind.html new file mode 100644 index 00000000..6873acb8 --- /dev/null +++ b/value-kind.html @@ -0,0 +1,28 @@ + + + + + +Value.kind — Ludic + + + + + + +
+
API Reference › Value › Value.kind
+
+ method +

Value.kind

+
+ Value.kind(value) -> int +

Returns the node's kind: 0 null, 1 int, 2 fixed, 3 bool, 4 str, 5 list, 6 object — so a generic reader can branch before unwrapping.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-list.html b/value-list.html new file mode 100644 index 00000000..6c710d60 --- /dev/null +++ b/value-list.html @@ -0,0 +1,28 @@ + + + + + +Value.list — Ludic + + + + + + +
+
API Reference › Value › Value.list
+
+ method +

Value.list

+
+ Value.list() -> value +

Builds an empty list node (kind 5). Append items with Value.add, read them with Value.at and Value.count.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-null.html b/value-null.html new file mode 100644 index 00000000..450e1426 --- /dev/null +++ b/value-null.html @@ -0,0 +1,28 @@ + + + + + +Value.null — Ludic + + + + + + +
+
API Reference › Value › Value.null
+
+ method +

Value.null

+
+ Value.null() -> value +

Builds a null node — the absent value. Value.get and Value.at return one for a missing key or out-of-range index, so a reader never faults on absent data. Its Value.kind is 0.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-object.html b/value-object.html new file mode 100644 index 00000000..7029a6ff --- /dev/null +++ b/value-object.html @@ -0,0 +1,28 @@ + + + + + +Value.object — Ludic + + + + + + +
+
API Reference › Value › Value.object
+
+ method +

Value.object

+
+ Value.object() -> value +

Builds an empty object node (kind 6) — an ordered set of key/value members. Set members with Value.put; read them with Value.get, Value.key_at, and Value.count. This is the shape Reflect.serialize produces.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-put.html b/value-put.html new file mode 100644 index 00000000..4635d605 --- /dev/null +++ b/value-put.html @@ -0,0 +1,28 @@ + + + + + +Value.put — Ludic + + + + + + +
+
API Reference › Value › Value.put
+
+ method +

Value.put

+
+ Value.put(obj, key: string, item) -> value +

Sets member key of the obj node to item (replacing an existing member with the same key) and returns the object. Insertion order is preserved for a stable Json.encode.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/value-str.html b/value-str.html new file mode 100644 index 00000000..616a80d0 --- /dev/null +++ b/value-str.html @@ -0,0 +1,28 @@ + + + + + +Value.str — Ludic + + + + + + +
+
API Reference › Value › Value.str
+
+ method +

Value.str

+
+ Value.str(s: string) -> value +

Builds a str node (kind 4) holding s. Json.encode quotes and escapes it; read it back with Value.as_str.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-add.html b/vector-add.html new file mode 100644 index 00000000..aef56dd1 --- /dev/null +++ b/vector-add.html @@ -0,0 +1,32 @@ + + + + + +Vector.add — Ludic + + + + + + +
+
API Reference › Vector › Vector.add
+
+ method +

Vector.add

+
+ Vector.add(a, b) -> Vector +

Returns the component-wise sum a + b — the workhorse for stepping a position by a velocity each frame.

+ +

Example

program Demo {
+  handler Step phase Update {
+    pos = Vector.add(pos, velocity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-angle.html b/vector-angle.html new file mode 100644 index 00000000..69d26179 --- /dev/null +++ b/vector-angle.html @@ -0,0 +1,32 @@ + + + + + +Vector.angle — Ludic + + + + + + +
+
API Reference › Vector › Vector.angle
+
+ method +

Vector.angle

+
+ Vector.angle(v) -> fixed +

Returns the angle of v in radians, measured from +x, in the range -pi..pi (via atan2). Feed it back into Vector.from_angle or Vector.rotate.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let heading = Vector.angle(velocity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-distance.html b/vector-distance.html new file mode 100644 index 00000000..e1cfb593 --- /dev/null +++ b/vector-distance.html @@ -0,0 +1,32 @@ + + + + + +Vector.distance — Ludic + + + + + + +
+
API Reference › Vector › Vector.distance
+
+ method +

Vector.distance

+
+ Vector.distance(a, b) -> fixed +

Returns the distance between points a and b — Vector.length(Vector.sub(a, b)). Handy for range checks and proximity triggers.

+ +

Example

program Demo {
+  handler Step phase Update {
+    if Vector.distance(pos, target) < 8.0 { arrived() }
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-dot.html b/vector-dot.html new file mode 100644 index 00000000..86ef4989 --- /dev/null +++ b/vector-dot.html @@ -0,0 +1,32 @@ + + + + + +Vector.dot — Ludic + + + + + + +
+
API Reference › Vector › Vector.dot
+
+ method +

Vector.dot

+
+ Vector.dot(a, b) -> fixed +

Returns the dot product ax*bx + ay*by as a fixed. Zero when the vectors are perpendicular; use it to project one vector onto another or to test facing.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let facing = Vector.dot(forward, toTarget)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-from_angle.html b/vector-from_angle.html new file mode 100644 index 00000000..7281fe7e --- /dev/null +++ b/vector-from_angle.html @@ -0,0 +1,32 @@ + + + + + +Vector.from_angle — Ludic + + + + + + +
+
API Reference › Vector › Vector.from_angle
+
+ method +

Vector.from_angle

+
+ Vector.from_angle(a) -> Vector +

Returns the unit vector (cos a, sin a) pointing at a radians. Scale it by a speed to launch a projectile in a chosen direction.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let shot = Vector.scale(Vector.from_angle(aim), 200.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-length.html b/vector-length.html new file mode 100644 index 00000000..ef5449d6 --- /dev/null +++ b/vector-length.html @@ -0,0 +1,32 @@ + + + + + +Vector.length — Ludic + + + + + + +
+
API Reference › Vector › Vector.length
+
+ method +

Vector.length

+
+ Vector.length(v) -> fixed +

Returns the Euclidean length of v, computed with the deterministic fixed-point square root. When you only need to compare magnitudes, comparing Vector.dot(v, v) avoids the square root.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let speed = Vector.length(velocity)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-lerp.html b/vector-lerp.html new file mode 100644 index 00000000..89ffaad8 --- /dev/null +++ b/vector-lerp.html @@ -0,0 +1,32 @@ + + + + + +Vector.lerp — Ludic + + + + + + +
+
API Reference › Vector › Vector.lerp
+
+ method +

Vector.lerp

+
+ Vector.lerp(a, b, t) -> Vector +

Returns the component-wise linear interpolation between a and b by t (a fixed in 0.0..1.0). Great for smoothing a camera toward a target.

+ +

Example

program Demo {
+  handler Step phase Update {
+    camera = Vector.lerp(camera, target, 0.1)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-make.html b/vector-make.html new file mode 100644 index 00000000..aff8b08d --- /dev/null +++ b/vector-make.html @@ -0,0 +1,32 @@ + + + + + +Vector.make — Ludic + + + + + + +
+
API Reference › Vector › Vector.make
+
+ method +

Vector.make

+
+ Vector.make(x, y) -> Vector +

Builds a Vector from its x and y components (both fixed). This is the usual way to make a position or a velocity; read the parts back with Vector.x and Vector.y.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let pos = Vector.make(64.0, 32.0)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-normalize.html b/vector-normalize.html new file mode 100644 index 00000000..370f36d2 --- /dev/null +++ b/vector-normalize.html @@ -0,0 +1,32 @@ + + + + + +Vector.normalize — Ludic + + + + + + +
+
API Reference › Vector › Vector.normalize
+
+ method +

Vector.normalize

+
+ Vector.normalize(v) -> Vector +

Returns a unit-length vector pointing the same way as v. The zero vector maps to itself, so this never divides by zero. Scale the result by a speed to build a velocity.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let dir = Vector.normalize(Vector.sub(target, pos))
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-rotate.html b/vector-rotate.html new file mode 100644 index 00000000..39ab76a1 --- /dev/null +++ b/vector-rotate.html @@ -0,0 +1,32 @@ + + + + + +Vector.rotate — Ludic + + + + + + +
+
API Reference › Vector › Vector.rotate
+
+ method +

Vector.rotate

+
+ Vector.rotate(v, angle) -> Vector +

Returns v rotated counter-clockwise by angle radians, using the deterministic fixed-point sine table. Positive angles turn toward +y.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let aimed = Vector.rotate(forward, spin)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-scale.html b/vector-scale.html new file mode 100644 index 00000000..b61f538f --- /dev/null +++ b/vector-scale.html @@ -0,0 +1,32 @@ + + + + + +Vector.scale — Ludic + + + + + + +
+
API Reference › Vector › Vector.scale
+
+ method +

Vector.scale

+
+ Vector.scale(v, s) -> Vector +

Returns v scaled by the fixed factor s. Multiply a direction by a speed, or a velocity by delta-time.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let step = Vector.scale(direction, speed)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-sub.html b/vector-sub.html new file mode 100644 index 00000000..104c630f --- /dev/null +++ b/vector-sub.html @@ -0,0 +1,32 @@ + + + + + +Vector.sub — Ludic + + + + + + +
+
API Reference › Vector › Vector.sub
+
+ method +

Vector.sub

+
+ Vector.sub(a, b) -> Vector +

Returns the component-wise difference a - b — the vector that points from b to a. Pair it with Vector.length or Vector.normalize to get a distance or a direction.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let toTarget = Vector.sub(target, pos)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-x.html b/vector-x.html new file mode 100644 index 00000000..95e2a468 --- /dev/null +++ b/vector-x.html @@ -0,0 +1,32 @@ + + + + + +Vector.x — Ludic + + + + + + +
+
API Reference › Vector › Vector.x
+
+ method +

Vector.x

+
+ Vector.x(v) -> fixed +

Returns the x component of v as a fixed. The companion of Vector.y.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let px = Vector.x(pos)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-y.html b/vector-y.html new file mode 100644 index 00000000..a22bee80 --- /dev/null +++ b/vector-y.html @@ -0,0 +1,32 @@ + + + + + +Vector.y — Ludic + + + + + + +
+
API Reference › Vector › Vector.y
+
+ method +

Vector.y

+
+ Vector.y(v) -> fixed +

Returns the y component of v as a fixed. The companion of Vector.x.

+ +

Example

program Demo {
+  handler Step phase Update {
+    let py = Vector.y(pos)
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/vector-zero.html b/vector-zero.html new file mode 100644 index 00000000..b19730b9 --- /dev/null +++ b/vector-zero.html @@ -0,0 +1,32 @@ + + + + + +Vector.zero — Ludic + + + + + + +
+
API Reference › Vector › Vector.zero
+
+ method +

Vector.zero

+
+ Vector.zero() -> Vector +

Returns the zero vector (0, 0) — a convenient identity for accumulating a sum of vectors or clearing a velocity.

+ +

Example

program Demo {
+  handler Step phase Update {
+    var velocity = Vector.zero()
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-attach.html b/world-attach.html new file mode 100644 index 00000000..5a1f7fe7 --- /dev/null +++ b/world-attach.html @@ -0,0 +1,28 @@ + + + + + +World.attach — Ludic + + + + + + +
+
API Reference › World › World.attach
+
+ method +

World.attach

+
+ World.attach(entity, prop_id) -> void +

Adds component prop_id to entity, so World.get/set/has then work on it.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-count.html b/world-count.html new file mode 100644 index 00000000..3bb03c2f --- /dev/null +++ b/world-count.html @@ -0,0 +1,28 @@ + + + + + +World.count — Ludic + + + + + + +
+
API Reference › World › World.count
+
+ method +

World.count

+
+ World.count() -> int +

Returns how many entities currently exist in the world.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-despawn.html b/world-despawn.html new file mode 100644 index 00000000..76bfe6e4 --- /dev/null +++ b/world-despawn.html @@ -0,0 +1,38 @@ + + + + + +World.despawn — Ludic + + + + + + +
+
API Reference › World › World.despawn
+
+ method +

World.despawn

+
+ World.despawn(entity) +

Despawns an entity by id: it runs the entity's model @OnDespawn hook (with reason Despawned), sweeps any entity-scoped event listeners, and frees the slot back to the pool for reuse — the reflective, by-id form of the despawn statement. Use it from a system that has an entity id in hand (the engine world-bounds kill policy uses it, #84) or wherever you would write despawn e but only have the id.

+ +

Example

program Demo {
+  property Mob { hp: int = 0 }
+  model M { Mob }
+  entry {
+    spawn M { Mob { hp: 1 } }
+    let pm = World.prop_id("Mob")
+    let e = World.query_next(pm, 0)
+    World.despawn(e)
+    print(World.query_next(pm, 0))     # -1 — none left
+  }
+}
+ + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-detach.html b/world-detach.html new file mode 100644 index 00000000..81c4e81d --- /dev/null +++ b/world-detach.html @@ -0,0 +1,28 @@ + + + + + +World.detach — Ludic + + + + + + +
+
API Reference › World › World.detach
+
+ method +

World.detach

+
+ World.detach(entity, prop_id) -> void +

Removes component prop_id from entity — the inverse of World.attach.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-field_id.html b/world-field_id.html new file mode 100644 index 00000000..487d50c7 --- /dev/null +++ b/world-field_id.html @@ -0,0 +1,28 @@ + + + + + +World.field_id — Ludic + + + + + + +
+
API Reference › World › World.field_id
+
+ method +

World.field_id

+
+ World.field_id(prop_id, name) -> int +

Returns the numeric id of field name within component prop_id.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-get.html b/world-get.html new file mode 100644 index 00000000..bb91a620 --- /dev/null +++ b/world-get.html @@ -0,0 +1,28 @@ + + + + + +World.get — Ludic + + + + + + +
+
API Reference › World › World.get
+
+ method +

World.get

+
+ World.get(prop_id, field_id, entity) -> int +

Reads the value of field field_id of component prop_id on entity, by numeric id rather than by name — the reflective form the bare world_get provides.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-has.html b/world-has.html new file mode 100644 index 00000000..38ecc445 --- /dev/null +++ b/world-has.html @@ -0,0 +1,28 @@ + + + + + +World.has — Ludic + + + + + + +
+
API Reference › World › World.has
+
+ method +

World.has

+
+ World.has(entity, prop_id) -> int +

Returns non-zero when entity carries the component prop_id. Use it before World.get on a dynamically-attached component.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-kind.html b/world-kind.html new file mode 100644 index 00000000..b9f48b56 --- /dev/null +++ b/world-kind.html @@ -0,0 +1,28 @@ + + + + + +World.kind — Ludic + + + + + + +
+
API Reference › World › World.kind
+
+ method +

World.kind

+
+ World.kind(entity) -> int +

Returns the model id that entity was spawned as.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-load.html b/world-load.html new file mode 100644 index 00000000..eaedd952 --- /dev/null +++ b/world-load.html @@ -0,0 +1,28 @@ + + + + + +World.load — Ludic + + + + + + +
+
API Reference › World › World.load
+
+ method +

World.load

+
+ World.load(buf, len) -> void +

Replaces the current world with the snapshot in the first len bytes of buf — the inverse of World.save.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-model_id.html b/world-model_id.html new file mode 100644 index 00000000..5499fcf9 --- /dev/null +++ b/world-model_id.html @@ -0,0 +1,28 @@ + + + + + +World.model_id — Ludic + + + + + + +
+
API Reference › World › World.model_id
+
+ method +

World.model_id

+
+ World.model_id(name) -> int +

Returns the numeric id of the model named name, for passing to World.spawn.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-prop_id.html b/world-prop_id.html new file mode 100644 index 00000000..bf8bb273 --- /dev/null +++ b/world-prop_id.html @@ -0,0 +1,28 @@ + + + + + +World.prop_id — Ludic + + + + + + +
+
API Reference › World › World.prop_id
+
+ method +

World.prop_id

+
+ World.prop_id(name) -> int +

Returns the numeric id of the component named name, for use with the by-id reflection calls.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-query_next.html b/world-query_next.html new file mode 100644 index 00000000..46f140ee --- /dev/null +++ b/world-query_next.html @@ -0,0 +1,28 @@ + + + + + +World.query_next — Ludic + + + + + + +
+
API Reference › World › World.query_next
+
+ method +

World.query_next

+
+ World.query_next(query_id, cursor) -> int +

Returns the next entity matching query query_id after cursor, or a terminal value when the query is exhausted.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-register_prop.html b/world-register_prop.html new file mode 100644 index 00000000..c42e3dca --- /dev/null +++ b/world-register_prop.html @@ -0,0 +1,28 @@ + + + + + +World.register_prop — Ludic + + + + + + +
+
API Reference › World › World.register_prop
+
+ method +

World.register_prop

+
+ World.register_prop(name, size) -> int +

Declares a brand-new component named name holding size integer fields and returns its id — a component the game's source never wrote, for mods.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-save.html b/world-save.html new file mode 100644 index 00000000..9326a030 --- /dev/null +++ b/world-save.html @@ -0,0 +1,28 @@ + + + + + +World.save — Ludic + + + + + + +
+
API Reference › World › World.save
+
+ method +

World.save

+
+ World.save(buf) -> int +

Writes a binary snapshot of the whole ECS world into buf and returns the number of bytes written. Size the buffer with World.size.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-set.html b/world-set.html new file mode 100644 index 00000000..254985b9 --- /dev/null +++ b/world-set.html @@ -0,0 +1,28 @@ + + + + + +World.set — Ludic + + + + + + +
+
API Reference › World › World.set
+
+ method +

World.set

+
+ World.set(prop_id, field_id, entity, value) -> void +

Writes value into field field_id of component prop_id on entity. The reflective counterpart to World.get.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-size.html b/world-size.html new file mode 100644 index 00000000..6e37f23e --- /dev/null +++ b/world-size.html @@ -0,0 +1,28 @@ + + + + + +World.size — Ludic + + + + + + +
+
API Reference › World › World.size
+
+ method +

World.size

+
+ World.size() -> int +

Returns the number of bytes a full World.save snapshot of the current world would take, for sizing a buffer.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/world-spawn.html b/world-spawn.html new file mode 100644 index 00000000..d6af636c --- /dev/null +++ b/world-spawn.html @@ -0,0 +1,28 @@ + + + + + +World.spawn — Ludic + + + + + + +
+
API Reference › World › World.spawn
+
+ method +

World.spawn

+
+ World.spawn(model_id) -> entity +

Creates a new entity of model model_id and returns it — the reflective form of the spawn statement.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-attr.html b/xml-attr.html new file mode 100644 index 00000000..b0c9cea9 --- /dev/null +++ b/xml-attr.html @@ -0,0 +1,28 @@ + + + + + +Xml.attr — Ludic + + + + + + +
+
API Reference › Xml › Xml.attr
+
+ method +

Xml.attr

+
+ Xml.attr(node, key: string) -> string +

Returns the value of attribute key (entity-decoded), or "" if the element has no such attribute. Use Xml.has to distinguish an empty value from an absent one.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-attr_count.html b/xml-attr_count.html new file mode 100644 index 00000000..21801f28 --- /dev/null +++ b/xml-attr_count.html @@ -0,0 +1,28 @@ + + + + + +Xml.attr_count — Ludic + + + + + + +
+
API Reference › Xml › Xml.attr_count
+
+ method +

Xml.attr_count

+
+ Xml.attr_count(node) -> int +

Returns the number of attributes on node.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-attr_int.html b/xml-attr_int.html new file mode 100644 index 00000000..122f4750 --- /dev/null +++ b/xml-attr_int.html @@ -0,0 +1,28 @@ + + + + + +Xml.attr_int — Ludic + + + + + + +
+
API Reference › Xml › Xml.attr_int
+
+ method +

Xml.attr_int

+
+ Xml.attr_int(node, key: string, dflt: int) -> int +

Returns attribute key parsed as a signed decimal integer, or dflt when the attribute is absent.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-child.html b/xml-child.html new file mode 100644 index 00000000..629d1499 --- /dev/null +++ b/xml-child.html @@ -0,0 +1,28 @@ + + + + + +Xml.child — Ludic + + + + + + +
+
API Reference › Xml › Xml.child
+
+ method +

Xml.child

+
+ Xml.child(node, index: int) -> xml +

Returns the child element at index in document order, or an empty node if the index is out of range.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-child_count.html b/xml-child_count.html new file mode 100644 index 00000000..66b32815 --- /dev/null +++ b/xml-child_count.html @@ -0,0 +1,28 @@ + + + + + +Xml.child_count — Ludic + + + + + + +
+
API Reference › Xml › Xml.child_count
+
+ method +

Xml.child_count

+
+ Xml.child_count(node) -> int +

Returns the number of direct child elements of node (text and comments are not children).

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-count.html b/xml-count.html new file mode 100644 index 00000000..161e62f8 --- /dev/null +++ b/xml-count.html @@ -0,0 +1,28 @@ + + + + + +Xml.count — Ludic + + + + + + +
+
API Reference › Xml › Xml.count
+
+ method +

Xml.count

+
+ Xml.count(node, tag: string) -> int +

Returns how many direct children of node are named tag — e.g. the number of <layer> elements in a map.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-find.html b/xml-find.html new file mode 100644 index 00000000..e5306a40 --- /dev/null +++ b/xml-find.html @@ -0,0 +1,28 @@ + + + + + +Xml.find — Ludic + + + + + + +
+
API Reference › Xml › Xml.find
+
+ method +

Xml.find

+
+ Xml.find(node, tag: string) -> xml +

Returns the first direct child of node whose tag is tag, or an empty node if there is none.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-has.html b/xml-has.html new file mode 100644 index 00000000..91847cbf --- /dev/null +++ b/xml-has.html @@ -0,0 +1,28 @@ + + + + + +Xml.has — Ludic + + + + + + +
+
API Reference › Xml › Xml.has
+
+ method +

Xml.has

+
+ Xml.has(node, key: string) -> int +

Returns 1 if node carries attribute key, else 0.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-parse.html b/xml-parse.html new file mode 100644 index 00000000..f8843960 --- /dev/null +++ b/xml-parse.html @@ -0,0 +1,28 @@ + + + + + +Xml.parse — Ludic + + + + + + +
+
API Reference › Xml › Xml.parse
+
+ method +

Xml.parse

+
+ Xml.parse(text: string) -> xml +

Parses an XML document and returns its root element (or an empty node if there is none). Handles nested elements, single- and double-quoted attributes, text/<![CDATA[…]]> content, comments, the <?xml?> prolog and <!DOCTYPE>. Best-effort, not validating.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-tag.html b/xml-tag.html new file mode 100644 index 00000000..a6357812 --- /dev/null +++ b/xml-tag.html @@ -0,0 +1,28 @@ + + + + + +Xml.tag — Ludic + + + + + + +
+
API Reference › Xml › Xml.tag
+
+ method +

Xml.tag

+
+ Xml.tag(node) -> string +

Returns the element name of node (e.g. tileset), or "" for the empty node.

+ + + + ← All symbols +
+ + + \ No newline at end of file diff --git a/xml-text.html b/xml-text.html new file mode 100644 index 00000000..037912ec --- /dev/null +++ b/xml-text.html @@ -0,0 +1,28 @@ + + + + + +Xml.text — Ludic + + + + + + +
+
API Reference › Xml › Xml.text
+
+ method +

Xml.text

+
+ Xml.text(node) -> string +

Returns the concatenated character data of node — the CSV inside a <data encoding="csv"> element, or a <property> value, lands here (entity-decoded).

+ + + + ← All symbols +
+ + + \ No newline at end of file