Closes the two open issues and lands the pending unreleased batch: - #90: `Sprite { atlas: 1 }` routes esys_sprite through atlas_draw_ex (scale/flip/tint), so cell / cell_span / strip ids of any size draw through the engine sprite-render system. examples/library/sprite_atlas is the pixel-readback regression. - #91: `become` from an @On(Event) listener / global handler / plain function no longer segfaults the compiler; it emits @L_scene_leave() (a dispatch on the live scene id) so the leaving scene's on-exit runs. UI_* handles are readable from any code (widget table built on first use). examples/library/scene_menus covers it. - fix: a windowed `ludicc -o` build that reaches the audio runtime only through the atlas/Assets preload import now links audio.ll + AVFoundation (the audio backend link was gated on a game-level Audio.* call, so any windowed game declaring Sprite failed to link). - the hand-written "Unreleased" CHANGELOG section is converted to changesets under changes/ so `x release` generates it. - plus the batch: engine-driven retained UI + UiClicked event, Overlay phase, TileSkin tilemap-render system, Key.* constants, Font/Ui/File namespaces, Sprite.strip, prefabs, managers, countdown fields, enum-typed machines, layer @Queries, ludic.prefs / ludic.dungeon packages, Ai.seek pathing, Solids.solid2, cursor confine (mode 3) fix, shooter centre-aim fix, reserved-word function diagnostic. Verified: x test (124/124), x test-tools, check-impl, check-vocabulary, check-docs, docs-gen + docs-check, bootstrap-cfree (seed is a fixpoint). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e9c2c51bc3
commit
ad548840c7
139 changed files with 58981 additions and 43671 deletions
19
docs/language/angle/angle-diff_degrees.md
Normal file
19
docs/language/angle/angle-diff_degrees.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: angle-diff_degrees
|
||||
name: Angle.diff_degrees
|
||||
category: angle
|
||||
kind: namespace-method
|
||||
tokens: Angle.diff_degrees
|
||||
sig: Angle.diff_degrees(a: int, b: int) -> int
|
||||
tip: The signed difference between two headings in whole degrees.
|
||||
order: 20
|
||||
ns: Angle
|
||||
member: diff_degrees
|
||||
---
|
||||
|
||||
<code>b - a</code> wrapped into -180 .. 180, for integer-degree headings such as <code>TopDown.aim_angle</code> and <code>IVec2.heading</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if abs(Angle.diff_degrees(a: aim, b: IVec2.heading(hero, foe))) <= 50 { hit() }
|
||||
```
|
||||
21
docs/language/assets/assets-enqueue_dir.md
Normal file
21
docs/language/assets/assets-enqueue_dir.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: assets-enqueue_dir
|
||||
name: Assets.enqueue_dir
|
||||
category: assets
|
||||
kind: namespace-method
|
||||
tokens: Assets.enqueue_dir
|
||||
sig: Assets.enqueue_dir(path: dir)
|
||||
tip: Enqueue every file of a directory, named by its file name.
|
||||
order: 10
|
||||
ns: Assets
|
||||
member: enqueue_dir
|
||||
---
|
||||
|
||||
Each file is enqueued under its name without the extension (<code>assets/audio/hit.wav</code> → <code>hit</code>); the queue sorts it by extension as usual, so a sound directory becomes the sound bank in one line.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Assets.enqueue_dir(path: "assets/audio")
|
||||
…
|
||||
Audio.play(name: "hit")
|
||||
```
|
||||
21
docs/language/assets/assets-font.md
Normal file
21
docs/language/assets/assets-font.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: assets-font
|
||||
name: Assets.font
|
||||
category: assets
|
||||
kind: namespace-method
|
||||
tokens: Assets.font
|
||||
sig: Assets.font(name: s) -> int
|
||||
tip: A font loaded through the preload queue, by name.
|
||||
order: 9
|
||||
ns: Assets
|
||||
member: font
|
||||
---
|
||||
|
||||
The font handle for a <code>.ttf</code> / <code>.ttc</code> that <code>Assets.enqueue</code> loaded under <code>name</code>; 0 while it is not loaded yet. The queue sorts by extension: images become sprites (<code>Assets.get</code>), sounds go to the audio bank (<code>Audio.play(name:)</code>), fonts here.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Assets.enqueue(name: "menu", path: "assets/fonts/PixelOperator.ttf")
|
||||
…
|
||||
menu_font = Assets.font(name: "menu")
|
||||
```
|
||||
20
docs/language/audio/audio-define.md
Normal file
20
docs/language/audio/audio-define.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: audio-define
|
||||
name: Audio.define
|
||||
category: audio
|
||||
kind: namespace-method
|
||||
tokens: Audio.define
|
||||
sig: Audio.define(name: s, path: p) -> int
|
||||
tip: Load a sound into the bank under a name.
|
||||
order: 20
|
||||
ns: Audio
|
||||
member: define
|
||||
---
|
||||
|
||||
Loads a sound file and registers it under <code>name</code>, so <code>Audio.play(name: …)</code> and <code>Audio.play_music(name: …)</code> can fire it without a handle in the game's own state. Returns the handle (0 headless). <code>Assets.enqueue</code> does the same for a <code>.wav</code> / <code>.mp3</code> path.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Audio.define(name: "hit", path: "assets/audio/hit.wav")
|
||||
Audio.play(name: "hit")
|
||||
```
|
||||
19
docs/language/audio/audio-named.md
Normal file
19
docs/language/audio/audio-named.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: audio-named
|
||||
name: Audio.named
|
||||
category: audio
|
||||
kind: namespace-method
|
||||
tokens: Audio.named
|
||||
sig: Audio.named(name: s) -> int
|
||||
tip: The handle registered under a name, or 0.
|
||||
order: 21
|
||||
ns: Audio
|
||||
member: named
|
||||
---
|
||||
|
||||
Looks a sound up in the bank by the name <code>Audio.define</code> (or <code>Assets.enqueue</code>) gave it.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let hit = Audio.named(name: "hit")
|
||||
```
|
||||
19
docs/language/camera/camera-shake_for.md
Normal file
19
docs/language/camera/camera-shake_for.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: camera-shake_for
|
||||
name: Camera.shake_for
|
||||
category: camera
|
||||
kind: namespace-method
|
||||
tokens: Camera.shake_for
|
||||
sig: Camera.shake_for(amount: px, frames: n)
|
||||
tip: Shake for a number of frames, then stop, with no bookkeeping.
|
||||
order: 5
|
||||
ns: Camera
|
||||
member: shake_for
|
||||
---
|
||||
|
||||
Shakes the camera by up to ±<code>amount</code> pixels for <code>frames</code> frames; the engine re-rolls the offset each frame and clears it when the time is up. A bigger amount replaces one in flight. <code>Camera.shake(amount: 0)</code> still cancels the offset for the rest of a frame — useful before a HUD pass that must stay steady.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
@On(Damaged) handler Hurt { if target == player { Camera.shake_for(amount: 4, frames: 8) } }
|
||||
```
|
||||
7
docs/language/file/_section.md
Normal file
7
docs/language/file/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: file
|
||||
title: File
|
||||
order: 44
|
||||
---
|
||||
|
||||
Raw file access over the C stdio calls, for programs that read and write their own formats. <a href="file-open"><code>File.open</code></a> returns a handle (or <code>null</code>), <a href="file-read"><code>File.read</code></a> / <a href="file-write"><code>File.write</code></a> move bytes through a <code>bytes</code> buffer, <a href="file-seek"><code>File.seek</code></a> / <a href="file-tell"><code>File.tell</code></a> position the cursor, and <a href="file-close"><code>File.close</code></a> releases the handle. For text and structured data prefer the higher-level <code>Fs</code>, <code>Json</code> and <code>Prefs</code> APIs.
|
||||
19
docs/language/file/file-close.md
Normal file
19
docs/language/file/file-close.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: file-close
|
||||
name: File.close
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: File.close
|
||||
sig: File.close(file: f)
|
||||
tip: Close a handle from File.open.
|
||||
order: 6
|
||||
ns: File
|
||||
member: close
|
||||
---
|
||||
|
||||
Flushes and releases the handle. Every successful <code>File.open</code> pairs with one <code>File.close</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
File.close(file: f)
|
||||
```
|
||||
20
docs/language/file/file-open.md
Normal file
20
docs/language/file/file-open.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: file-open
|
||||
name: File.open
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: File.open
|
||||
sig: File.open(path: p, mode: m) -> pointer
|
||||
tip: Open a file (fopen); null when it cannot be opened.
|
||||
order: 1
|
||||
ns: File
|
||||
member: open
|
||||
---
|
||||
|
||||
Opens <code>path</code> with a C <code>fopen</code> mode string (<code>"rb"</code>, <code>"wb"</code>, <code>"ab"</code>). Returns the handle, or <code>null</code> when the file cannot be opened — always test for it.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let f = File.open(path: "save/best.txt", mode: "rb")
|
||||
if f == null { return }
|
||||
```
|
||||
20
docs/language/file/file-read.md
Normal file
20
docs/language/file/file-read.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: file-read
|
||||
name: File.read
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: 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.
|
||||
order: 2
|
||||
ns: File
|
||||
member: read
|
||||
---
|
||||
|
||||
Reads at most <code>count</code> bytes from the handle into <code>buffer</code> (a <code>bytes</code> allocation) and returns how many arrived; fewer than asked means the end of the file.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let buf = bytes(size + 1)
|
||||
let got = File.read(file: f, buffer: buf, count: size)
|
||||
```
|
||||
21
docs/language/file/file-seek.md
Normal file
21
docs/language/file/file-seek.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: file-seek
|
||||
name: File.seek
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: File.seek
|
||||
sig: File.seek(file: f, offset: o, whence: w)
|
||||
tip: Move the file cursor (0 start, 1 current, 2 end).
|
||||
order: 4
|
||||
ns: File
|
||||
member: seek
|
||||
---
|
||||
|
||||
Positions the cursor at <code>offset</code> bytes from <code>whence</code>: 0 the start, 1 the current position, 2 the end. Seeking to the end and reading <code>File.tell</code> measures a file.
|
||||
|
||||
```ludic
|
||||
# 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)
|
||||
```
|
||||
19
docs/language/file/file-tell.md
Normal file
19
docs/language/file/file-tell.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: file-tell
|
||||
name: File.tell
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: File.tell
|
||||
sig: File.tell(file: f) -> int
|
||||
tip: The cursor position in bytes.
|
||||
order: 5
|
||||
ns: File
|
||||
member: tell
|
||||
---
|
||||
|
||||
Returns the current cursor position in bytes from the start of the file.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let size = File.tell(file: f)
|
||||
```
|
||||
20
docs/language/file/file-write.md
Normal file
20
docs/language/file/file-write.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: file-write
|
||||
name: File.write
|
||||
category: file
|
||||
kind: namespace-method
|
||||
tokens: File.write
|
||||
sig: File.write(file: f, buffer: b, count: n) -> int
|
||||
tip: Write n bytes from a buffer; returns the bytes written.
|
||||
order: 3
|
||||
ns: File
|
||||
member: write
|
||||
---
|
||||
|
||||
Writes <code>count</code> bytes of <code>buffer</code> (a string or a <code>bytes</code> allocation) to the handle and returns how many were written.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let line = "best_floor=3\n"
|
||||
File.write(file: f, buffer: line, count: len(line))
|
||||
```
|
||||
7
docs/language/font/_section.md
Normal file
7
docs/language/font/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: font
|
||||
title: Font
|
||||
order: 45
|
||||
---
|
||||
|
||||
TrueType fonts for the retained UI and for <code>Screen.draw_text</code>-style drawing. <a href="font-load"><code>Font.load</code></a> reads a <code>.ttf</code> / <code>.ttc</code> file and returns a handle that a <code>ui</code> block's <code>font:</code> property or a text call takes.
|
||||
23
docs/language/font/font-load.md
Normal file
23
docs/language/font/font-load.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
id: font-load
|
||||
name: Font.load
|
||||
category: font
|
||||
kind: namespace-method
|
||||
tokens: Font.load
|
||||
sig: Font.load(path: p) -> int
|
||||
tip: Load a TrueType font; returns a font handle.
|
||||
order: 1
|
||||
ns: Font
|
||||
member: load
|
||||
---
|
||||
|
||||
Loads a TrueType font file and returns its handle. Load it before <code>Ui.build</code> so the menus can measure their text; the handle is what a <code>ui</code> block's <code>font:</code> reads.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
var menu_font: int = 0
|
||||
handler Boot phase Start {
|
||||
menu_font = Font.load(path: "assets/fonts/PixelOperator.ttf")
|
||||
Ui.build()
|
||||
}
|
||||
```
|
||||
7
docs/language/fx/_section.md
Normal file
7
docs/language/fx/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: fx
|
||||
title: Fx
|
||||
order: 46
|
||||
---
|
||||
|
||||
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.
|
||||
22
docs/language/fx/fx-clear.md
Normal file
22
docs/language/fx/fx-clear.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: fx-clear
|
||||
name: Fx.clear
|
||||
category: fx
|
||||
kind: namespace-method
|
||||
tokens: Fx.clear
|
||||
sig: Fx.clear()
|
||||
tip: Drop every spark and number at once.
|
||||
order: 3
|
||||
ns: Fx
|
||||
member: clear
|
||||
---
|
||||
|
||||
Removes every effect in flight — call it when the world changes under them (a room change, a new run).
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
function clear_room() -> void {
|
||||
for (p) in query [Position] { despawn self() }
|
||||
Fx.clear()
|
||||
}
|
||||
```
|
||||
19
docs/language/fx/fx-number.md
Normal file
19
docs/language/fx/fx-number.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: fx-number
|
||||
name: Fx.number
|
||||
category: fx
|
||||
kind: namespace-method
|
||||
tokens: Fx.number
|
||||
sig: Fx.number(x: px, y: px, value: n, color: c)
|
||||
tip: A number that floats up from a point and fades.
|
||||
order: 2
|
||||
ns: Fx
|
||||
member: number
|
||||
---
|
||||
|
||||
Shows <code>value</code> above (x, y) for 30 frames, rising one pixel every other frame — the damage number of every action game.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
@On(Damaged) handler ShowHit { Fx.number(x: center(target).x, y: center(target).y, value: amount, color: Color.Lemon) }
|
||||
```
|
||||
19
docs/language/fx/fx-sparks.md
Normal file
19
docs/language/fx/fx-sparks.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: fx-sparks
|
||||
name: Fx.sparks
|
||||
category: fx
|
||||
kind: namespace-method
|
||||
tokens: Fx.sparks
|
||||
sig: Fx.sparks(x: px, y: px, color: c, count: n)
|
||||
tip: A burst of sparks flying out of a point.
|
||||
order: 1
|
||||
ns: Fx
|
||||
member: sparks
|
||||
---
|
||||
|
||||
Spawns <code>count</code> 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 <code>color</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
@On(Died) handler Burst { Fx.sparks(x: center(e).x, y: center(e).y, color: Color.Crimson, count: 10) }
|
||||
```
|
||||
20
docs/language/input/input-move_i.md
Normal file
20
docs/language/input/input-move_i.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: input-move_i
|
||||
name: Input.move_i
|
||||
category: input
|
||||
kind: namespace-method
|
||||
tokens: Input.move_i
|
||||
sig: Input.move_i() -> IVec2
|
||||
tip: The standard top-down movement intent, -1/0/1 per axis.
|
||||
order: 60
|
||||
ns: Input
|
||||
member: move_i
|
||||
---
|
||||
|
||||
WASD or the arrow keys, and the left stick of pad 0 (past a 0.35 deadzone) when one is connected, as an <code>IVec2</code> of -1, 0 or 1 per axis — what a top-down mover feeds <code>TopDown.move</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let move = Input.move_i()
|
||||
TopDown.move(player, move.x, move.y)
|
||||
```
|
||||
19
docs/language/ivec2/ivec2-along.md
Normal file
19
docs/language/ivec2/ivec2-along.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ivec2-along
|
||||
name: IVec2.along
|
||||
category: ivec2
|
||||
kind: namespace-method
|
||||
tokens: IVec2.along
|
||||
sig: IVec2.along(origin, degrees, distance) -> IVec2
|
||||
tip: The point a distance along a heading.
|
||||
order: 23
|
||||
ns: IVec2
|
||||
member: along
|
||||
---
|
||||
|
||||
The point <code>distance</code> pixels from <code>origin</code> along <code>degrees</code> — the tip of an aim line, the centre of a melee arc.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let tip = IVec2.along(hero, aim, 18)
|
||||
```
|
||||
19
docs/language/ivec2/ivec2-distance2.md
Normal file
19
docs/language/ivec2/ivec2-distance2.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ivec2-distance2
|
||||
name: IVec2.distance2
|
||||
category: ivec2
|
||||
kind: namespace-method
|
||||
tokens: IVec2.distance2
|
||||
sig: IVec2.distance2(a, b) -> int
|
||||
tip: The squared distance between two points.
|
||||
order: 20
|
||||
ns: IVec2
|
||||
member: distance2
|
||||
---
|
||||
|
||||
<code>dx*dx + dy*dy</code> — compare it against a squared radius to avoid a square root.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if IVec2.distance2(hero, foe) < 24 * 24 { … }
|
||||
```
|
||||
19
docs/language/ivec2/ivec2-heading.md
Normal file
19
docs/language/ivec2/ivec2-heading.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ivec2-heading
|
||||
name: IVec2.heading
|
||||
category: ivec2
|
||||
kind: namespace-method
|
||||
tokens: IVec2.heading
|
||||
sig: IVec2.heading(from, to) -> int
|
||||
tip: The direction from one point to another, in degrees.
|
||||
order: 22
|
||||
ns: IVec2
|
||||
member: heading
|
||||
---
|
||||
|
||||
The angle from <code>from</code> to <code>to</code> in whole degrees: 0 is +x, 90 is +y (screen y grows downward). Pair it with <code>Angle.diff_degrees</code> for an arc test.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let to_foe = IVec2.heading(hero, foe)
|
||||
```
|
||||
19
docs/language/ivec2/ivec2-step.md
Normal file
19
docs/language/ivec2/ivec2-step.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ivec2-step
|
||||
name: IVec2.step
|
||||
category: ivec2
|
||||
kind: namespace-method
|
||||
tokens: IVec2.step
|
||||
sig: IVec2.step(degrees) -> IVec2
|
||||
tip: A -1/0/1 unit step along a heading.
|
||||
order: 24
|
||||
ns: IVec2
|
||||
member: step
|
||||
---
|
||||
|
||||
The heading snapped to the eight compass steps: each component is -1, 0 or 1. A roll or knockback direction from an aim angle.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let direction = IVec2.step(TopDown.of(player).aim_angle)
|
||||
```
|
||||
19
docs/language/ivec2/ivec2-within.md
Normal file
19
docs/language/ivec2/ivec2-within.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ivec2-within
|
||||
name: IVec2.within
|
||||
category: ivec2
|
||||
kind: namespace-method
|
||||
tokens: IVec2.within
|
||||
sig: IVec2.within(a, b, radius) -> bool
|
||||
tip: Are two points within a radius of each other?
|
||||
order: 21
|
||||
ns: IVec2
|
||||
member: within
|
||||
---
|
||||
|
||||
True when the distance between <code>a</code> and <code>b</code> is at most <code>radius</code> (compared squared, no square root).
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if IVec2.within(hero, heart, 14) { pick_up() }
|
||||
```
|
||||
19
docs/language/list/list-sample.md
Normal file
19
docs/language/list/list-sample.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: list-sample
|
||||
name: List.sample
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.sample
|
||||
sig: List.sample(pool, count) -> []int
|
||||
tip: Random picks from an int slice, distinct while it can.
|
||||
order: 30
|
||||
ns: List
|
||||
member: sample
|
||||
---
|
||||
|
||||
Returns <code>count</code> picks from an <code>[]int</code>, 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.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
relic_offer = List.sample(Relics.unowned(), 3)
|
||||
```
|
||||
20
docs/language/map/map-border.md
Normal file
20
docs/language/map/map-border.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: map-border
|
||||
name: Map.border
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.border
|
||||
sig: Map.border(glyph: g)
|
||||
tip: The outermost ring of cells.
|
||||
order: 14
|
||||
ns: Map
|
||||
member: border
|
||||
---
|
||||
|
||||
Writes <code>glyph</code> along the map's edge — the arena wall around a generated room.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Map.fill(glyph: '.')
|
||||
Map.border(glyph: '#')
|
||||
```
|
||||
20
docs/language/map/map-fill.md
Normal file
20
docs/language/map/map-fill.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: map-fill
|
||||
name: Map.fill
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.fill
|
||||
sig: Map.fill(glyph: g)
|
||||
tip: Every cell becomes the glyph.
|
||||
order: 12
|
||||
ns: Map
|
||||
member: fill
|
||||
---
|
||||
|
||||
Fills the whole map with one glyph — the first stroke of a generated room.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Map.size(20, 15)
|
||||
Map.fill(glyph: '.')
|
||||
```
|
||||
19
docs/language/map/map-get.md
Normal file
19
docs/language/map/map-get.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-get
|
||||
name: Map.get
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.get
|
||||
sig: Map.get(x: tx, y: ty) -> int
|
||||
tip: The glyph at a cell ('#' outside the map).
|
||||
order: 10
|
||||
ns: Map
|
||||
member: get
|
||||
---
|
||||
|
||||
Reads one cell of the tilemap by tile coordinates; outside the map it answers <code>'#'</code>, so the edge of the world reads as wall.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if Map.get(x: 3, y: 4) == '.' { … }
|
||||
```
|
||||
19
docs/language/map/map-height.md
Normal file
19
docs/language/map/map-height.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-height
|
||||
name: Map.height
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.height
|
||||
sig: Map.height() -> int
|
||||
tip: The map's height in cells.
|
||||
order: 19
|
||||
ns: Map
|
||||
member: height
|
||||
---
|
||||
|
||||
The height set by <code>Map.size</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
for y in 0 .. Map.height() { … }
|
||||
```
|
||||
19
docs/language/map/map-is_solid.md
Normal file
19
docs/language/map/map-is_solid.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-is_solid
|
||||
name: Map.is_solid
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.is_solid
|
||||
sig: Map.is_solid(x: tx, y: ty) -> bool
|
||||
tip: Is a cell solid, per the Solids config?
|
||||
order: 16
|
||||
ns: Map
|
||||
member: is_solid
|
||||
---
|
||||
|
||||
True when the cell holds one of the solid glyphs the <code>Solids</code> configuration names (<code>wall</code>, <code>solid2</code>), or lies outside the map. With no <code>Solids</code> entity nothing is solid. The move system and the projectiles use the same answer.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if Map.is_solid(x: tile.x, y: tile.y) { continue }
|
||||
```
|
||||
19
docs/language/map/map-is_solid_at.md
Normal file
19
docs/language/map/map-is_solid_at.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-is_solid_at
|
||||
name: Map.is_solid_at
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.is_solid_at
|
||||
sig: Map.is_solid_at(x: px, y: py) -> bool
|
||||
tip: Is the cell under a pixel position solid?
|
||||
order: 17
|
||||
ns: Map
|
||||
member: is_solid_at
|
||||
---
|
||||
|
||||
<code>Map.is_solid</code> for a pixel position, using the tile size from the <code>Solids</code> config.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
if not Map.is_solid_at(x: pushed.x, y: pushed.y) { Position.x = pushed.x }
|
||||
```
|
||||
20
docs/language/map/map-random_cell.md
Normal file
20
docs/language/map/map-random_cell.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: map-random_cell
|
||||
name: Map.random_cell
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.random_cell
|
||||
sig: Map.random_cell(glyph: g) -> IVec2
|
||||
tip: A random cell holding the glyph (seeded RNG).
|
||||
order: 15
|
||||
ns: Map
|
||||
member: random_cell
|
||||
---
|
||||
|
||||
Picks a random cell whose glyph matches: random tries first, then a sweep, so a map with any such cell always yields one; <code>(-1, -1)</code> when none exists. Deterministic, from the seeded RNG.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let tile = Map.random_cell(glyph: '.')
|
||||
Spawn.enemy(kind, IVec2.scale(tile, 16))
|
||||
```
|
||||
19
docs/language/map/map-random_cell_far.md
Normal file
19
docs/language/map/map-random_cell_far.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-random_cell_far
|
||||
name: Map.random_cell_far
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: 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.
|
||||
order: 20
|
||||
ns: Map
|
||||
member: random_cell_far
|
||||
---
|
||||
|
||||
Like <code>Map.random_cell</code>, but at least <code>min_tiles</code> from <code>from</code> when it can find one — a spawn point away from the hero.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let tile = Map.random_cell_far(glyph: '.', from: hero_tile, min_tiles: 6)
|
||||
```
|
||||
19
docs/language/map/map-rect.md
Normal file
19
docs/language/map/map-rect.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-rect
|
||||
name: Map.rect
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.rect
|
||||
sig: Map.rect(x: tx, y: ty, width: w, height: h, glyph: g)
|
||||
tip: Fill a rectangle of cells.
|
||||
order: 13
|
||||
ns: Map
|
||||
member: rect
|
||||
---
|
||||
|
||||
Writes <code>glyph</code> into every cell of the rectangle; cells outside the map are skipped. A wall slab, a carved corridor, a cleared lane.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Map.rect(x: 9, y: 1, width: 2, height: 13, glyph: '.') # a lane the cover never blocks
|
||||
```
|
||||
19
docs/language/map/map-set.md
Normal file
19
docs/language/map/map-set.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-set
|
||||
name: Map.set
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.set
|
||||
sig: Map.set(x: tx, y: ty, glyph: g)
|
||||
tip: Write one cell in place.
|
||||
order: 11
|
||||
ns: Map
|
||||
member: set
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Map.set(x: 9, y: 0, glyph: '.') # open the north door
|
||||
```
|
||||
19
docs/language/map/map-to_tile.md
Normal file
19
docs/language/map/map-to_tile.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-to_tile
|
||||
name: Map.to_tile
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.to_tile
|
||||
sig: Map.to_tile(pixel: IVec2) -> IVec2
|
||||
tip: The tile under a pixel position.
|
||||
order: 21
|
||||
ns: Map
|
||||
member: to_tile
|
||||
---
|
||||
|
||||
Divides a pixel position by the tile size of the <code>Solids</code> config.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let tile = Map.to_tile(pixel: Collider.center(player))
|
||||
```
|
||||
19
docs/language/map/map-width.md
Normal file
19
docs/language/map/map-width.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: map-width
|
||||
name: Map.width
|
||||
category: map
|
||||
kind: namespace-method
|
||||
tokens: Map.width
|
||||
sig: Map.width() -> int
|
||||
tip: The map's width in cells.
|
||||
order: 18
|
||||
ns: Map
|
||||
member: width
|
||||
---
|
||||
|
||||
The width set by <code>Map.size</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
for x in 0 .. Map.width() { … }
|
||||
```
|
||||
7
docs/language/prefab/_section.md
Normal file
7
docs/language/prefab/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: prefab
|
||||
title: Prefab
|
||||
order: 47
|
||||
---
|
||||
|
||||
A <code>prefab</code> is a model with preset component fields — <code>prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } }</code> — spawned with <code>spawn Grunt { Position { x: 40 } }</code>, where the spawn's own fields override the presets. Prefabs chain (<code>prefab Grunt: Foe</code>, where <code>Foe</code> is itself a prefab) so shared presets live once. <code>spawn</code> is also an expression yielding the new entity (<code>let e = spawn Grunt { … }</code>), and <a href="prefab-spawn"><code>Prefab.spawn</code></a> spawns a prefab chosen at runtime by name.
|
||||
20
docs/language/prefab/prefab-spawn.md
Normal file
20
docs/language/prefab/prefab-spawn.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: prefab-spawn
|
||||
name: Prefab.spawn
|
||||
category: prefab
|
||||
kind: namespace-method
|
||||
tokens: Prefab.spawn
|
||||
sig: Prefab.spawn(name: s) -> entity
|
||||
tip: Spawn a prefab chosen by name at runtime; -1 if none has that name.
|
||||
order: 1
|
||||
ns: Prefab
|
||||
member: spawn
|
||||
---
|
||||
|
||||
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 <code>Prop.of(entity)</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let e = Prefab.spawn(name: roster.roll())
|
||||
Position.of(e).x = tile.x * 16
|
||||
```
|
||||
19
docs/language/prefab/prefab-spawn_at.md
Normal file
19
docs/language/prefab/prefab-spawn_at.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: prefab-spawn_at
|
||||
name: Prefab.spawn_at
|
||||
category: prefab
|
||||
kind: namespace-method
|
||||
tokens: Prefab.spawn_at
|
||||
sig: Prefab.spawn_at(name: s, at: IVec2) -> entity
|
||||
tip: Spawn a prefab by name and place it.
|
||||
order: 2
|
||||
ns: Prefab
|
||||
member: spawn_at
|
||||
---
|
||||
|
||||
`Prefab.spawn` followed by writing the new entity's `Position` — the common case in one call.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let foe = Prefab.spawn_at(name: "Grunt", at: IVec2.make(60, 60))
|
||||
```
|
||||
19
docs/language/random/random-weighted.md
Normal file
19
docs/language/random/random-weighted.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: random-weighted
|
||||
name: Random.weighted
|
||||
category: random
|
||||
kind: namespace-method
|
||||
tokens: Random.weighted
|
||||
sig: Random.weighted(weights: []int) -> int
|
||||
tip: An index drawn in proportion to its weight.
|
||||
order: 10
|
||||
ns: Random
|
||||
member: weighted
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
let pick = roster[Random.weighted(weights: weights)]
|
||||
```
|
||||
19
docs/language/screen/screen-bar.md
Normal file
19
docs/language/screen/screen-bar.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: screen-bar
|
||||
name: Screen.bar
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.bar
|
||||
sig: Screen.bar(x:, y:, width:, height:, value:, max:, color:, back:)
|
||||
tip: A filled meter: value of max over a track.
|
||||
order: 40
|
||||
ns: Screen
|
||||
member: bar
|
||||
---
|
||||
|
||||
Draws a <code>back</code>-coloured track and fills <code>value / max</code> of its width in <code>color</code>. Health bars, cooldowns, loading progress.
|
||||
|
||||
```ludic
|
||||
# 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)
|
||||
```
|
||||
19
docs/language/sprite/sprite-draw_meter.md
Normal file
19
docs/language/sprite/sprite-draw_meter.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: sprite-draw_meter
|
||||
name: Sprite.draw_meter
|
||||
category: sprite
|
||||
kind: namespace-method
|
||||
tokens: 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.
|
||||
order: 10
|
||||
ns: Sprite
|
||||
member: draw_meter
|
||||
---
|
||||
|
||||
Draws <code>max / per_icon</code> icons, each full, half or empty according to <code>value</code> — hearts, stars, ammo pips.
|
||||
|
||||
```ludic
|
||||
# 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)
|
||||
```
|
||||
20
docs/language/sprite/sprite-strip.md
Normal file
20
docs/language/sprite/sprite-strip.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: sprite-strip
|
||||
name: Sprite.strip
|
||||
category: sprite
|
||||
kind: namespace-method
|
||||
tokens: Sprite.strip
|
||||
sig: Sprite.strip(sheet, col, row, count, rows) -> int
|
||||
tip: A run of animation frames as consecutive sprite ids.
|
||||
order: 9
|
||||
ns: Sprite
|
||||
member: strip
|
||||
---
|
||||
|
||||
Registers <code>count</code> frames starting at cell (<code>col</code>, <code>row</code>), each <code>rows</code> cells tall, as consecutive ids and returns the first. With a <code>SpriteAnim</code> on the entity the engine adds the current frame to <code>Sprite.id</code>, so the strip's first id is all a spawn needs.
|
||||
|
||||
```ludic
|
||||
# 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 } }
|
||||
```
|
||||
21
docs/language/structure/kw-prefab.md
Normal file
21
docs/language/structure/kw-prefab.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: kw-prefab
|
||||
name: prefab
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: prefab
|
||||
sig: prefab Name: Model { Comp { field: value }, … }
|
||||
tip: A model with preset component fields — spawn it, override what varies.
|
||||
order: 9
|
||||
---
|
||||
|
||||
A <code>prefab</code> names a model together with preset field values, so what every spawn of a kind shares is written once: <code>prefab Grunt: Creature { Stats { hp: 30 }, Weapon { def_id: 1 } }</code>. <code>spawn Grunt { Position { x: 40 } }</code> spawns the model with the prefab's presets and then the spawn's own fields on top. Prefabs chain — <code>prefab Grunt: Foe</code> where <code>Foe</code> 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), <code>@OnSpawn(Model)</code> runs as for any spawn of the model, <code>spawn</code> is also an expression yielding the new entity, and <code>Prefab.spawn(name:)</code> picks a prefab by name at runtime.
|
||||
|
||||
```ludic
|
||||
# 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")
|
||||
```
|
||||
20
docs/language/types/type-countdown.md
Normal file
20
docs/language/types/type-countdown.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: type-countdown
|
||||
name: countdown
|
||||
category: types
|
||||
kind: type
|
||||
tokens: countdown
|
||||
sig: countdown
|
||||
tip: An int component field the engine steps toward 0 once per Update.
|
||||
order: 9
|
||||
---
|
||||
|
||||
`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`.
|
||||
|
||||
```ludic
|
||||
# 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() }
|
||||
```
|
||||
7
docs/language/ui/_section.md
Normal file
7
docs/language/ui/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: ui
|
||||
title: Ui
|
||||
order: 43
|
||||
---
|
||||
|
||||
The retained-mode menu API over a <code>ui</code> block. <a href="ui-build"><code>Ui.build</code></a> constructs every declared widget tree once (after fonts and skins are loaded); <a href="ui-open"><code>Ui.open</code></a> makes one menu active and <a href="ui-close"><code>Ui.close</code></a> deactivates it; the frame loop ticks navigation on its own, an activation fires the <code>UiClicked</code> event, and <a href="ui-clicked"><code>Ui.clicked</code></a> is the polled form; <a href="ui-set_text"><code>Ui.set_text</code></a> updates a label or button, and <a href="ui-render"><code>Ui.render</code></a> draws the active menu (call it from an <code>Overlay</code> handler so it paints over the world). Every <code>id: Name</code> in a <code>ui</code> block mints a <code>UI_Name</code> handle.
|
||||
21
docs/language/ui/ui-build.md
Normal file
21
docs/language/ui/ui-build.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-build
|
||||
name: Ui.build
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.build
|
||||
sig: Ui.build()
|
||||
tip: Construct every declared ui block (loads skins, measures fonts).
|
||||
order: 1
|
||||
ns: Ui
|
||||
member: build
|
||||
---
|
||||
|
||||
Builds the widget trees of every <code>ui</code> block. Call it once, after the fonts and images the blocks reference are loaded — a loading scene's last step is the usual place.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
handler Boot phase Start {
|
||||
Ui.build()
|
||||
}
|
||||
```
|
||||
21
docs/language/ui/ui-clicked.md
Normal file
21
docs/language/ui/ui-clicked.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-clicked
|
||||
name: Ui.clicked
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.clicked
|
||||
sig: Ui.clicked(id: UI_Name) -> bool
|
||||
tip: Was this control activated this frame? (polled form of UiClicked)
|
||||
order: 5
|
||||
ns: Ui
|
||||
member: clicked
|
||||
---
|
||||
|
||||
True on the frame a focused control with that id was activated (Enter, Space, or pad A). The event form is <code>@On(UiClicked) handler … { if id == UI_Name { … } }</code>; either can <code>become</code> another scene.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
handler Menu phase Update {
|
||||
if Ui.clicked(id: UI_Play) { become Play }
|
||||
}
|
||||
```
|
||||
21
docs/language/ui/ui-close.md
Normal file
21
docs/language/ui/ui-close.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-close
|
||||
name: Ui.close
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.close
|
||||
sig: Ui.close()
|
||||
tip: Deactivate the menu: no menu is open.
|
||||
order: 3
|
||||
ns: Ui
|
||||
member: close
|
||||
---
|
||||
|
||||
Closes whatever menu is active, so nothing is drawn by <code>Ui.render</code> and no click can fire. A play scene opens with it so a menu left over from the title never lingers.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
scene Play {
|
||||
on enter { Ui.close() }
|
||||
}
|
||||
```
|
||||
21
docs/language/ui/ui-open.md
Normal file
21
docs/language/ui/ui-open.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-open
|
||||
name: Ui.open
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.open
|
||||
sig: Ui.open(id: UI_Name)
|
||||
tip: Make one menu active and focus its first button.
|
||||
order: 2
|
||||
ns: Ui
|
||||
member: open
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
scene Title {
|
||||
on enter { Ui.open(id: UI_TitleMenu) }
|
||||
}
|
||||
```
|
||||
21
docs/language/ui/ui-render.md
Normal file
21
docs/language/ui/ui-render.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-render
|
||||
name: Ui.render
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.render
|
||||
sig: Ui.render()
|
||||
tip: Draw the active menu.
|
||||
order: 7
|
||||
ns: Ui
|
||||
member: render
|
||||
---
|
||||
|
||||
Draws the active menu at its laid-out position. Call it from a handler in the <code>Overlay</code> phase so the menu is painted over the engine-drawn world; it draws nothing while no menu is open.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
layer Menu {
|
||||
handler Draw phase Overlay { Ui.render() }
|
||||
}
|
||||
```
|
||||
19
docs/language/ui/ui-set_text.md
Normal file
19
docs/language/ui/ui-set_text.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: ui-set_text
|
||||
name: Ui.set_text
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.set_text
|
||||
sig: Ui.set_text(id: UI_Name, text: s)
|
||||
tip: Replace a label's or button's text.
|
||||
order: 6
|
||||
ns: Ui
|
||||
member: set_text
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
Ui.set_text(id: UI_Stats, text: `floor {run.floor} kills {run.kills}`)
|
||||
```
|
||||
21
docs/language/ui/ui-tick.md
Normal file
21
docs/language/ui/ui-tick.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: ui-tick
|
||||
name: Ui.tick
|
||||
category: ui
|
||||
kind: namespace-method
|
||||
tokens: Ui.tick
|
||||
sig: Ui.tick(key: k)
|
||||
tip: Advance navigation by one key (the frame loop does this for you).
|
||||
order: 4
|
||||
ns: Ui
|
||||
member: tick
|
||||
---
|
||||
|
||||
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 <code>entry</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — illustrative
|
||||
entry {
|
||||
Ui.tick(key: Input.key())
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue