feat(stdlib): namespaced standard library (issue #2)
Implement the bulk of the namespaced-stdlib proposal (workshopsoft/ludic#2): 156 namespace methods across Math, Text, List, Ease, Collide, World, Net, Sys, Save, Mem, extended Screen, Color functions, extended Random, and Time. All deterministic fixed-point; self-hosting (C-free bootstrap fixpoint holds). Compiler (selfhost/): - Math.*: sqrt/sin/cos/tan/atan2/asin/acos (fixed-point runtime prelude — bit-by-bit isqrt, 256-entry interpolated sine table, Ross atan2), plus hypot/dist/dist2/deg_to_rad/rad_to_deg/posmod/wrap/ping_pong/snapped/ move_toward/smoothstep/lerp/remap/sign/floor/ceil/round. - Text.* (complete): upper/lower/trim/repeat/pad, split/join/replace, and the libc-backed queries. - List.* (complete): insert/remove_at/remove/sort plus the earlier ops. - Ease.* (in/out/in_out/back/bounce) and Collide.* (rects/point_rect/ circles/rect_circle). - Phase 3: World/Net/Sys/Save namespaced over the bare builtins (byte- identical IR) and Mem.* (bytes/words/copy/fill/peek/poke). - Screen.* extended (line/circle/fill_circle/triangle/fill_triangle via new runtime primitives; sprite/sprite_scaled aliases), Color.* functions, Random.* (value/int/sign), Time.* (frame/delta/elapsed/now — new game-loop frame counter). - Fix a lexer bug: fixed-point literals with >4 fractional digits overflowed. Docs & tooling: - 129 new per-symbol doc pages; gen.py made data-driven (namespaces discovered from the docs, no hardcoded list); new check-impl.py enforces that every implemented namespace method / keyword / type / phase has a doc page, wired into `x test-tools`. Document the previously-undocumented keywords (break/continue/where/entry/new/public + and/or/not tokens). - LSP: namespaced signature help (ns_method_sig) covering every namespace. Tests: 12 new self-host/regression tests + a golden render for the drawing primitives. All suites green (selfhost 21, regression 45, tools 29). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ff15c4e01d
commit
a38195128f
235 changed files with 24676 additions and 7762 deletions
7
docs/language/collide/_section.md
Normal file
7
docs/language/collide/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: collide
|
||||
title: Collide
|
||||
order: 6
|
||||
---
|
||||
|
||||
2D overlap tests on integer coordinates (pixels or tiles). Rectangles are <code>(x, y, w, h)</code> from the top-left; circles are <code>(x, y, r)</code>. Each returns a bool. Squared distances are computed in 64-bit so large coordinates never overflow.
|
||||
22
docs/language/collide/collide-circles.md
Normal file
22
docs/language/collide/collide-circles.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: collide-circles
|
||||
name: Collide.circles
|
||||
category: collide
|
||||
kind: namespace-method
|
||||
tokens: Collide.circles
|
||||
sig: Collide.circles(ax, ay, ar, bx, by, br) -> bool
|
||||
tip: Do two circles overlap?
|
||||
order: 2
|
||||
ns: Collide
|
||||
member: circles
|
||||
---
|
||||
|
||||
Returns <code>true</code> when circles centred at <code>(ax, ay)</code> and <code>(bx, by)</code> with radii <code>ar</code> and <code>br</code> overlap — that is, when the distance between centres is at most <code>ar + br</code>. It compares squared distances internally, so there is no square root and no precision loss.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
if Collide.circles(px, py, 8, ex, ey, 8) { collide() }
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/collide/collide-point_rect.md
Normal file
22
docs/language/collide/collide-point_rect.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: collide-point_rect
|
||||
name: Collide.point_rect
|
||||
category: collide
|
||||
kind: namespace-method
|
||||
tokens: Collide.point_rect
|
||||
sig: Collide.point_rect(px, py, rx, ry, rw, rh) -> bool
|
||||
tip: Is a point inside a rectangle?
|
||||
order: 1
|
||||
ns: Collide
|
||||
member: point_rect
|
||||
---
|
||||
|
||||
Returns <code>true</code> when the point <code>(px, py)</code> lies within the rectangle <code>(rx, ry, rw, rh)</code> — 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
if Collide.point_rect(mx, my, bx, by, bw, bh) { press() }
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/collide/collide-rect_circle.md
Normal file
22
docs/language/collide/collide-rect_circle.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: collide-rect_circle
|
||||
name: Collide.rect_circle
|
||||
category: collide
|
||||
kind: namespace-method
|
||||
tokens: Collide.rect_circle
|
||||
sig: Collide.rect_circle(rx, ry, rw, rh, cx, cy, cr) -> bool
|
||||
tip: Does a rectangle overlap a circle?
|
||||
order: 3
|
||||
ns: Collide
|
||||
member: rect_circle
|
||||
---
|
||||
|
||||
Returns <code>true</code> when the rectangle <code>(rx, ry, rw, rh)</code> overlaps the circle centred at <code>(cx, cy)</code> with radius <code>cr</code>. 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
if Collide.rect_circle(tx, ty, 16, 16, bx, by, 6) { block() }
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/collide/collide-rects.md
Normal file
22
docs/language/collide/collide-rects.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: collide-rects
|
||||
name: Collide.rects
|
||||
category: collide
|
||||
kind: namespace-method
|
||||
tokens: Collide.rects
|
||||
sig: Collide.rects(ax, ay, aw, ah, bx, by, bw, bh) -> bool
|
||||
tip: Do two rectangles overlap?
|
||||
order: 0
|
||||
ns: Collide
|
||||
member: rects
|
||||
---
|
||||
|
||||
Returns <code>true</code> when the axis-aligned rectangles <code>(ax, ay, aw, ah)</code> and <code>(bx, by, bw, bh)</code> overlap. Edges that merely touch do not count as overlapping. This is the standard broad-phase test for two hitboxes, tiles, or UI regions.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
if Collide.rects(px, py, 16, 16, ex, ey, 16, 16) { hit() }
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/color/_section.md
Normal file
7
docs/language/color/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: color
|
||||
title: Color functions
|
||||
order: 6
|
||||
---
|
||||
|
||||
Building and blending colors at runtime. Colors are <code>0x00RRGGBB</code> ints; these pack channels and transform an existing color. The named palette constants (<code>Color.Charcoal</code>, …) are a separate compile-time set.
|
||||
24
docs/language/color/color-darken.md
Normal file
24
docs/language/color/color-darken.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-darken
|
||||
name: Color.darken
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.darken
|
||||
sig: Color.darken(c, amount) -> int
|
||||
tip: Scale a color toward black.
|
||||
order: 3
|
||||
ns: Color
|
||||
member: darken
|
||||
---
|
||||
|
||||
Multiplies each channel of <code>c</code> by <code>1 - amount</code> (amount a fixed 0..1), returning a darker color. <code>Color.darken(c, 0.0)</code> is unchanged and <code>1.0</code> is black. Handy for shadows and pressed-button states.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.darken(Color.Red, 0.3)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/color/color-lerp.md
Normal file
24
docs/language/color/color-lerp.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-lerp
|
||||
name: Color.lerp
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.lerp
|
||||
sig: Color.lerp(c0, c1, t) -> int
|
||||
tip: Blend between two colors.
|
||||
order: 2
|
||||
ns: Color
|
||||
member: lerp
|
||||
---
|
||||
|
||||
Interpolates each channel of <code>c0</code> and <code>c1</code> by the fixed amount <code>t</code> in <code>0.0</code>..<code>1.0</code>, returning the blended color. Use it for gradients, fades between two tints, or a flash that returns to base.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.lerp(Color.Black, Color.White, 0.5)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/color/color-lighten.md
Normal file
24
docs/language/color/color-lighten.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-lighten
|
||||
name: Color.lighten
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.lighten
|
||||
sig: Color.lighten(c, amount) -> int
|
||||
tip: Scale a color toward white.
|
||||
order: 4
|
||||
ns: Color
|
||||
member: lighten
|
||||
---
|
||||
|
||||
Moves each channel of <code>c</code> a fraction <code>amount</code> of the way to 255, returning a lighter color — the counterpart to <code>Color.darken</code>. Good for highlights and hover states.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.lighten(Color.Blue, 0.3)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/color/color-rgb.md
Normal file
24
docs/language/color/color-rgb.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-rgb
|
||||
name: Color.rgb
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.rgb
|
||||
sig: Color.rgb(r, g, b) -> int
|
||||
tip: Build a color from red, green, blue.
|
||||
order: 0
|
||||
ns: Color
|
||||
member: rgb
|
||||
---
|
||||
|
||||
Packs three 0..255 channel values into a single <code>0xRRGGBB</code> color int. Use it to build a color from computed channels rather than a fixed literal or a palette name.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.rgb(255, 128, 64)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/color/color-rgba.md
Normal file
24
docs/language/color/color-rgba.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-rgba
|
||||
name: Color.rgba
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.rgba
|
||||
sig: Color.rgba(r, g, b, a) -> int
|
||||
tip: Build a color with an alpha byte.
|
||||
order: 1
|
||||
ns: Color
|
||||
member: rgba
|
||||
---
|
||||
|
||||
Packs <code>r</code>, <code>g</code>, <code>b</code>, and an alpha <code>a</code> (0..255) into a <code>0xAARRGGBB</code> int. The software renderer draws opaque, but the alpha byte is preserved for your own blending or storage.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.rgba(255, 128, 64, 200)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/color/color-with_alpha.md
Normal file
24
docs/language/color/color-with_alpha.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: color-with_alpha
|
||||
name: Color.with_alpha
|
||||
category: color
|
||||
kind: namespace-method
|
||||
tokens: Color.with_alpha
|
||||
sig: Color.with_alpha(c, a) -> int
|
||||
tip: Replace a color's alpha byte.
|
||||
order: 5
|
||||
ns: Color
|
||||
member: with_alpha
|
||||
---
|
||||
|
||||
Returns <code>c</code> with its alpha byte set to <code>a</code> (0..255), leaving the RGB channels untouched.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
let c = Color.with_alpha(Color.Green, 128)
|
||||
Screen.clear(c)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
27
docs/language/control/kw-break.md
Normal file
27
docs/language/control/kw-break.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: kw-break
|
||||
name: break
|
||||
category: control
|
||||
kind: keyword
|
||||
tokens: break
|
||||
sig: break
|
||||
tip: Leave the enclosing loop immediately.
|
||||
order: 9
|
||||
---
|
||||
|
||||
<code>break</code> exits the innermost <code>while</code> or <code>for</code> 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 <code>while true</code> that decides when to leave. It affects only the loop that contains it; an outer loop keeps running.
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/language/control/kw-continue.md
Normal file
25
docs/language/control/kw-continue.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: kw-continue
|
||||
name: continue
|
||||
category: control
|
||||
kind: keyword
|
||||
tokens: continue
|
||||
sig: continue
|
||||
tip: Skip to the next iteration of the loop.
|
||||
order: 10
|
||||
---
|
||||
|
||||
<code>continue</code> abandons the rest of the current loop pass and jumps straight to the next one — the next condition check in a <code>while</code>, or the next index in a <code>for</code>. Use it to skip elements that do not apply without nesting the rest of the body inside an <code>if</code>: filter out empty slots, ignore inactive entities, or step over even numbers. Like <code>break</code>, it acts on the innermost loop only.
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/language/control/kw-where.md
Normal file
25
docs/language/control/kw-where.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: kw-where
|
||||
name: where
|
||||
category: control
|
||||
kind: keyword
|
||||
tokens: where
|
||||
sig: for (Binds) in query [Comps] where cond { … }
|
||||
tip: Filter a query loop to entities that satisfy a condition.
|
||||
order: 11
|
||||
---
|
||||
|
||||
<code>where</code> attaches a boolean condition to a <code>query</code> 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 <code>where Battle.hp <= 0 and Battle.side == 1</code>. It is the imperative twin of a component filter written inside a <code>@Queries</code> annotation, and keeps the guard next to the loop instead of repeating an <code>if</code> at the top of the body.
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/ease/_section.md
Normal file
7
docs/language/ease/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: ease
|
||||
title: Ease
|
||||
order: 6
|
||||
---
|
||||
|
||||
Tween curves — the "juice" layer. Each takes a normalized amount <code>t</code> in <code>0.0</code>..<code>1.0</code> and returns an eased <code>fixed</code>, ready to feed to <code>Math.lerp</code>. All deterministic fixed-point.
|
||||
22
docs/language/ease/ease-back.md
Normal file
22
docs/language/ease/ease-back.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-back
|
||||
name: Ease.back
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.back
|
||||
sig: Ease.back(t) -> fixed
|
||||
tip: Ease in with a small backward anticipation.
|
||||
order: 3
|
||||
ns: Ease
|
||||
member: back
|
||||
---
|
||||
|
||||
Returns an ease-in that first dips slightly below <code>0</code> 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let s = Math.lerp(rest, poke, Ease.back(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/ease/ease-bounce.md
Normal file
22
docs/language/ease/ease-bounce.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-bounce
|
||||
name: Ease.bounce
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.bounce
|
||||
sig: Ease.bounce(t) -> fixed
|
||||
tip: Ease out with a settling bounce.
|
||||
order: 4
|
||||
ns: Ease
|
||||
member: bounce
|
||||
---
|
||||
|
||||
Returns an ease-out that overshoots and bounces a few times before settling at <code>1.0</code>, like a ball dropping to the floor. Great for coins landing, badges popping, or a menu that lands with character.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let y = Math.lerp(top, floor_y, Ease.bounce(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/ease/ease-in.md
Normal file
22
docs/language/ease/ease-in.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-in
|
||||
name: Ease.in
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.in
|
||||
sig: Ease.in(t) -> fixed
|
||||
tip: Accelerate from rest (quadratic ease-in).
|
||||
order: 0
|
||||
ns: Ease
|
||||
member: in
|
||||
---
|
||||
|
||||
Returns <code>t * t</code> — motion that starts slow and speeds up. Feed the result to <code>Math.lerp</code> as the blend amount so a value accelerates into its target. The input <code>t</code> is expected in <code>0.0</code>..<code>1.0</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let x = Math.lerp(start_x, end_x, Ease.in(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/ease/ease-in_out.md
Normal file
22
docs/language/ease/ease-in_out.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-in_out
|
||||
name: Ease.in_out
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.in_out
|
||||
sig: Ease.in_out(t) -> fixed
|
||||
tip: Ease in and then out (smoothstep).
|
||||
order: 2
|
||||
ns: Ease
|
||||
member: in_out
|
||||
---
|
||||
|
||||
Returns the smooth <code>3t^2 - 2t^3</code> 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let a = Math.lerp(0.0, 1.0, Ease.in_out(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/ease/ease-out.md
Normal file
22
docs/language/ease/ease-out.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-out
|
||||
name: Ease.out
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.out
|
||||
sig: Ease.out(t) -> fixed
|
||||
tip: Decelerate to rest (quadratic ease-out).
|
||||
order: 1
|
||||
ns: Ease
|
||||
member: out
|
||||
---
|
||||
|
||||
Returns <code>t * (2 - t)</code> — 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let y = Math.lerp(top, rest_y, Ease.out(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/list/_section.md
Normal file
7
docs/language/list/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: list
|
||||
title: List
|
||||
order: 6
|
||||
---
|
||||
|
||||
Operations over <code>[]T</code> 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.
|
||||
27
docs/language/list/list-clear.md
Normal file
27
docs/language/list/list-clear.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: list-clear
|
||||
name: List.clear
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.clear
|
||||
sig: List.clear(s) -> void
|
||||
tip: Remove all elements, keeping capacity.
|
||||
order: 2
|
||||
ns: List
|
||||
member: clear
|
||||
---
|
||||
|
||||
Empties <code>s</code> 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:
|
||||
- `s` — the slice to empty
|
||||
|
||||
```ludic
|
||||
program FrameHits {
|
||||
var hits: []int = new []int
|
||||
|
||||
handler BeginFrame phase Update {
|
||||
List.clear(hits) # start the frame with an empty list
|
||||
}
|
||||
}
|
||||
```
|
||||
31
docs/language/list/list-contains.md
Normal file
31
docs/language/list/list-contains.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
id: list-contains
|
||||
name: List.contains
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.contains
|
||||
sig: List.contains(s, v) -> bool
|
||||
tip: Whether a value appears in a slice.
|
||||
order: 7
|
||||
ns: List
|
||||
member: contains
|
||||
---
|
||||
|
||||
Scans <code>s</code> and returns <code>true</code> if any element equals <code>v</code>. 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:
|
||||
- `s` — the slice to search
|
||||
- `v` — the value to look for
|
||||
|
||||
```ludic
|
||||
program Visited {
|
||||
var seen: []int = new []int
|
||||
|
||||
handler Step phase Update {
|
||||
let tile = 42
|
||||
if not List.contains(seen, tile) {
|
||||
List.push(seen, tile)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
29
docs/language/list/list-first.md
Normal file
29
docs/language/list/list-first.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: list-first
|
||||
name: List.first
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.first
|
||||
sig: List.first(s) -> T
|
||||
tip: The first element of a slice.
|
||||
order: 3
|
||||
ns: List
|
||||
member: first
|
||||
---
|
||||
|
||||
Returns element <code>0</code> of <code>s</code> — the same as <code>s[0]</code>, named for readability. The slice must hold at least one element; calling it on an empty slice reads out of bounds, so guard with <code>List.len(s) > 0</code> when that is possible. Use it to peek at the head of a queue or the front of an ordered list.
|
||||
|
||||
Parameters:
|
||||
- `s` — a non-empty slice
|
||||
|
||||
```ludic
|
||||
program Queue {
|
||||
var waiting: []int = new []int
|
||||
|
||||
handler Serve phase Update {
|
||||
if List.len(waiting) > 0 {
|
||||
let next = List.first(waiting)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
31
docs/language/list/list-index_of.md
Normal file
31
docs/language/list/list-index_of.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
id: list-index_of
|
||||
name: List.index_of
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.index_of
|
||||
sig: List.index_of(s, v) -> int
|
||||
tip: The index of a value in a slice, or -1 if absent.
|
||||
order: 8
|
||||
ns: List
|
||||
member: index_of
|
||||
---
|
||||
|
||||
Scans <code>s</code> and returns the index of the first element equal to <code>v</code>, or <code>-1</code> if none match. It uses the same value/identity comparison as <code>List.contains</code>, 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 <code>if idx >= 0</code>. Only the first match is reported.
|
||||
|
||||
Parameters:
|
||||
- `s` — the slice to search
|
||||
- `v` — the value to locate
|
||||
|
||||
```ludic
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/list/list-insert.md
Normal file
22
docs/language/list/list-insert.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: list-insert
|
||||
name: List.insert
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.insert
|
||||
sig: List.insert(s, i, v) -> void
|
||||
tip: Insert an element at an index, shifting the rest up.
|
||||
order: 10
|
||||
ns: List
|
||||
member: insert
|
||||
---
|
||||
|
||||
Inserts <code>v</code> at index <code>i</code>, moving the elements at and after <code>i</code> 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 <code>List.push</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
List.insert(queue, 0, next_id) # push to the front
|
||||
}
|
||||
}
|
||||
```
|
||||
29
docs/language/list/list-last.md
Normal file
29
docs/language/list/list-last.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: list-last
|
||||
name: List.last
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.last
|
||||
sig: List.last(s) -> T
|
||||
tip: The last element of a slice.
|
||||
order: 4
|
||||
ns: List
|
||||
member: last
|
||||
---
|
||||
|
||||
Returns the final element of <code>s</code> — the value at index <code>List.len(s) - 1</code>. Like <code>List.first</code>, 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 <code>List.pop</code> when you want to take it off.
|
||||
|
||||
Parameters:
|
||||
- `s` — a non-empty slice
|
||||
|
||||
```ludic
|
||||
program Trail {
|
||||
var points: []int = new []int
|
||||
|
||||
handler Head phase Update {
|
||||
if List.len(points) > 0 {
|
||||
let newest = List.last(points)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
27
docs/language/list/list-len.md
Normal file
27
docs/language/list/list-len.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: list-len
|
||||
name: List.len
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.len
|
||||
sig: List.len(s) -> int
|
||||
tip: The number of elements in a slice.
|
||||
order: 0
|
||||
ns: List
|
||||
member: len
|
||||
---
|
||||
|
||||
Returns how many elements <code>s</code> currently holds. It reads the slice's length, which changes as you <code>List.push</code> and <code>List.pop</code>, and is the same value as the bare <code>len(s)</code>. Use it to bound a loop, test for emptiness, or find the last index with <code>List.len(s) - 1</code>.
|
||||
|
||||
Parameters:
|
||||
- `s` — the slice to measure
|
||||
|
||||
```ludic
|
||||
program CountEnemies {
|
||||
var enemies: []int = new []int
|
||||
|
||||
handler Report phase Update {
|
||||
let n = List.len(enemies)
|
||||
}
|
||||
}
|
||||
```
|
||||
29
docs/language/list/list-pop.md
Normal file
29
docs/language/list/list-pop.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: list-pop
|
||||
name: List.pop
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.pop
|
||||
sig: List.pop(s) -> T
|
||||
tip: Remove and return the last element.
|
||||
order: 5
|
||||
ns: List
|
||||
member: pop
|
||||
---
|
||||
|
||||
Removes the final element of <code>s</code> and returns it, shortening the slice by one. Together with <code>List.push</code> 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:
|
||||
- `s` — a non-empty slice
|
||||
|
||||
```ludic
|
||||
program UndoStack {
|
||||
var history: []int = new []int
|
||||
|
||||
handler Undo phase Update {
|
||||
if List.len(history) > 0 {
|
||||
let last_action = List.pop(history)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
28
docs/language/list/list-push.md
Normal file
28
docs/language/list/list-push.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: list-push
|
||||
name: List.push
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.push
|
||||
sig: List.push(s, v) -> void
|
||||
tip: Append an element to the end of a slice.
|
||||
order: 1
|
||||
ns: List
|
||||
member: push
|
||||
---
|
||||
|
||||
Adds <code>v</code> to the end of <code>s</code>, growing the backing buffer if needed, so the slice's length increases by one. It is the same operation as the bare <code>push(s, v)</code>. 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:
|
||||
- `s` — the slice to append to
|
||||
- `v` — the element to add (matching the slice's element type)
|
||||
|
||||
```ludic
|
||||
program Collect {
|
||||
var picked: []int = new []int
|
||||
|
||||
handler Grab phase Update {
|
||||
List.push(picked, 7)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/list/list-remove.md
Normal file
22
docs/language/list/list-remove.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: list-remove
|
||||
name: List.remove
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.remove
|
||||
sig: List.remove(s, v) -> void
|
||||
tip: Remove the first element equal to a value.
|
||||
order: 12
|
||||
ns: List
|
||||
member: remove
|
||||
---
|
||||
|
||||
Scans for the first element equal to <code>v</code> 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 <code>List.contains</code>. Only the first match is removed.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
List.remove(active, dead_entity)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/list/list-remove_at.md
Normal file
22
docs/language/list/list-remove_at.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: list-remove_at
|
||||
name: List.remove_at
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.remove_at
|
||||
sig: List.remove_at(s, i) -> void
|
||||
tip: Remove the element at an index, shifting the rest down.
|
||||
order: 11
|
||||
ns: List
|
||||
member: remove_at
|
||||
---
|
||||
|
||||
Removes the element at index <code>i</code>, 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 <code>List.pop</code> is cheaper.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
List.remove_at(effects, expired)
|
||||
}
|
||||
}
|
||||
```
|
||||
28
docs/language/list/list-reverse.md
Normal file
28
docs/language/list/list-reverse.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: list-reverse
|
||||
name: List.reverse
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.reverse
|
||||
sig: List.reverse(s) -> void
|
||||
tip: Reverse the order of a slice in place.
|
||||
order: 9
|
||||
ns: List
|
||||
member: reverse
|
||||
---
|
||||
|
||||
Reverses <code>s</code> 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:
|
||||
- `s` — the slice to reverse
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/list/list-sort.md
Normal file
22
docs/language/list/list-sort.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: list-sort
|
||||
name: List.sort
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.sort
|
||||
sig: List.sort(s) -> void
|
||||
tip: Sort a slice in ascending order, in place.
|
||||
order: 13
|
||||
ns: List
|
||||
member: sort
|
||||
---
|
||||
|
||||
Sorts the elements of <code>s</code> in ascending order in place, using an insertion sort. It is intended for slices of scalar elements (ints, entities, fixed) where <code><</code> is meaningful; reference elements are ordered by identity, which is rarely useful. Insertion sort is simple and fast for the small, nearly-sorted lists games usually hold.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
List.sort(scores)
|
||||
}
|
||||
}
|
||||
```
|
||||
32
docs/language/list/list-swap.md
Normal file
32
docs/language/list/list-swap.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
id: list-swap
|
||||
name: List.swap
|
||||
category: list
|
||||
kind: namespace-method
|
||||
tokens: List.swap
|
||||
sig: List.swap(s, i, j) -> void
|
||||
tip: Exchange the elements at two indices.
|
||||
order: 6
|
||||
ns: List
|
||||
member: swap
|
||||
---
|
||||
|
||||
Exchanges the elements at indices <code>i</code> and <code>j</code> in <code>s</code>, 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 <code>List.pop</code> to remove it cheaply without shifting the rest.
|
||||
|
||||
Parameters:
|
||||
- `s` — the slice to modify
|
||||
- `i` — the first index
|
||||
- `j` — the second index
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/math/_section.md
Normal file
7
docs/language/math/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: math
|
||||
title: Math
|
||||
order: 6
|
||||
---
|
||||
|
||||
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.
|
||||
28
docs/language/math/math-abs.md
Normal file
28
docs/language/math/math-abs.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: math-abs
|
||||
name: Math.abs
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.abs
|
||||
sig: Math.abs(x) -> int
|
||||
tip: The magnitude of a value, dropping its sign.
|
||||
order: 2
|
||||
ns: Math
|
||||
member: abs
|
||||
---
|
||||
|
||||
Returns the absolute value of <code>x</code> — its distance from zero, always non-negative. It preserves the type of its argument, so <code>Math.abs</code> of a <code>fixed</code> stays <code>fixed</code>. 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 <code>abs(x)</code> is kept as a convenience alias.
|
||||
|
||||
Parameters:
|
||||
- `x` — the value whose magnitude you want
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-acos.md
Normal file
22
docs/language/math/math-acos.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-acos
|
||||
name: Math.acos
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.acos
|
||||
sig: Math.acos(x) -> fixed
|
||||
tip: The arccosine of a value, in radians.
|
||||
order: 28
|
||||
ns: Math
|
||||
member: acos
|
||||
---
|
||||
|
||||
Returns the angle in radians whose cosine is <code>x</code>, in <code>0</code>..<code>pi</code>. The input should be in <code>-1.0</code>..<code>1.0</code>. Handy for recovering the angle between two normalized directions from their dot product.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let between = Math.acos(dot)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-asin.md
Normal file
22
docs/language/math/math-asin.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-asin
|
||||
name: Math.asin
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.asin
|
||||
sig: Math.asin(x) -> fixed
|
||||
tip: The arcsine of a value, in radians.
|
||||
order: 27
|
||||
ns: Math
|
||||
member: asin
|
||||
---
|
||||
|
||||
Returns the angle in radians whose sine is <code>x</code>, in <code>-pi/2</code>..<code>pi/2</code>. The input should be in <code>-1.0</code>..<code>1.0</code>. Built from <code>Math.atan2</code> and <code>Math.sqrt</code>, so it is deterministic.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let angle = Math.asin(height / radius)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-atan2.md
Normal file
22
docs/language/math/math-atan2.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-atan2
|
||||
name: Math.atan2
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.atan2
|
||||
sig: Math.atan2(y, x) -> fixed
|
||||
tip: The angle of the vector (x, y), in radians.
|
||||
order: 26
|
||||
ns: Math
|
||||
member: atan2
|
||||
---
|
||||
|
||||
Returns the angle in radians from the positive x-axis to the point <code>(x, y)</code>, in the full range <code>-pi</code>..<code>pi</code> — the standard way to get a heading from a direction vector. Note the argument order is <code>y</code> then <code>x</code>. Deterministic fixed-point, accurate to within about a degree.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let heading = Math.atan2(target_y - y, target_x - x)
|
||||
}
|
||||
}
|
||||
```
|
||||
29
docs/language/math/math-ceil.md
Normal file
29
docs/language/math/math-ceil.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: math-ceil
|
||||
name: Math.ceil
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.ceil
|
||||
sig: Math.ceil(x) -> int
|
||||
tip: Round a fixed value up to the nearest whole int.
|
||||
order: 6
|
||||
ns: Math
|
||||
member: ceil
|
||||
---
|
||||
|
||||
Takes a <code>fixed</code> value and returns the smallest <code>int</code> not less than it — rounding toward positive infinity, so <code>Math.ceil(2.1)</code> is <code>3</code> and <code>Math.ceil(4.0)</code> stays <code>4</code>. 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:
|
||||
- `x` — the `fixed` value to round up
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
31
docs/language/math/math-clamp.md
Normal file
31
docs/language/math/math-clamp.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
id: math-clamp
|
||||
name: Math.clamp
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.clamp
|
||||
sig: Math.clamp(v, lo, hi) -> int
|
||||
tip: Constrain a value to the range [lo, hi].
|
||||
order: 3
|
||||
ns: Math
|
||||
member: clamp
|
||||
---
|
||||
|
||||
Returns <code>v</code> held inside the inclusive range <code>lo</code>..<code>hi</code>: below <code>lo</code> it returns <code>lo</code>, above <code>hi</code> it returns <code>hi</code>, otherwise <code>v</code> unchanged. It is the single-call form of <code>Math.max(lo, Math.min(v, hi))</code> and works for <code>int</code> and <code>fixed</code> 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 <code>clamp(v, lo, hi)</code> is kept as a convenience alias.
|
||||
|
||||
Parameters:
|
||||
- `v` — the value to constrain
|
||||
- `lo` — the lowest allowed result (inclusive)
|
||||
- `hi` — the highest allowed result (inclusive)
|
||||
|
||||
```ludic
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-cos.md
Normal file
22
docs/language/math/math-cos.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-cos
|
||||
name: Math.cos
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.cos
|
||||
sig: Math.cos(radians) -> fixed
|
||||
tip: Cosine of an angle in radians.
|
||||
order: 13
|
||||
ns: Math
|
||||
member: cos
|
||||
---
|
||||
|
||||
Returns the cosine of <code>radians</code> as a <code>fixed</code> in <code>-1.0</code>..<code>1.0</code>, computed as <code>Math.sin(radians + pi/2)</code> off the same deterministic table. Use <code>Math.cos</code> and <code>Math.sin</code> together to convert an angle into an <code>(x, y)</code> direction — for orbits, aiming, or steering.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let x = center_x + Math.cos(angle) * radius
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-deg_to_rad.md
Normal file
22
docs/language/math/math-deg_to_rad.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-deg_to_rad
|
||||
name: Math.deg_to_rad
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.deg_to_rad
|
||||
sig: Math.deg_to_rad(degrees) -> fixed
|
||||
tip: Convert degrees to radians.
|
||||
order: 18
|
||||
ns: Math
|
||||
member: deg_to_rad
|
||||
---
|
||||
|
||||
Converts an angle from degrees to radians (multiplying by <code>pi/180</code>), since <code>Math.sin</code>/<code>Math.cos</code>/<code>Math.tan</code> 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let r = Math.sin(Math.deg_to_rad(90.0)) # ~1.0
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-dist.md
Normal file
22
docs/language/math/math-dist.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-dist
|
||||
name: Math.dist
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.dist
|
||||
sig: Math.dist(x0, y0, x1, y1) -> fixed
|
||||
tip: Distance between two points.
|
||||
order: 16
|
||||
ns: Math
|
||||
member: dist
|
||||
---
|
||||
|
||||
Returns the straight-line distance between <code>(x0, y0)</code> and <code>(x1, y1)</code> — <code>sqrt(dx*dx + dy*dy)</code> in fixed-point. Use it for proximity and range checks. When you only need to compare distances (which is nearer? within radius?), prefer <code>Math.dist2</code>, which skips the square root.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
if Math.dist(px, py, ex, ey) < attack_range { strike() }
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/language/math/math-dist2.md
Normal file
23
docs/language/math/math-dist2.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
id: math-dist2
|
||||
name: Math.dist2
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.dist2
|
||||
sig: Math.dist2(x0, y0, x1, y1) -> fixed
|
||||
tip: Squared distance between two points.
|
||||
order: 17
|
||||
ns: Math
|
||||
member: dist2
|
||||
---
|
||||
|
||||
Returns the squared distance between <code>(x0, y0)</code> and <code>(x1, y1)</code> — <code>dx*dx + dy*dy</code>, 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 <code>radius * radius</code> instead of calling <code>Math.dist</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let r2 = radius * radius
|
||||
if Math.dist2(px, py, ex, ey) < r2 { in_range() }
|
||||
}
|
||||
}
|
||||
```
|
||||
29
docs/language/math/math-floor.md
Normal file
29
docs/language/math/math-floor.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: math-floor
|
||||
name: Math.floor
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.floor
|
||||
sig: Math.floor(x) -> int
|
||||
tip: Round a fixed value down to the nearest whole int.
|
||||
order: 5
|
||||
ns: Math
|
||||
member: floor
|
||||
---
|
||||
|
||||
Takes a <code>fixed</code> value and returns the largest <code>int</code> not greater than it — rounding toward negative infinity, so <code>Math.floor(2.7)</code> is <code>2</code> and <code>Math.floor(-0.2)</code> is <code>-1</code>. 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 <code>flr(x)</code>.
|
||||
|
||||
Parameters:
|
||||
- `x` — the `fixed` value to round down
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-hypot.md
Normal file
22
docs/language/math/math-hypot.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-hypot
|
||||
name: Math.hypot
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.hypot
|
||||
sig: Math.hypot(x, y) -> fixed
|
||||
tip: Length of the vector (x, y).
|
||||
order: 15
|
||||
ns: Math
|
||||
member: hypot
|
||||
---
|
||||
|
||||
Returns <code>sqrt(x*x + y*y)</code> — the length of the 2D vector <code>(x, y)</code>, 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 <code>Math.hypot</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let length = Math.hypot(velocity_x, velocity_y)
|
||||
}
|
||||
}
|
||||
```
|
||||
31
docs/language/math/math-inverse_lerp.md
Normal file
31
docs/language/math/math-inverse_lerp.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
id: math-inverse_lerp
|
||||
name: Math.inverse_lerp
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: 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.
|
||||
order: 9
|
||||
ns: Math
|
||||
member: inverse_lerp
|
||||
---
|
||||
|
||||
The inverse of <code>Math.lerp</code>: given endpoints <code>a</code> and <code>b</code> and a value <code>v</code>, it returns the fraction <code>(v - a) / (b - a)</code> — <code>0.0</code> when <code>v</code> equals <code>a</code>, <code>1.0</code> when it equals <code>b</code>. 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 <code>t</code> to feed into another <code>Math.lerp</code>. The endpoints must differ, since equal ones would divide by zero.
|
||||
|
||||
Parameters:
|
||||
- `a` — the endpoint that maps to `0.0`
|
||||
- `b` — the endpoint that maps to `1.0`
|
||||
- `v` — the value to locate between them
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
30
docs/language/math/math-lerp.md
Normal file
30
docs/language/math/math-lerp.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
id: math-lerp
|
||||
name: Math.lerp
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.lerp
|
||||
sig: Math.lerp(a, b, t) -> fixed
|
||||
tip: Blend between two values by a 0..1 amount.
|
||||
order: 8
|
||||
ns: Math
|
||||
member: lerp
|
||||
---
|
||||
|
||||
Linear interpolation: returns <code>a</code> when <code>t</code> is <code>0.0</code>, <code>b</code> when <code>t</code> is <code>1.0</code>, and a proportional blend in between — the exact value <code>a + (b - a) * t</code>. All three arguments are <code>fixed</code> and the result is <code>fixed</code>. It is the workhorse behind smooth motion: ease a camera toward a target, fade a value over time, or sample a gradient. Values of <code>t</code> outside <code>0..1</code> extrapolate past the endpoints.
|
||||
|
||||
Parameters:
|
||||
- `a` — the value returned at `t = 0.0`
|
||||
- `b` — the value returned at `t = 1.0`
|
||||
- `t` — the blend amount, normally `0.0`..`1.0`
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
28
docs/language/math/math-max.md
Normal file
28
docs/language/math/math-max.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: math-max
|
||||
name: Math.max
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.max
|
||||
sig: Math.max(a, b) -> int
|
||||
tip: The larger of two values.
|
||||
order: 1
|
||||
ns: Math
|
||||
member: max
|
||||
---
|
||||
|
||||
Returns whichever of <code>a</code> and <code>b</code> is larger. Like <code>Math.min</code>, it works for both <code>int</code> and <code>fixed</code> 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 <code>max(a, b)</code> is kept as a convenience alias.
|
||||
|
||||
Parameters:
|
||||
- `a` — the first value
|
||||
- `b` — the second value
|
||||
|
||||
```ludic
|
||||
program Countdown {
|
||||
var timer: int = 60
|
||||
|
||||
handler Tick phase Update {
|
||||
timer = Math.max(timer - 1, 0) # stops at 0, never negative
|
||||
}
|
||||
}
|
||||
```
|
||||
28
docs/language/math/math-min.md
Normal file
28
docs/language/math/math-min.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: math-min
|
||||
name: Math.min
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.min
|
||||
sig: Math.min(a, b) -> int
|
||||
tip: The smaller of two values.
|
||||
order: 0
|
||||
ns: Math
|
||||
member: min
|
||||
---
|
||||
|
||||
Returns whichever of <code>a</code> and <code>b</code> is smaller. It works on plain <code>int</code> values and on <code>fixed</code> 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 <code>min(a, b)</code> is kept as a convenience alias.
|
||||
|
||||
Parameters:
|
||||
- `a` — the first value
|
||||
- `b` — the second value
|
||||
|
||||
```ludic
|
||||
program ClampScore {
|
||||
var score: int = 0
|
||||
|
||||
handler AddPoint phase Update {
|
||||
score = Math.min(score + 1, 100) # never climbs past 100
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-move_toward.md
Normal file
22
docs/language/math/math-move_toward.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-move_toward
|
||||
name: Math.move_toward
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.move_toward
|
||||
sig: Math.move_toward(from, to, delta) -> fixed
|
||||
tip: Step from one value toward another by at most delta.
|
||||
order: 24
|
||||
ns: Math
|
||||
member: move_toward
|
||||
---
|
||||
|
||||
Moves <code>from</code> toward <code>to</code> by at most <code>delta</code>, never overshooting — when the gap is smaller than <code>delta</code> it returns exactly <code>to</code>. Unlike <code>Math.lerp</code>, 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.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
health_shown = Math.move_toward(health_shown, health, 2.0)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-ping_pong.md
Normal file
22
docs/language/math/math-ping_pong.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-ping_pong
|
||||
name: Math.ping_pong
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.ping_pong
|
||||
sig: Math.ping_pong(t, len) -> int
|
||||
tip: Bounce a counter back and forth in [0, len].
|
||||
order: 22
|
||||
ns: Math
|
||||
member: ping_pong
|
||||
---
|
||||
|
||||
Maps an ever-increasing counter <code>t</code> to a value that rises from <code>0</code> to <code>len</code> and back, bouncing forever — the triangle wave to <code>Math.wrap</code>'s sawtooth. Feed it a frame counter to drive a patrol, a pulsing highlight, or a back-and-forth sweep without tracking direction yourself.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let phase = Math.ping_pong(frame, 30)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-posmod.md
Normal file
22
docs/language/math/math-posmod.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-posmod
|
||||
name: Math.posmod
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.posmod
|
||||
sig: Math.posmod(a, m) -> int
|
||||
tip: Modulo that is always non-negative.
|
||||
order: 20
|
||||
ns: Math
|
||||
member: posmod
|
||||
---
|
||||
|
||||
Returns <code>a</code> modulo <code>m</code>, but always in the range <code>0</code>..<code>m-1</code> — unlike the <code>%</code> operator, which keeps the sign of <code>a</code> (so <code>-1 % 5</code> is <code>-1</code>, while <code>Math.posmod(-1, 5)</code> is <code>4</code>). It is what you want for wrapping indices and grid coordinates that can go negative.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let col = Math.posmod(x, grid_width) # never negative
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-rad_to_deg.md
Normal file
22
docs/language/math/math-rad_to_deg.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-rad_to_deg
|
||||
name: Math.rad_to_deg
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.rad_to_deg
|
||||
sig: Math.rad_to_deg(radians) -> fixed
|
||||
tip: Convert radians to degrees.
|
||||
order: 19
|
||||
ns: Math
|
||||
member: rad_to_deg
|
||||
---
|
||||
|
||||
Converts an angle from radians to degrees (multiplying by <code>180/pi</code>) — the inverse of <code>Math.deg_to_rad</code>. Use it to present an internally-radians angle in the degrees a player or designer expects, for a HUD readout or a debug overlay.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let heading_deg = Math.rad_to_deg(heading)
|
||||
}
|
||||
}
|
||||
```
|
||||
34
docs/language/math/math-remap.md
Normal file
34
docs/language/math/math-remap.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
id: math-remap
|
||||
name: Math.remap
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.remap
|
||||
sig: Math.remap(v, in0, in1, out0, out1) -> fixed
|
||||
tip: Rescale a value from one range into another.
|
||||
order: 10
|
||||
ns: Math
|
||||
member: remap
|
||||
---
|
||||
|
||||
Maps <code>v</code> from the input range <code>in0</code>..<code>in1</code> onto the output range <code>out0</code>..<code>out1</code>, keeping its relative position — equivalent to <code>Math.lerp(out0, out1, Math.inverse_lerp(in0, in1, v))</code>. All arguments are <code>fixed</code>. It is the one call that converts between units: a health value into a bar width, a noise sample in <code>-1..1</code> into a <code>0..255</code> shade, or a mouse position into a world coordinate. Swap the output ends to invert the mapping.
|
||||
|
||||
Parameters:
|
||||
- `v` — the value to rescale
|
||||
- `in0` — the start of the input range
|
||||
- `in1` — the end of the input range
|
||||
- `out0` — the start of the output range
|
||||
- `out1` — the end of the output range
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
27
docs/language/math/math-round.md
Normal file
27
docs/language/math/math-round.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: math-round
|
||||
name: Math.round
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.round
|
||||
sig: Math.round(x) -> int
|
||||
tip: Round a fixed value to the nearest whole int.
|
||||
order: 7
|
||||
ns: Math
|
||||
member: round
|
||||
---
|
||||
|
||||
Takes a <code>fixed</code> value and returns the nearest <code>int</code>, with halves rounding up: <code>Math.round(2.5)</code> is <code>3</code> and <code>Math.round(2.4)</code> is <code>2</code>. Choose it over <code>Math.floor</code> 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:
|
||||
- `x` — the `fixed` value to round to nearest
|
||||
|
||||
```ludic
|
||||
program SnapToPixel {
|
||||
var smooth_x: fixed = 10.5
|
||||
|
||||
handler Draw phase Render {
|
||||
let px = Math.round(smooth_x) # 11
|
||||
}
|
||||
}
|
||||
```
|
||||
28
docs/language/math/math-sign.md
Normal file
28
docs/language/math/math-sign.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: math-sign
|
||||
name: Math.sign
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.sign
|
||||
sig: Math.sign(x) -> int
|
||||
tip: The sign of a value as -1, 0, or 1.
|
||||
order: 4
|
||||
ns: Math
|
||||
member: sign
|
||||
---
|
||||
|
||||
Returns <code>-1</code> when <code>x</code> is negative, <code>1</code> when it is positive, and <code>0</code> when it is exactly zero. It works on both <code>int</code> and <code>fixed</code> values, since zero and sign are the same in Q16.16. Multiply a step by <code>Math.sign(dx)</code> to move one unit toward a target, or read the sign of a velocity to decide which way a sprite faces.
|
||||
|
||||
Parameters:
|
||||
- `x` — the value to test
|
||||
|
||||
```ludic
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-sin.md
Normal file
22
docs/language/math/math-sin.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-sin
|
||||
name: Math.sin
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.sin
|
||||
sig: Math.sin(radians) -> fixed
|
||||
tip: Sine of an angle in radians.
|
||||
order: 12
|
||||
ns: Math
|
||||
member: sin
|
||||
---
|
||||
|
||||
Returns the sine of <code>radians</code> as a <code>fixed</code> in <code>-1.0</code>..<code>1.0</code>. 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 <code>Math.cos</code> to turn an angle into a direction.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let y = center_y + Math.sin(angle) * radius
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-smoothstep.md
Normal file
22
docs/language/math/math-smoothstep.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-smoothstep
|
||||
name: Math.smoothstep
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.smoothstep
|
||||
sig: Math.smoothstep(e0, e1, x) -> fixed
|
||||
tip: A smooth 0..1 ramp between two edges.
|
||||
order: 25
|
||||
ns: Math
|
||||
member: smoothstep
|
||||
---
|
||||
|
||||
Returns <code>0.0</code> when <code>x</code> is at or below <code>e0</code>, <code>1.0</code> at or above <code>e1</code>, and a smooth S-curve (ease-in and ease-out) in between — the classic <code>t*t*(3 - 2t)</code> Hermite ramp. Use it for fades, dissolves, and easing a normalized amount before feeding it to <code>Math.lerp</code>, when a straight linear ramp looks too abrupt.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let a = Math.smoothstep(0.0, 1.0, fade_t)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-snapped.md
Normal file
22
docs/language/math/math-snapped.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-snapped
|
||||
name: Math.snapped
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.snapped
|
||||
sig: Math.snapped(v, step) -> fixed
|
||||
tip: Round a value to the nearest multiple of step.
|
||||
order: 23
|
||||
ns: Math
|
||||
member: snapped
|
||||
---
|
||||
|
||||
Rounds <code>v</code> to the nearest multiple of <code>step</code> (both <code>fixed</code>) — for snapping a position to a grid, quantizing an angle to 8 directions, or rounding a value to a tidy increment. <code>Math.snapped(x, 16.0)</code> snaps <code>x</code> to a 16-unit grid.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let gx = Math.snapped(world_x, 16.0)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-sqrt.md
Normal file
22
docs/language/math/math-sqrt.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-sqrt
|
||||
name: Math.sqrt
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.sqrt
|
||||
sig: Math.sqrt(x) -> fixed
|
||||
tip: The square root of a fixed value.
|
||||
order: 11
|
||||
ns: Math
|
||||
member: sqrt
|
||||
---
|
||||
|
||||
Returns the non-negative square root of <code>x</code>, 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 <code>0</code>. Use it for lengths and distances; when you have two legs of a right triangle reach for <code>Math.hypot</code>, and when you only need to compare magnitudes prefer the cheaper squared distance <code>Math.dist2</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let speed = Math.sqrt(vx * vx + vy * vy)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-tan.md
Normal file
22
docs/language/math/math-tan.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-tan
|
||||
name: Math.tan
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.tan
|
||||
sig: Math.tan(radians) -> fixed
|
||||
tip: Tangent of an angle in radians.
|
||||
order: 14
|
||||
ns: Math
|
||||
member: tan
|
||||
---
|
||||
|
||||
Returns the tangent of <code>radians</code>, computed as <code>Math.sin(radians) / Math.cos(radians)</code> in fixed-point. Near odd multiples of <code>pi/2</code> the cosine approaches zero and the result grows without bound, so guard those angles. For most gameplay, <code>Math.sin</code>/<code>Math.cos</code> are what you want; <code>tan</code> is here for slopes and field-of-view math.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let slope = Math.tan(pitch)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-wrap.md
Normal file
22
docs/language/math/math-wrap.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-wrap
|
||||
name: Math.wrap
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.wrap
|
||||
sig: Math.wrap(v, lo, hi) -> int
|
||||
tip: Wrap a value into the range [lo, hi).
|
||||
order: 21
|
||||
ns: Math
|
||||
member: wrap
|
||||
---
|
||||
|
||||
Wraps <code>v</code> into the half-open range <code>lo</code>..<code>hi</code>, so values that run off one end reappear at the other — the toroidal counterpart to <code>Math.clamp</code>. Use it for wrap-around movement, cycling through a menu, or keeping a scrolling offset in bounds.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let x = Math.wrap(player_x, 0, world_width)
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/mem/_section.md
Normal file
7
docs/language/mem/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: mem
|
||||
title: Mem
|
||||
order: 6
|
||||
---
|
||||
|
||||
Raw memory — allocate byte and word buffers, copy and fill regions, and peek/poke individual bytes. The low-level escape hatch.
|
||||
14
docs/language/mem/mem-bytes.md
Normal file
14
docs/language/mem/mem-bytes.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-bytes
|
||||
name: Mem.bytes
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.bytes
|
||||
sig: Mem.bytes(n) -> ptr
|
||||
tip: Allocate n bytes.
|
||||
order: 0
|
||||
ns: Mem
|
||||
member: bytes
|
||||
---
|
||||
|
||||
Allocates an <code>n</code>-byte buffer and returns a pointer to it.
|
||||
14
docs/language/mem/mem-copy.md
Normal file
14
docs/language/mem/mem-copy.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-copy
|
||||
name: Mem.copy
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.copy
|
||||
sig: Mem.copy(dst, src, n) -> void
|
||||
tip: Copy n bytes between buffers.
|
||||
order: 2
|
||||
ns: Mem
|
||||
member: copy
|
||||
---
|
||||
|
||||
Copies <code>n</code> bytes from <code>src</code> to <code>dst</code> (regions must not overlap).
|
||||
14
docs/language/mem/mem-fill.md
Normal file
14
docs/language/mem/mem-fill.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-fill
|
||||
name: Mem.fill
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.fill
|
||||
sig: Mem.fill(buf, value, n) -> void
|
||||
tip: Set n bytes to a value.
|
||||
order: 3
|
||||
ns: Mem
|
||||
member: fill
|
||||
---
|
||||
|
||||
Sets the first <code>n</code> bytes of <code>buf</code> to the byte <code>value</code>.
|
||||
14
docs/language/mem/mem-peek.md
Normal file
14
docs/language/mem/mem-peek.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-peek
|
||||
name: Mem.peek
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.peek
|
||||
sig: Mem.peek(buf, i) -> int
|
||||
tip: Read one byte.
|
||||
order: 4
|
||||
ns: Mem
|
||||
member: peek
|
||||
---
|
||||
|
||||
Returns the byte at <code>buf[i]</code> as an int in 0..255.
|
||||
14
docs/language/mem/mem-poke.md
Normal file
14
docs/language/mem/mem-poke.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-poke
|
||||
name: Mem.poke
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.poke
|
||||
sig: Mem.poke(buf, i, value) -> void
|
||||
tip: Write one byte.
|
||||
order: 5
|
||||
ns: Mem
|
||||
member: poke
|
||||
---
|
||||
|
||||
Stores the low byte of <code>value</code> at <code>buf[i]</code>.
|
||||
14
docs/language/mem/mem-words.md
Normal file
14
docs/language/mem/mem-words.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: mem-words
|
||||
name: Mem.words
|
||||
category: mem
|
||||
kind: namespace-method
|
||||
tokens: Mem.words
|
||||
sig: Mem.words(n) -> words
|
||||
tip: Allocate n 32-bit words.
|
||||
order: 1
|
||||
ns: Mem
|
||||
member: words
|
||||
---
|
||||
|
||||
Allocates a buffer of <code>n</code> 32-bit integer words, indexable as <code>w[i]</code>.
|
||||
7
docs/language/net/_section.md
Normal file
7
docs/language/net/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: net
|
||||
title: Net
|
||||
order: 6
|
||||
---
|
||||
|
||||
The low-level networking seam — send and poll datagrams, serialize and apply entity state, and read ownership and role. Offline these collapse to single-player defaults.
|
||||
14
docs/language/net/net-apply.md
Normal file
14
docs/language/net/net-apply.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-apply
|
||||
name: Net.apply
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.apply
|
||||
sig: Net.apply(entity, buf) -> int
|
||||
tip: Apply serialized state to an entity.
|
||||
order: 3
|
||||
ns: Net
|
||||
member: apply
|
||||
---
|
||||
|
||||
Reads synced fields from <code>buf</code> into <code>entity</code> — the inverse of <code>Net.serialize</code>.
|
||||
14
docs/language/net/net-is_owner.md
Normal file
14
docs/language/net/net-is_owner.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-is_owner
|
||||
name: Net.is_owner
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.is_owner
|
||||
sig: Net.is_owner(entity) -> bool
|
||||
tip: Does this peer own the entity?
|
||||
order: 7
|
||||
ns: Net
|
||||
member: is_owner
|
||||
---
|
||||
|
||||
Returns true when this peer owns <code>entity</code> — <code>owner(entity) == local_id()</code>.
|
||||
14
docs/language/net/net-is_server.md
Normal file
14
docs/language/net/net-is_server.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-is_server
|
||||
name: Net.is_server
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.is_server
|
||||
sig: Net.is_server() -> bool
|
||||
tip: Is this peer the server?
|
||||
order: 6
|
||||
ns: Net
|
||||
member: is_server
|
||||
---
|
||||
|
||||
Returns true on the authoritative peer. Offline it is true, so server-only guards run in single-player.
|
||||
14
docs/language/net/net-local_id.md
Normal file
14
docs/language/net/net-local_id.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-local_id
|
||||
name: Net.local_id
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.local_id
|
||||
sig: Net.local_id() -> int
|
||||
tip: This peer's own id.
|
||||
order: 8
|
||||
ns: Net
|
||||
member: local_id
|
||||
---
|
||||
|
||||
Returns the network id of the local peer.
|
||||
14
docs/language/net/net-owner.md
Normal file
14
docs/language/net/net-owner.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-owner
|
||||
name: Net.owner
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.owner
|
||||
sig: Net.owner(entity) -> int
|
||||
tip: The peer that owns an entity.
|
||||
order: 4
|
||||
ns: Net
|
||||
member: owner
|
||||
---
|
||||
|
||||
Returns the peer id that owns <code>entity</code>'s <code>@Owned</code> state.
|
||||
14
docs/language/net/net-poll.md
Normal file
14
docs/language/net/net-poll.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-poll
|
||||
name: Net.poll
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.poll
|
||||
sig: Net.poll(buf, cap) -> int
|
||||
tip: Read an inbound datagram.
|
||||
order: 1
|
||||
ns: Net
|
||||
member: poll
|
||||
---
|
||||
|
||||
Reads up to <code>cap</code> bytes of the next inbound datagram into <code>buf</code> and returns the byte count, or zero when none is waiting.
|
||||
14
docs/language/net/net-send.md
Normal file
14
docs/language/net/net-send.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-send
|
||||
name: Net.send
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.send
|
||||
sig: Net.send(peer, buf, len) -> void
|
||||
tip: Put a datagram on the wire.
|
||||
order: 0
|
||||
ns: Net
|
||||
member: send
|
||||
---
|
||||
|
||||
Sends the first <code>len</code> bytes of <code>buf</code> to <code>peer</code>. Absent a real socket it uses the built-in loopback, so offline code still runs.
|
||||
14
docs/language/net/net-serialize.md
Normal file
14
docs/language/net/net-serialize.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-serialize
|
||||
name: Net.serialize
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.serialize
|
||||
sig: Net.serialize(entity, buf) -> int
|
||||
tip: Serialize an entity's synced state.
|
||||
order: 2
|
||||
ns: Net
|
||||
member: serialize
|
||||
---
|
||||
|
||||
Writes the <code>@Sync</code> fields of <code>entity</code> into <code>buf</code> and returns the byte count, for sending over the wire.
|
||||
14
docs/language/net/net-set_owner.md
Normal file
14
docs/language/net/net-set_owner.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: net-set_owner
|
||||
name: Net.set_owner
|
||||
category: net
|
||||
kind: namespace-method
|
||||
tokens: Net.set_owner
|
||||
sig: Net.set_owner(entity, peer) -> void
|
||||
tip: Assign ownership of an entity.
|
||||
order: 5
|
||||
ns: Net
|
||||
member: set_owner
|
||||
---
|
||||
|
||||
Sets the owning peer of <code>entity</code> to <code>peer</code>.
|
||||
|
|
@ -3,6 +3,7 @@ id: op-logical
|
|||
name: Logical
|
||||
category: operators
|
||||
kind: operator
|
||||
tokens: and or not
|
||||
sig: and or not
|
||||
tip: The boolean combinators, spelled as words — never && or || or a bare !.
|
||||
order: 2
|
||||
|
|
|
|||
25
docs/language/random/random-int.md
Normal file
25
docs/language/random/random-int.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: random-int
|
||||
name: Random.int
|
||||
category: random
|
||||
kind: namespace-method
|
||||
tokens: Random.int
|
||||
sig: Random.int(max) -> int
|
||||
tip: A random integer in [0, max).
|
||||
order: 4
|
||||
ns: Random
|
||||
member: int
|
||||
---
|
||||
|
||||
Returns a deterministic integer from <code>0</code> up to but not including <code>max</code>. Use it to pick a random index into a list of length <code>max</code>. Returns 0 when <code>max</code> is not positive.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Seed phase Start { Random.seed(value: 7) }
|
||||
handler Step phase Update {
|
||||
let idx = Random.int(list_count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/language/random/random-sign.md
Normal file
25
docs/language/random/random-sign.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: random-sign
|
||||
name: Random.sign
|
||||
category: random
|
||||
kind: namespace-method
|
||||
tokens: Random.sign
|
||||
sig: Random.sign() -> int
|
||||
tip: A random +1 or -1.
|
||||
order: 5
|
||||
ns: Random
|
||||
member: sign
|
||||
---
|
||||
|
||||
Returns <code>1</code> or <code>-1</code> with equal probability — a deterministic coin flip for choosing a direction or jitter sign.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Seed phase Start { Random.seed(value: 7) }
|
||||
handler Step phase Update {
|
||||
let dir = Random.sign()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/language/random/random-value.md
Normal file
25
docs/language/random/random-value.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: random-value
|
||||
name: Random.value
|
||||
category: random
|
||||
kind: namespace-method
|
||||
tokens: Random.value
|
||||
sig: Random.value() -> fixed
|
||||
tip: A random fixed value in [0, 1).
|
||||
order: 3
|
||||
ns: Random
|
||||
member: value
|
||||
---
|
||||
|
||||
Returns a deterministic fixed-point value in <code>0.0</code>..<code>1.0</code> (exclusive of 1). Multiply it into a range, or compare it against a probability. The sequence is fixed for a given <code>Random.seed</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Seed phase Start { Random.seed(value: 7) }
|
||||
handler Step phase Update {
|
||||
let t = Random.value()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
7
docs/language/save/_section.md
Normal file
7
docs/language/save/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: save
|
||||
title: Save
|
||||
order: 6
|
||||
---
|
||||
|
||||
Whole-world save and restore — write a binary snapshot of the ECS world and read it back.
|
||||
14
docs/language/save/save-read.md
Normal file
14
docs/language/save/save-read.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: save-read
|
||||
name: Save.read
|
||||
category: save
|
||||
kind: namespace-method
|
||||
tokens: Save.read
|
||||
sig: Save.read() -> bool
|
||||
tip: Restore the saved snapshot.
|
||||
order: 1
|
||||
ns: Save
|
||||
member: read
|
||||
---
|
||||
|
||||
Restores the world from the save slot, returning false when there is no snapshot to load.
|
||||
14
docs/language/save/save-write.md
Normal file
14
docs/language/save/save-write.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
id: save-write
|
||||
name: Save.write
|
||||
category: save
|
||||
kind: namespace-method
|
||||
tokens: Save.write
|
||||
sig: Save.write() -> void
|
||||
tip: Save a snapshot of the world.
|
||||
order: 0
|
||||
ns: Save
|
||||
member: write
|
||||
---
|
||||
|
||||
Writes a binary snapshot of the whole ECS world to the save slot.
|
||||
24
docs/language/screen/screen-circle.md
Normal file
24
docs/language/screen/screen-circle.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-circle
|
||||
name: Screen.circle
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.circle
|
||||
sig: Screen.circle(x, y, radius, color)
|
||||
tip: Draw a circle outline.
|
||||
order: 11
|
||||
ns: Screen
|
||||
member: circle
|
||||
---
|
||||
|
||||
Draws the outline of a circle centred at <code>(x, y)</code> with the given <code>radius</code> in <code>color</code>. Use it for rings, radii, and selection markers; for a solid disc use <code>Screen.fill_circle</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
Screen.clear(Color.MidnightBlue)
|
||||
Screen.circle(x: 160, y: 120, radius: 40, color: Color.Cyan)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-fill_circle.md
Normal file
24
docs/language/screen/screen-fill_circle.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-fill_circle
|
||||
name: Screen.fill_circle
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.fill_circle
|
||||
sig: Screen.fill_circle(x, y, radius, color)
|
||||
tip: Draw a filled disc.
|
||||
order: 12
|
||||
ns: Screen
|
||||
member: fill_circle
|
||||
---
|
||||
|
||||
Draws a solid disc centred at <code>(x, y)</code> with the given <code>radius</code> in <code>color</code>, one horizontal span per row. Use it for round bodies, bullets, and glows.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
Screen.clear(Color.MidnightBlue)
|
||||
Screen.fill_circle(x: bx, y: by, radius: 6, color: Color.Gold)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-fill_triangle.md
Normal file
24
docs/language/screen/screen-fill_triangle.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-fill_triangle
|
||||
name: Screen.fill_triangle
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.fill_triangle
|
||||
sig: Screen.fill_triangle(x1, y1, x2, y2, x3, y3, color)
|
||||
tip: Draw a filled triangle.
|
||||
order: 14
|
||||
ns: Screen
|
||||
member: fill_triangle
|
||||
---
|
||||
|
||||
Fills the triangle with corners <code>(x1, y1)</code>, <code>(x2, y2)</code>, <code>(x3, y3)</code> in <code>color</code>, scanning its bounding box with an inside test. The building block for flat-shaded polygons and arrowheads.
|
||||
|
||||
```ludic
|
||||
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()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-line.md
Normal file
24
docs/language/screen/screen-line.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-line
|
||||
name: Screen.line
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.line
|
||||
sig: Screen.line(x1, y1, x2, y2, color)
|
||||
tip: Draw a straight line between two points.
|
||||
order: 10
|
||||
ns: Screen
|
||||
member: line
|
||||
---
|
||||
|
||||
Draws a one-pixel line from <code>(x1, y1)</code> to <code>(x2, y2)</code> in <code>color</code>, 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 <code>Render</code> phase.
|
||||
|
||||
```ludic
|
||||
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()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-sprite.md
Normal file
24
docs/language/screen/screen-sprite.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-sprite
|
||||
name: Screen.sprite
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.sprite
|
||||
sig: Screen.sprite(id, x, y)
|
||||
tip: Blit a sprite at its natural size.
|
||||
order: 15
|
||||
ns: Screen
|
||||
member: sprite
|
||||
---
|
||||
|
||||
Draws sprite <code>id</code> from the loaded sprite sheet at <code>(x, y)</code>, at its natural 16x16 size. Load the sheet once at start, then draw sprites each frame during <code>Render</code>.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
Screen.clear(Color.MidnightBlue)
|
||||
Screen.sprite(id: hero_frame, x: px, y: py)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-sprite_scaled.md
Normal file
24
docs/language/screen/screen-sprite_scaled.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-sprite_scaled
|
||||
name: Screen.sprite_scaled
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.sprite_scaled
|
||||
sig: Screen.sprite_scaled(id, x, y, scale)
|
||||
tip: Blit a sprite at an integer scale.
|
||||
order: 16
|
||||
ns: Screen
|
||||
member: sprite_scaled
|
||||
---
|
||||
|
||||
Draws sprite <code>id</code> at <code>(x, y)</code> enlarged by an integer <code>scale</code> (2 doubles it, and so on) — for chunky pixel-art zoom without blurring.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler DrawWorld phase Render {
|
||||
Screen.clear(Color.MidnightBlue)
|
||||
Screen.sprite_scaled(id: boss, x: bx, y: by, scale: 3)
|
||||
Screen.show()
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/screen/screen-triangle.md
Normal file
24
docs/language/screen/screen-triangle.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: screen-triangle
|
||||
name: Screen.triangle
|
||||
category: screen
|
||||
kind: namespace-method
|
||||
tokens: Screen.triangle
|
||||
sig: Screen.triangle(x1, y1, x2, y2, x3, y3, color)
|
||||
tip: Draw a triangle outline.
|
||||
order: 13
|
||||
ns: Screen
|
||||
member: triangle
|
||||
---
|
||||
|
||||
Draws the outline of the triangle with corners <code>(x1, y1)</code>, <code>(x2, y2)</code>, <code>(x3, y3)</code> in <code>color</code> — three lines. Use it for arrows, wedges, and vector shapes.
|
||||
|
||||
```ludic
|
||||
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()
|
||||
}
|
||||
}
|
||||
```
|
||||
21
docs/language/structure/kw-entry.md
Normal file
21
docs/language/structure/kw-entry.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: kw-entry
|
||||
name: entry
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: entry
|
||||
sig: entry { … }
|
||||
tip: The program's main block — runs once, top to bottom.
|
||||
order: 10
|
||||
---
|
||||
|
||||
<code>entry</code> 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 <code>handler</code>s driven by phases each frame, an <code>entry</code> 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 <code>program</code> with handlers for interactive games.
|
||||
|
||||
```ludic
|
||||
program Greet {
|
||||
entry {
|
||||
print("hello")
|
||||
print(2 + 2)
|
||||
}
|
||||
}
|
||||
```
|
||||
24
docs/language/structure/kw-new.md
Normal file
24
docs/language/structure/kw-new.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
id: kw-new
|
||||
name: new
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: new
|
||||
sig: new Record { … } new []T
|
||||
tip: Allocate a property record or an empty slice on the heap.
|
||||
order: 12
|
||||
---
|
||||
|
||||
<code>new</code> constructs a heap value and returns a reference to it. <code>new Record { field: value, … }</code> allocates a property (or struct) record, filling any omitted fields with their declared defaults; <code>new []T</code> allocates an empty growable slice of element type <code>T</code>, ready for <code>List.push</code>. Because both are references, passing one around shares the same underlying object. Use <code>new</code> for standalone data your code holds directly — as opposed to <code>spawn</code>, which creates a full ECS entity the world tracks and queries.
|
||||
|
||||
```ludic
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/language/structure/kw-public.md
Normal file
23
docs/language/structure/kw-public.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
id: kw-public
|
||||
name: public
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: public
|
||||
sig: scene Name start public { … }
|
||||
tip: Expose a declaration's lifecycle on the public event bus.
|
||||
order: 11
|
||||
---
|
||||
|
||||
<code>public</code> marks a <code>scene</code>, <code>layer</code>, or <code>event</code> as part of the game's public surface, so its lifecycle is emitted as named events other systems and mods can react to with <code>@On</code>. A <code>public</code> scene fires <code>scene_<Name>_enter</code> and <code>scene_<Name>_exit</code>; a <code>public</code> layer fires <code>layer_<Name>_show</code> / <code>_hide</code>. It is the declaration-level counterpart to the <code>@Public</code> 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.
|
||||
|
||||
```ludic
|
||||
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) }
|
||||
}
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue