refactor(runtime,packages,examples): named constants, package enums, idiom sweep

- runtime: HEADLESS_FRAME_PATH, STICK_DEADZONE / STICK_LEFT_X/Y, key and
  byte codes as char literals throughout (`k == 'w'`, `fill(rt_map, ' ', …)`)
- ludic.gameplay/stats: drop the duplicate `stat_field` (it answered "atk"
  for every build stat); Stats.base uses stats_field_name
- ludic.shooter: compare aim modes and fire patterns with AimMode.* and
  WeaponPattern.* instead of raw ints; STICK_RIGHT_X/Y
- ludic.npcai: DecisionMade / brain_set_state use AiState.*
- examples/games/menu.ludic uses Font.load / Ui.* with FONT_PATH and
  BACKDROP named; strings.ludic header says what it prints
- whole tree: `x = x + 1` → `x += 1` (single-term right-hand sides only),
  `0 - x` → `-x`, ASCII codes → char literals; every .ludic and every
  ```ludic fence reformatted with the fixed formatter (whitespace only)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-05 01:12:26 +03:00
parent e3e1bc7784
commit bf36bc8a8f
103 changed files with 1256 additions and 1252 deletions

View file

@ -19,8 +19,8 @@ program Hello {
# runs once per match, each property bound by name and self() giving that entity.
@Queries(these: [Pos, Vel])
handler Move phase FixedUpdate {
Pos.x = Pos.x + Vel.dx
Pos.y = Pos.y + Vel.dy
Pos.x += Vel.dx
Pos.y += Vel.dy
}
handler Report phase Update {

View file

@ -16,8 +16,8 @@ program WorldQuery {
var sum = 0
var e = world_query_next(H, 0)
while e >= 0 {
count = count + 1
sum = sum + world_get(e, H, hp)
count += 1
sum += world_get(e, H, hp)
e = world_query_next(H, e + 1)
}
print(count) # 2

View file

@ -19,10 +19,10 @@ program WorldScan {
var e = 0
while e < n {
if world_kind(e) == Um {
count = count + 1
sum = sum + world_get(e, H, hp)
count += 1
sum += world_get(e, H, hp)
}
e = e + 1
e += 1
}
print(count) # 2
print(sum) # 110

View file

@ -20,8 +20,8 @@ program Cancel {
# the realistic shape: only apply the effect when the decision isn't vetoed
var hp = 100
if emit BeforeHurt(amount: 8) == 0 { hp = hp - 8 } # allowed -> 92
if emit BeforeHurt(amount: 50) == 0 { hp = hp - 50 } # vetoed -> unchanged
if emit BeforeHurt(amount: 8) == 0 { hp -= 8 } # allowed -> 92
if emit BeforeHurt(amount: 50) == 0 { hp -= 50 } # vetoed -> unchanged
print(hp) # 92
}
}

View file

@ -17,7 +17,7 @@ program LayerEvents {
}
layer Ctrl {
handler Drive phase LateUpdate {
step = step + 1
step += 1
if step == 1 { disable layer Hud } # frame 1: hide -> Hidden 2, Draw stops
if step == 2 { enable layer Hud } # frame 2: show -> Shown 1, Draw resumes
if step == 3 { quit() } # frame 3: after Draw ran again

View file

@ -6,7 +6,7 @@ program ModEvents {
var total: int = 0
event Damage { amount: int = 0 }
@On(Damage) handler Native { print(amount) } # prints each hit
@On(Damage) handler Accum { total = total + amount } # sums them
@On(Damage) handler Accum { total += amount } # sums them
entry {
emit Damage(amount: 10) # Native prints 10

View file

@ -11,8 +11,8 @@ program ProgramEvents {
@Public @OnStart handler Boot { print(1) } # -> program_start
@Public @OnQuit handler Bye { print(2) } # -> program_quit
@On(program_start) handler ModInit { print(100) } # mod boots after the game
@On(program_quit) handler ModDone { print(200) } # mod tears down as the game exits
@On(program_start) handler ModInit { print(100) } # mod boots after the game
@On(program_quit) handler ModDone { print(200) } # mod tears down as the game exits
handler Run phase Update { quit() }
}

View file

@ -11,7 +11,7 @@ program Recurse {
event Ping { }
event Pong { }
@On(Ping) handler A { n = n + 1; emit Pong() } # each Ping deepens by one Pong
@On(Ping) handler A { n += 1; emit Pong() } # each Ping deepens by one Pong
@On(Pong) handler B { emit Ping() } # ...and each Pong by one Ping
entry {

View file

@ -8,7 +8,7 @@ program Scoped {
model Unit { Health }
var fires: int = 0
event Tick { }
@On(Tick) handler OnTick { for (Health) in query [Health, {Unit}] { fires = fires + 1 } }
@On(Tick) handler OnTick { for (Health) in query [Health, {Unit}] { fires += 1 } }
entry {
spawn Unit { Health { hp: 1 } }

View file

@ -27,13 +27,13 @@ handler Battle phase Update {
match menu_cursor {
KnightAct.Attack => {
let d = max(1, st.atk - e.def + Random.range(0, 4))
e.hp = e.hp - d
e.hp -= d
player_hit = d
}
KnightAct.Guard => { st.guard = 1; player_hit = 0 }
KnightAct.Item => {
if potions > 0 {
potions = potions - 1
potions -= 1
st.hp = min(st.maxhp, st.hp + 24)
player_hit = 24
} else { player_hit = 0 }
@ -55,7 +55,7 @@ handler Battle phase Update {
if k != 0 {
if e.hp <= 0 {
grant_xp(e.xp)
gold = gold + e.xp
gold += e.xp
if enemy_type == 2 { boss_down = 1; game_mode = 3; ack = 0 }
else { game_mode = 0 }
become KnightMenu
@ -78,12 +78,12 @@ handler Battle phase Update {
match menu_cursor {
MageAct.Attack => {
let d = max(1, st.atk + 5 - e.def + Random.range(0, 5))
e.hp = e.hp - d
e.hp -= d
player_hit = d
}
MageAct.Heal => {
if st.mp >= 4 {
st.mp = st.mp - 4
st.mp -= 4
for (t2, p2) in query [Stats, Party] {
if p2.slot == 0 { t2.hp = min(t2.maxhp, t2.hp + 16) }
}
@ -104,7 +104,7 @@ handler Battle phase Update {
if k != 0 {
if e.hp <= 0 {
grant_xp(e.xp)
gold = gold + e.xp
gold += e.xp
if enemy_type == 2 { boss_down = 1; game_mode = 3; ack = 0 }
else { game_mode = 0 }
become KnightMenu
@ -121,8 +121,8 @@ handler Battle phase Update {
if done == 0 {
if t3.hp > 0 {
var ed = max(1, e.atk - t3.def + Random.range(0, 4))
if t3.guard == 1 { ed = ed / 2; t3.guard = 0 }
t3.hp = t3.hp - ed
if t3.guard == 1 { ed /= 2; t3.guard = 0 }
t3.hp -= ed
enemy_hit = ed
done = 1
}
@ -130,7 +130,7 @@ handler Battle phase Update {
}
var alive = 0
for (t4) in query [Stats, {Party}] {
if t4.hp > 0 { alive = alive + 1 }
if t4.hp > 0 { alive += 1 }
}
if alive == 0 { game_mode = 2; ack = 0 }
else {

View file

@ -84,33 +84,33 @@ handler Control phase Input {
if tc != 'D' {
if tc != 'S' {
if tc != 'B' {
if tc != 'h' {
encounter = encounter - 1
if encounter <= 0 {
encounter = Random.range(4, 9)
var et = 0
if world_map == 1 { et = 1 }
if world_map == 0 {
if Random.chance(40) { et = 1 }
}
enemy_type = et
if et == 0 {
spawn Enemy {
Actor { kind: 6 }
Stats { hp: 18, maxhp: 18, mp: 0, maxmp: 0, atk: 8, def: 2, lvl: 1, xp: 7, guard: 0 }
if tc != 'h' {
encounter -= 1
if encounter <= 0 {
encounter = Random.range(4, 9)
var et = 0
if world_map == 1 { et = 1 }
if world_map == 0 {
if Random.chance(40) { et = 1 }
}
}
if et == 1 {
spawn Enemy {
Actor { kind: 7 }
Stats { hp: 30, maxhp: 30, mp: 0, maxmp: 0, atk: 11, def: 5, lvl: 1, xp: 15, guard: 0 }
enemy_type = et
if et == 0 {
spawn Enemy {
Actor { kind: 6 }
Stats { hp: 18, maxhp: 18, mp: 0, maxmp: 0, atk: 8, def: 2, lvl: 1, xp: 7, guard: 0 }
}
}
if et == 1 {
spawn Enemy {
Actor { kind: 7 }
Stats { hp: 30, maxhp: 30, mp: 0, maxmp: 0, atk: 11, def: 5, lvl: 1, xp: 15, guard: 0 }
}
}
game_mode = 1
turn_phase = 0
menu_cursor = 0
}
game_mode = 1
turn_phase = 0
menu_cursor = 0
}
}
}
}
}
@ -130,8 +130,8 @@ handler Shop phase Update {
if is_confirm(k) {
if menu_cursor == 0 {
if gold >= 10 {
gold = gold - 10
potions = potions + 1
gold -= 10
potions += 1
}
}
if menu_cursor == 1 { game_mode = 0 }

View file

@ -15,7 +15,7 @@ function walkable(x: int, y: int) -> bool {
let c = Map.tile(x, y)
return c == '.' or c == 'D' or c == 'r' or c == ',' or c == 'S' or c == 'B' or c == 'h'
}
function is_confirm(k: int) -> bool { return k == 32 or k == 10 or k == 13 }
function is_confirm(k: int) -> bool { return k == ' ' or k == '\n' or k == '\r' }
function enemy_name(et: int) -> string {
if et == 0 { return "SLIME" }
if et == 1 { return "SKELETON" }
@ -70,15 +70,15 @@ function build_dungeon(x: int) -> int {
function grant_xp(amount: int) -> int {
for (t) in query [Stats, {Party}] {
if t.hp > 0 {
t.xp = t.xp + amount
t.xp += amount
while t.xp >= t.lvl * 20 {
t.xp = t.xp - t.lvl * 20
t.lvl = t.lvl + 1
t.maxhp = t.maxhp + 8
t.lvl += 1
t.maxhp += 8
t.hp = t.maxhp
t.maxmp = t.maxmp + 2
t.atk = t.atk + 2
t.def = t.def + 1
t.maxmp += 2
t.atk += 2
t.def += 1
}
}
}

View file

@ -4,6 +4,11 @@
# and activation. The game only loads a font, opens the UI, and reads clicks.
program Menu {
# the macOS system font: the toolchain targets macOS today, and the sample keeps
# the repo free of a bundled font
const FONT_PATH: string = "/System/Library/Fonts/Supplemental/Arial.ttf"
const BACKDROP: int = 0x0e0e16
var title_font: int = 0
ui MainMenu {
@ -19,22 +24,22 @@ program Menu {
}
handler Boot phase Start {
title_font = font_load("/System/Library/Fonts/Supplemental/Arial.ttf")
ui_build()
ui_open(UI_MainMenu)
title_font = Font.load(FONT_PATH)
Ui.build()
Ui.open(UI_MainMenu)
}
handler Nav phase Update {
ui_tick(Input.key())
if ui_clicked(UI_NewGame) { ui_set_text(UI_NewGame, "Starting...") }
if ui_clicked(UI_Continue) { ui_set_text(UI_Continue, "No save") }
if ui_clicked(UI_Options) { ui_set_text(UI_Options, "Soon (TM)") }
if ui_clicked(UI_Quit) { quit() }
Ui.tick(Input.key())
if Ui.clicked(UI_NewGame) { Ui.set_text(UI_NewGame, "Starting...") }
if Ui.clicked(UI_Continue) { Ui.set_text(UI_Continue, "No save") }
if Ui.clicked(UI_Options) { Ui.set_text(UI_Options, "Soon (TM)") }
if Ui.clicked(UI_Quit) { quit() }
}
handler Draw phase Render {
Screen.clear(0x0e0e16)
ui_render()
Screen.clear(BACKDROP)
Ui.render()
Screen.show()
}
}

View file

@ -34,9 +34,9 @@ program NpcAiDemo {
@OnSpawn(Dummy) handler DInit { }
var spotted: int = 0
@On(TargetSpotted) handler OnSpot { spotted = spotted + 1 }
@On(TargetSpotted) handler OnSpot { spotted += 1 }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
function gx(e: int) -> int { let P = World.prop_id("Position"); return World.get(e, P, World.field_id(P, "x")) }
function gy(e: int) -> int { let P = World.prop_id("Position"); return World.get(e, P, World.field_id(P, "y")) }
@ -52,7 +52,7 @@ program NpcAiDemo {
let PBr = World.prop_id("Brain")
var e = World.query_next(PS, 0)
while e >= 0 { if World.has(e, PBr) == 0 { return e }; e = World.query_next(PS, e + 1) }
return 0 - 1
return -1
}
entry {
@ -102,7 +102,7 @@ program NpcAiDemo {
# ---- E. companion follower advances toward its leader ------------------
spawn Ally { Position { x: 40, y: 40 }, Collider { w: 12, h: 12 },
TopDown { move_speed: 3 }, Faction { id: 2 }, Stats { hp: 30, max_hp: 30 },
Brain { model: 0 }, Follower { leader: 0 - 1, distance: 24 } }
Brain { model: 0 }, Follower { leader: -1, distance: 24 } }
# the ally is the Brain+Follower entity that isn't the enemy
var ally = World.query_next(PBr, 0)
while (ally >= 0) and (World.has(ally, World.prop_id("Follower")) == 0) { ally = World.query_next(PBr, ally + 1) }

View file

@ -33,14 +33,14 @@ program PlatformerDemo {
@OnSpawn(Wall) handler WallInit { }
var jumps: int = 0
@On(JumpPerformed) handler CountJumps { jumps = jumps + 1 }
@On(JumpPerformed) handler CountJumps { jumps += 1 }
# a game rule: forbid the air (double) jump unless a "power" flag is set — pure
# lever-4 veto, no controller edit.
var have_power: int = 0
var vetoed_air: int = 0
@On(JumpRequested) handler GateAir { if (kind == 2) and (have_power == 0) { vetoed_air = 1; cancel } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
function pf_coyote(e: int) -> int {
let P = World.prop_id("Platformer")
@ -78,7 +78,7 @@ program PlatformerDemo {
Platformer.jump(h)
var top = World.get(h, PP, fy)
var i = 0
while i < 20 { tick_fixed(); let y = World.get(h, PP, fy); if y < top { top = y }; i = i + 1 }
while i < 20 { tick_fixed(); let y = World.get(h, PP, fy); if y < top { top = y }; i += 1 }
let rise = floor_y - top # px risen at apex (target 64 = 4 tiles)
print(bi((rise >= 56) and (rise <= 72))) # 1
Platformer.hold(h, 0)

View file

@ -34,13 +34,13 @@ program PlatformerScaffolding {
@OnSpawn(Spike) handler SI { }
var picked: int = 0
@On(ItemPickedUp) handler OnPick { picked = picked + 1 }
@On(ItemPickedUp) handler OnPick { picked += 1 }
var sprang: int = 0
@On(Sprang) handler OnSpring { sprang = sprang + 1 }
@On(Sprang) handler OnSpring { sprang += 1 }
var hurt: int = 0
@On(Hurt) handler OnHurt { hurt = hurt + 1 }
@On(Hurt) handler OnHurt { hurt += 1 }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
entry {

View file

@ -41,13 +41,13 @@ program RpgDemo {
var locked: int = 0
@On(MoveRequested) handler LockDoor { if (locked == 1) and (dx == 1) { cancel } }
var entered: int = 0
@On(TileEntered) handler CountEnter { entered = entered + 1 }
@On(TileEntered) handler CountEnter { entered += 1 }
var talked: int = 0
@On(Interacted) handler OnTalk { if target >= 0 { talked = talked + 1 } }
@On(Interacted) handler OnTalk { if target >= 0 { talked += 1 } }
var quest_done: int = 0
@On(QuestCompleted) handler OnQuestDone { quest_done = quest_done + 1 }
@On(QuestCompleted) handler OnQuestDone { quest_done += 1 }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
function px(e: int) -> int { let P = World.prop_id("Position"); return World.get(e, P, World.field_id(P, "x")) }
function py(e: int) -> int { let P = World.prop_id("Position"); return World.get(e, P, World.field_id(P, "y")) }
@ -112,7 +112,7 @@ program RpgDemo {
let before = Inventory.count(h, "herb")
print(bi(Craft.can(h, "potion") == 1)) # 1
print(bi(Craft.make(h, "potion") == 1)) # 1 — consumed 2, produced 1
print(bi(Inventory.count(h, "herb") == before - 1)) # 1 — net -2 + 1
print(bi(Inventory.count(h, "herb") == before - 1)) # 1 — net -2 + 1
# ================= Module D: quests =================
Quest.define("hunt")
@ -126,7 +126,7 @@ program RpgDemo {
let dn0 = Dialog.node("elder", "hello")
let dn1 = Dialog.node("elder", "farewell")
Dialog.choice(dn0, "greet", dn1)
Dialog.choice(dn1, "leave", 0 - 1)
Dialog.choice(dn1, "leave", -1)
Dialog.start(h, "elder")
print(bi(Dialog.line(h) == "hello")) # 1 — entry node text
Dialog.choose(h, 0)

View file

@ -31,20 +31,20 @@ program ShooterDemo {
@OnSpawn(Nest) handler NInit { }
var spawn_reqs: int = 0
@On(SpawnRequested) handler OnSpawnReq { spawn_reqs = spawn_reqs + 1 }
@On(SpawnRequested) handler OnSpawnReq { spawn_reqs += 1 }
var waves_cleared: int = 0
@On(WaveCleared) handler OnWaveClear { waves_cleared = waves_cleared + 1 }
@On(WaveCleared) handler OnWaveClear { waves_cleared += 1 }
var spawned: int = 0
@On(ProjectileSpawned) handler CountSpawn { spawned = spawned + 1 }
@On(ProjectileSpawned) handler CountSpawn { spawned += 1 }
var hits: int = 0
@On(ProjectileHit) handler CountHit { hits = hits + 1 }
@On(ProjectileHit) handler CountHit { hits += 1 }
# reap the dead (the realistic policy: a Died body leaves the world) so stale
# corpses do not sit in the faction/targeting scans.
var deaths: int = 0
@On(Died) handler Reap { deaths = deaths + 1; despawn e }
@On(Died) handler Reap { deaths += 1; despawn e }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
function hp_of(e: int) -> int {
let P = World.prop_id("Stats")
@ -57,7 +57,7 @@ program ShooterDemo {
let PT = World.prop_id("TopDown")
var e = World.query_next(PS, 0)
while e >= 0 { if World.has(e, PT) == 0 { return e }; e = World.query_next(PS, e + 1) }
return 0 - 1
return -1
}
# a mob still alive (hp > 0) — the dead one from an earlier test stays in the world.
function find_live_mob() -> int {
@ -69,7 +69,7 @@ program ShooterDemo {
if (World.has(e, PT) == 0) and (World.get(e, PS, fhp) > 0) { return e }
e = World.query_next(PS, e + 1)
}
return 0 - 1
return -1
}
entry {
@ -138,7 +138,7 @@ program ShooterDemo {
var f = 0
while (deaths == d0) and (f < 400) {
TopDown.fire(g, 1); tick_fixed(); TopDown.fire(g, 0); tick_fixed()
f = f + 1
f += 1
}
print(bi(deaths == d0 + 1)) # 1 — enemy destroyed (Died fired)

View file

@ -79,7 +79,7 @@ program Snake {
handler Move phase Update {
if mode == MODE_PLAY {
tick = tick + 1
tick += 1
if tick >= 6 {
tick = 0
# remember the tail cell, then drag every segment onto the one ahead of it
@ -102,10 +102,10 @@ program Snake {
ate = 0
for (ph, sh) in query [Pos, Seg] {
if sh.order == 0 {
if direction == DIR_UP { ph.y = ph.y - 1 }
if direction == DIR_DOWN { ph.y = ph.y + 1 }
if direction == DIR_LEFT { ph.x = ph.x - 1 }
if direction == DIR_RIGHT { ph.x = ph.x + 1 }
if direction == DIR_UP { ph.y -= 1 }
if direction == DIR_DOWN { ph.y += 1 }
if direction == DIR_LEFT { ph.x -= 1 }
if direction == DIR_RIGHT { ph.x += 1 }
if ph.x < 0 { mode = MODE_OVER }
if ph.x >= GRID_W { mode = MODE_OVER }
if ph.y < 0 { mode = MODE_OVER }
@ -124,9 +124,9 @@ program Snake {
}
}
if ate == 1 {
score = score + 1
score += 1
spawn Body { Seg { order: length }; Pos { x: tail_x, y: tail_y } }
length = length + 1
length += 1
food_x = Random.range(low: 0, high: GRID_W - 1)
food_y = Random.range(low: 0, high: GRID_H - 1)
}

View file

@ -59,6 +59,6 @@ program AnimEcs {
# run the Update phase n times; the engine systems advance one tick each call.
function tick_n(n: int) -> void {
var i = 0
while i < n { tick_fixed(); i = i + 1 }
while i < n { tick_fixed(); i += 1 }
}
}

View file

@ -16,7 +16,7 @@ program AnimSugar {
model Sprite { SpriteAnim }
model Mover { Motion }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
entry {

View file

@ -25,7 +25,7 @@ program AtlasDemo {
let d = Sprite.define("hero", sheet, 2, 3) # name a cell
print(bi(Sprite.named("hero") == d)) # 1 — looked up by name
print(bi(Assets.get("hero") == d)) # 1 — Assets.get is the same lookup
print(bi(Sprite.named("nope") == (0 - 1))) # 1 — an unknown name is -1
print(bi(Sprite.named("nope") == (-1))) # 1 — an unknown name is -1
let whole = Assets.image("assets/kenney/tiny-dungeon/Tiles/tile_0096.png")
print(Sprite.width(whole)) # 16 — a whole tile as one sprite
@ -34,7 +34,7 @@ program AtlasDemo {
Screen.clear(0)
Sprite.draw(whole, 10, 10)
var n = 0
for yy in 10 .. 26 { for xx in 10 .. 26 { if Screen.pixel(xx, yy) != 0 { n = n + 1 } } }
for yy in 10 .. 26 { for xx in 10 .. 26 { if Screen.pixel(xx, yy) != 0 { n += 1 } } }
print(bi(n > 0)) # 1 — the sprite drew content
}
}

View file

@ -19,7 +19,7 @@ program Combat {
@export function damage(attack: int, armour: int, roll: int) -> int {
var raw = attack - armour
if raw < 1 { raw = 1 }
if roll == 20 { raw = raw * CRIT_MULT }
if roll == 20 { raw *= CRIT_MULT }
return raw
}
@ -29,8 +29,8 @@ program Combat {
var left = hp
var n = 0
while left > 0 {
left = left - damage(attack, armour, 0)
n = n + 1
left -= damage(attack, armour, 0)
n += 1
}
return n
}

View file

@ -30,7 +30,7 @@ program ComponentAccess {
Hero.of(player).iframes -= 5
print(Hero.of(player).iframes) # 15
let hero = Hero.of(player)
hero.roll_frames = hero.roll_frames + 1
hero.roll_frames += 1
print(Hero.of(player).roll_frames) # 10
print(Position.of(player).x + Position.of(1).x) # 33
print(Hero.has(player)) # 1

View file

@ -12,7 +12,7 @@ program Containers {
if Dict.get(bank, "gold") == 150 { print(1) }
if Dict.get(bank, "wood") == 50 { print(2) }
if Dict.get(bank, "stone") == 0 { print(3) } # absent -> 0
if Dict.get_or(bank, "stone", 0 - 1) == 0 - 1 { print(4) }
if Dict.get_or(bank, "stone", -1) == -1 { print(4) }
if Dict.has(bank, "wood") { print(5) }
if Dict.size(bank) == 2 { print(6) }
Dict.remove(bank, "wood")
@ -21,7 +21,7 @@ program Containers {
# grow past the initial capacity, then read back
var i = 0
while i < 100 { Dict.set(bank, "k" + string(i), i * 2); i = i + 1 }
while i < 100 { Dict.set(bank, "k" + string(i), i * 2); i += 1 }
if Dict.get(bank, "k42") == 84 { print(9) }
if Dict.size(bank) == 101 { print(10) } # gold + k0..k99
if len(Dict.keys(bank)) == 101 { print(11) }

View file

@ -14,7 +14,7 @@ program CoreComponents {
# subclassing, just another property on the same model.
property Health { hp: int = 0 }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
entry {
let pp = World.prop_id("Position")

View file

@ -16,7 +16,7 @@ program CoverageSpec {
return 1
}
if n < 0 {
return 0 - 1
return -1
}
return 0
}
@ -35,7 +35,7 @@ program CoverageSpec {
test "sign covers every branch" {
expect_eq(sign(7), 1)
expect_eq(sign(0 - 3), 0 - 1)
expect_eq(sign(-3), -1)
expect_eq(sign(0), 0)
}

View file

@ -21,7 +21,7 @@ program Errors {
entry {
print(checked_div(10, 2)) # 5
print(clampi(15, 0, 10)) # 10 (above hi)
print(clampi(0 - 3, 0, 10)) # 0 (below lo)
print(clampi(-3, 0, 10)) # 0 (below lo)
print(clampi(7, 0, 10)) # 7 (in range)
print(1) # reached: every assertion held
}

View file

@ -24,9 +24,9 @@ program GameplayFoundation {
}
var deaths: int = 0
@On(Died) handler Tally { deaths = deaths + 1 }
@On(Died) handler Tally { deaths += 1 }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
entry {
@ -74,7 +74,7 @@ program GameplayFoundation {
# finish it off with sub-100 hits so the veto never blocks the kill
var guard = 0
while (World.get(t, PS, fhp) > 0) and (guard < 100) { Combat.damage(t, a, 90); guard = guard + 1 }
while (World.get(t, PS, fhp) > 0) and (guard < 100) { Combat.damage(t, a, 90); guard += 1 }
print(deaths) # 1
quit()

View file

@ -45,12 +45,12 @@ program Grid {
let dx = p[i].x - p[i - 1].x
let dy = p[i].y - p[i - 1].y
var ad = dx
if ad < 0 { ad = 0 - ad }
if ad < 0 { ad = -ad }
var ay = dy
if ay < 0 { ay = 0 - ay }
if ay < 0 { ay = -ay }
if ad + ay != 1 { ok = false }
if Grid.blocked(p[i].x, p[i].y, '#') { ok = false }
i = i + 1
i += 1
}
if ok { print(9) }
@ -58,7 +58,7 @@ program Grid {
if len(Grid.a_star(1, 1, 3, 2, '#')) == 0 { print(10) } # (3,2) is a wall
if Grid.blocked(3, 2, '#') { print(11) }
if not Grid.blocked(1, 1, '#') { print(12) }
if Grid.blocked(0 - 1, 0, '#') { print(13) } # out of bounds
if Grid.blocked(-1, 0, '#') { print(13) } # out of bounds
}
handler Run phase Update { quit() }

View file

@ -15,6 +15,6 @@ program InputAuto {
var hits: int = 0
@OnStart handler Boot { Input.action("go", 'l') } # 'l' == 108
handler Sense phase Update { if Input.active("go") { hits = hits + 1 } }
handler Sense phase Update { if Input.active("go") { hits += 1 } }
@OnQuit handler Report { print(hits) }
}

View file

@ -19,9 +19,9 @@ program InputEdge {
handler In phase Update {
var v = 0
if Input.key_down('k') { v = v + 1 } # held
if Input.key_pressed('k') { v = v + 10 } # went down this frame
if Input.key_released('k') { v = v + 100 } # went up this frame
if Input.key_down('k') { v += 1 } # held
if Input.key_pressed('k') { v += 10 } # went down this frame
if Input.key_released('k') { v += 100 } # went up this frame
print(v)
}
}

View file

@ -69,7 +69,7 @@ program LightTiers {
dark()
Light.clear_normals()
Light.normal(18, 30, 8, 8, 0.7, 0.0) # this patch faces right (toward the light)
Light.normal(46, 30, 8, 8, 0.0 - 0.7, 0.0) # this patch faces left (away)
Light.normal(46, 30, 8, 8, 0.0 - 0.7, 0.0) # this patch faces left (away)
Light.point(32, 34, 44, 16777215, 1.0) # a light centred between the patches
let toward = lum(22, 34) # patch facing the light
let away = lum(48, 34) # patch facing away

View file

@ -14,7 +14,7 @@ program Numeric {
if Huge.str(Huge.add(Huge.from(1000000000), Huge.from(1))) == "1.00e9" { print(6) } # +1 negligible
if Huge.cmp(Huge.from(1000000000), Huge.from(1000000)) == 1 { print(7) }
if Huge.exp(Huge.mul(Huge.from(1000000), Huge.from(1000000))) == 12 { print(8) }
if Huge.str(Huge.from(0 - 42)) == "-4.20e1" { print(9) }
if Huge.str(Huge.from(-42)) == "-4.20e1" { print(9) }
if Huge.str(Huge.from(0)) == "0" { print(10) }
# --- Angle: wrapping radians (built on the deterministic Math.*) ---

View file

@ -12,7 +12,7 @@ program OptionResult {
}
# a fallible parse — a value or a message
function to_digit(c: int) -> result {
if c >= 48 and c <= 57 { return ok(c - 48) } # '0'..'9'
if c >= '0' and c <= '9' { return ok(c - 48) } # '0'..'9'
return err("not a digit")
}
entry {
@ -20,20 +20,20 @@ program OptionResult {
let a = some(42)
if is_some(a) { print(1) }
if not is_none(a) { print(2) }
if unwrap_or(a, 0 - 1) == 42 { print(3) }
if unwrap_or(a, -1) == 42 { print(3) }
let b = none()
if is_none(b) { print(4) }
if unwrap_or(b, 0 - 1) == 0 - 1 { print(5) }
if unwrap_or(b, -1) == -1 { print(5) }
if unwrap_or(slot_of(5), 0) == 50 { print(6) }
if is_none(slot_of(0 - 1)) { print(7) }
if is_none(slot_of(-1)) { print(7) }
# --- result + try/else ---
if is_ok(to_digit(55)) { print(8) }
if is_err(to_digit(120)) { print(9) }
let d = try to_digit(55) else { 0 } # '7' -> 7
if d == 7 { print(10) }
let bad = try to_digit(120) else { 0 - 1 } # not a digit -> fallback
if bad == 0 - 1 { print(11) }
let bad = try to_digit(120) else { -1 } # not a digit -> fallback
if bad == -1 { print(11) }
let msg = try to_digit(120) else { print(12); 0 } # else binds `error`
}
}

View file

@ -20,7 +20,7 @@ program Physics {
hit: int = 0, entered: int = 0, exited: int = 0
}
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
# despawn every live entity carrying a property, so each scenario starts clean.
@ -65,7 +65,7 @@ program Physics {
# re-apply the run velocity each frame, the way a controller drives it from
# held input — so hit_wall stays asserted while pressed against the wall.
var bi2 = 0
while bi2 < 80 { World.set(a, pb, f_vx, fixed(4)); tick_fixed(); bi2 = bi2 + 1 }
while bi2 < 80 { World.set(a, pb, f_vx, fixed(4)); tick_fixed(); bi2 += 1 }
print(World.get(a, pp, f_x)) # 284 — right edge stops at the wall (300)
print(World.get(a, pb, f_hw)) # 1 — hit_wall while pressed
print(bi(World.get(a, pb, f_vx) == 0)) # 1 — the sweep zeroed it this frame
@ -93,7 +93,7 @@ program Physics {
spawn Plat { Position { x: 400, y: 150 }, Collider { w: 64, h: 8, one_way: 1 } }
spawn Runner { Position { x: 405, y: 60 }, Body { gravity: fixed(1), max_fall: fixed(6) }, Collider { w: 16, h: 16 } }
let e1 = World.query_next(pb, 0)
spawn Riser { Position { x: 430, y: 200 }, Body { vy: fixed(0 - 4), policy: 1 }, Collider { w: 16, h: 16 } }
spawn Riser { Position { x: 430, y: 200 }, Body { vy: fixed(-4), policy: 1 }, Collider { w: 16, h: 16 } }
var e2 = World.query_next(pb, 0)
if e2 == e1 { e2 = World.query_next(pb, e1 + 1) }
tick_n(20)

View file

@ -49,7 +49,7 @@ program PhysicsTiles {
spawn Runner { Position { x: 32, y: 16 }, Body { gravity: fixed(1), max_fall: fixed(6) }, Collider { w: 16, h: 16 } }
var a = World.query_next(pb, 0)
var i = 0
while i < 80 { tick_fixed(); i = i + 1 }
while i < 80 { tick_fixed(); i += 1 }
print(World.get(a, pp, f_y)) # 112 — feet rest on the tile floor top (128)
print(World.get(a, pb, f_grd)) # 1
@ -58,7 +58,7 @@ program PhysicsTiles {
spawn Runner { Position { x: 16, y: 112 }, Body { gravity: fixed(1), max_fall: fixed(6) }, Collider { w: 16, h: 16 } }
a = World.query_next(pb, 0)
i = 0
while i < 60 { World.set(a, pb, f_vx, fixed(4)); tick_fixed(); i = i + 1 }
while i < 60 { World.set(a, pb, f_vx, fixed(4)); tick_fixed(); i += 1 }
print(World.get(a, pp, f_x)) # 80 — right edge stops at the wall column (96)
print(World.get(a, pb, f_hw)) # 1
@ -67,16 +67,16 @@ program PhysicsTiles {
spawn Runner { Position { x: 176, y: 16 }, Body { gravity: fixed(1), max_fall: fixed(6) }, Collider { w: 16, h: 16 } }
a = World.query_next(pb, 0)
i = 0
while i < 40 { tick_fixed(); i = i + 1 }
while i < 40 { tick_fixed(); i += 1 }
print(World.get(a, pp, f_y)) # 48 — feet on the one-way top (64)
print(World.get(a, pb, f_grd)) # 1
# ---- T3b. rise straight up through the one-way tile --------------------
clear()
spawn Riser { Position { x: 176, y: 120 }, Body { vy: fixed(0 - 4), policy: 1 }, Collider { w: 16, h: 16 } }
spawn Riser { Position { x: 176, y: 120 }, Body { vy: fixed(-4), policy: 1 }, Collider { w: 16, h: 16 } }
a = World.query_next(pb, 0)
i = 0
while i < 15 { tick_fixed(); i = i + 1 }
while i < 15 { tick_fixed(); i += 1 }
print(World.get(a, pp, f_y)) # 60 — passed through (120 - 4*15), above the platform
quit()

View file

@ -52,7 +52,7 @@ program Query {
let near0 = Query.within(P, P, 0, 0, 0, cx, cy)
if len(near0) == 1 { print(14) } # only e0 at (0,0)
if World.get(near0[0], P, cx) == 0 { print(15) } # and it is e0
if len(Query.within(G, P, 0, 0, 100, cx, cy)) == 0 { print(16) } # empty
if len(Query.within(G, P, 0, 0, 100, cx, cy)) == 0 { print(16) } # empty
if len(Query.within(K, P, 0, 0, 6, cx, cy)) == 2 { print(17) } # Mobs e0, e2 (filter K, pos P)
if World.get(Query.nearest(P, P, cx, cy, 20, 20), P, cx) == 20 { print(18) } # e4

View file

@ -52,11 +52,11 @@ program Reflect {
let fc = Reflect.field_count(p)
var f = 0
while f < fc {
total = total + Reflect.get(e, p, f)
f = f + 1
total += Reflect.get(e, p, f)
f += 1
}
}
p = p + 1
p += 1
}
if total == 327697 { print(17) } # 7 + 10 + 131072 + 196608

View file

@ -42,7 +42,7 @@ program Regex {
var tags = ""
var cur = Regex.exec("#a #bb #ccc", re)
while Regex.ok(cur) {
tags = tags + Regex.group(cur, 1)
tags += Regex.group(cur, 1)
cur = Regex.next("#a #bb #ccc", re, Regex.end(cur, 0))
}
if tags == "abbccc" { print(16) }

View file

@ -32,7 +32,7 @@ program TestingSpec {
test "loops and locals work in a test" {
var sum = 0
for i in 0 .. 5 { sum = sum + i } # 0+1+2+3+4 = 10
for i in 0 .. 5 { sum += i } # 0+1+2+3+4 = 10
expect_eq(sum, 10)
}

View file

@ -26,7 +26,7 @@ program TiledP05 {
# --- four layers, every encoding decodes to the SAME 16-GID array ---
let layers = Value.get(m, "layers")
if Value.count(layers) == 4 { print(8) }
let FLIP = 0 - 2147483647 # 0x80000001 as i32
let FLIP = -2147483647 # 0x80000001 as i32
var same = 1
var li = 0
while li < 4 {
@ -35,7 +35,7 @@ program TiledP05 {
if Value.as_int(Value.at(data, 0)) != 1 { same = 0 }
if Value.as_int(Value.at(data, 14)) != 15 { same = 0 }
if Value.as_int(Value.at(data, 15)) != FLIP { same = 0 }
li = li + 1
li += 1
}
if same == 1 { print(9) }
# the CSV path and the gzip path agree on the flip-flagged GID
@ -61,9 +61,9 @@ program TiledP05 {
var gi = 0
while gi < 16 {
if Value.as_int(Value.at(jdata, gi)) != Value.as_int(Value.at(csvdata, gi)) { jsame = 0 }
gi = gi + 1
gi += 1
}
jli = jli + 1
jli += 1
}
if jsame == 1 { print(16) }

View file

@ -18,9 +18,9 @@ program TiledP1 {
var x = 0
while x < 320 {
h = h * 31 + rt_get_px(x, y)
x = x + 4
x += 4
}
y = y + 4
y += 4
}
return h
}
@ -56,7 +56,7 @@ program TiledP1 {
# --- flips draw with the correct orientation ---
# find a horizontally-asymmetric tile so the mirror test is meaningful
var chosen = 0 - 1
var chosen = -1
var t = 0
while t < 132 {
rt_clear(0)
@ -67,12 +67,12 @@ program TiledP1 {
var xx = 0
while xx < 8 {
if rt_get_px(xx, yy) != rt_get_px(15 - xx, yy) { asym = 1 }
xx = xx + 1
xx += 1
}
yy = yy + 1
yy += 1
}
if asym == 1 and chosen < 0 { chosen = t }
t = t + 1
t += 1
}
if chosen >= 0 { print(12) }
@ -88,9 +88,9 @@ program TiledP1 {
while xx2 < 16 {
if rt_get_px(xx2, yy2) != rt_get_px(31 - xx2, yy2) { mirror = 0 } # H-flip mirrors
if rt_get_px(xx2, yy2) != rt_get_px(15 - xx2, yy2) { diff = 1 } # the tile isn't symmetric
xx2 = xx2 + 1
xx2 += 1
}
yy2 = yy2 + 1
yy2 += 1
}
if mirror == 1 { print(13) }
if diff == 1 { print(14) }

View file

@ -23,16 +23,16 @@ program TiledP2 {
var n = 0
var y = 0
while y < 10 { var x = 0
while x < 16 { if rt_tile(x, y) == 35 { n = n + 1 }; x = x + 1 }
y = y + 1 }
while x < 16 { if rt_tile(x, y) == 35 { n += 1 }; x += 1 }
y += 1 }
return n
}
function grid_hash() -> int {
var h = 0
var y = 0
while y < 10 { var x = 0
while x < 16 { h = h * 31 + rt_tile(x, y); x = x + 1 }
y = y + 1 }
while x < 16 { h = h * 31 + rt_tile(x, y); x += 1 }
y += 1 }
return h
}
@ -75,7 +75,7 @@ program TiledP2 {
let f_grd = World.field_id(pb, "on_ground")
let a = World.query_next(pb, 0)
var i = 0
while i < 80 { tick_fixed(); i = i + 1 }
while i < 80 { tick_fixed(); i += 1 }
if World.get(a, pp, f_y) == 112 { print(13) } # feet rest on the tile floor top (128)
if World.get(a, pb, f_grd) == 1 { print(14) }
}

View file

@ -13,8 +13,8 @@ program TiledP3 {
var h = 0
var y = 0
while y < 64 { var x = 0
while x < 64 { h = h * 31 + rt_get_px(x, y); x = x + 2 }
y = y + 2 }
while x < 64 { h = h * 31 + rt_get_px(x, y); x += 2 }
y += 2 }
return h
}
@ -38,13 +38,13 @@ program TiledP3 {
# --- tile object parsed from the object layer ---
let tree = Tiled.tree(m)
var oglayer = Value.null()
var oglayer = Value.null ()
let layers = Value.get(tree, "layers")
var li = 0
while li < Value.count(layers) {
let L = Value.at(layers, li)
if Value.as_str(Value.get(L, "type")) == "objectgroup" { oglayer = L }
li = li + 1
li += 1
}
let objs = Value.get(oglayer, "objects")
if Value.count(objs) == 1 { print(8) }
@ -62,7 +62,7 @@ program TiledP3 {
# --- tile objects draw with correct GID flips (mirror test) ---
# find an asymmetric tile, draw it normal vs H-flipped as a tile object
var chosen = 0 - 1
var chosen = -1
var t = 0
while t < 132 {
rt_clear(0)
@ -70,10 +70,10 @@ program TiledP3 {
var asym = 0
var yy = 0
while yy < 16 { var xx = 0
while xx < 8 { if rt_get_px(xx, yy) != rt_get_px(15 - xx, yy) { asym = 1 }; xx = xx + 1 }
yy = yy + 1 }
while xx < 8 { if rt_get_px(xx, yy) != rt_get_px(15 - xx, yy) { asym = 1 }; xx += 1 }
yy += 1 }
if asym == 1 and chosen < 0 { chosen = t }
t = t + 1
t += 1
}
rt_clear(0)
tmap_draw_gid(m, 1 + chosen, 0, 0)
@ -81,8 +81,8 @@ program TiledP3 {
var mirror = 1
var yy2 = 0
while yy2 < 16 { var xx2 = 0
while xx2 < 16 { if rt_get_px(xx2, yy2) != rt_get_px(31 - xx2, yy2) { mirror = 0 }; xx2 = xx2 + 1 }
yy2 = yy2 + 1 }
while xx2 < 16 { if rt_get_px(xx2, yy2) != rt_get_px(31 - xx2, yy2) { mirror = 0 }; xx2 += 1 }
yy2 += 1 }
if mirror == 1 and chosen >= 0 { print(12) }
# --- the design's named fixture: rpg/beach_tileset.tsx (33 animations) ---
@ -94,8 +94,8 @@ program TiledP3 {
while ti < Value.count(tiles) {
let tt = Value.at(tiles, ti)
let an = Value.get(tt, "animation")
if Value.count(an) > 0 { nanim = nanim + 1; nframes = nframes + Value.count(an) }
ti = ti + 1
if Value.count(an) > 0 { nanim += 1; nframes += Value.count(an) }
ti += 1
}
if nanim == 33 { print(13) }
if nframes == 131 { print(14) }

View file

@ -51,11 +51,11 @@ program TiledP4 {
# --- the design's named fixture: orthogonal-outside.tmx object shapes ---
let og = Tiled.load("assets/tiled-fixtures/orthogonal-outside.tmx")
var olayer = 0 - 1
var olayer = -1
var oli = 0
while oli < Tiled.layer_count(og) {
if Tiled.object_count(og, oli) > 0 { olayer = oli }
oli = oli + 1
oli += 1
}
var nell = 0
var npt = 0
@ -64,11 +64,11 @@ program TiledP4 {
var oi = 0
while oi < Tiled.object_count(og, olayer) {
let sh = Tiled.object_shape(Tiled.object(og, olayer, oi))
if sh == "ellipse" { nell = nell + 1 }
if sh == "point" { npt = npt + 1 }
if sh == "polygon" { npg = npg + 1 }
if sh == "polyline" { npl = npl + 1 }
oi = oi + 1
if sh == "ellipse" { nell += 1 }
if sh == "point" { npt += 1 }
if sh == "polygon" { npg += 1 }
if sh == "polyline" { npl += 1 }
oi += 1
}
if nell == 1 and npt == 1 and npg == 1 { print(17) }
if npl == 2 { print(18) }

View file

@ -13,7 +13,7 @@ program TiledP5 {
let iso = Tiled.load("assets/tiled-fixtures/isometric_grass_and_water.tmx")
if Value.as_str(Value.get(Tiled.tree(iso), "orientation")) == "isometric" { print(1) }
if Tiled.cell_x(iso, 1, 0) == 32 { print(2) } # (1-0)*64/2
if Tiled.cell_x(iso, 0, 1) == 0 - 32 { print(3) } # (0-1)*64/2
if Tiled.cell_x(iso, 0, 1) == -32 { print(3) } # (0-1)*64/2
if Tiled.cell_y(iso, 1, 1) == 32 { print(4) } # (1+1)*32/2
if Tiled.cell_y(iso, 2, 0) == 32 { print(5) } # (2+0)*32/2
@ -23,8 +23,8 @@ program TiledP5 {
var cy = 0
while cy < Tiled.height(iso) and g == 0 {
var cx = 0
while cx < Tiled.width(iso) and g == 0 { g = Tiled.gid(iso, 0, cx, cy); cx = cx + 1 }
cy = cy + 1
while cx < Tiled.width(iso) and g == 0 { g = Tiled.gid(iso, 0, cx, cy); cx += 1 }
cy += 1
}
let r = Tiled.resolve(iso, g)
if r.tileset == 0 and r.local >= 0 { print(6) }
@ -48,8 +48,8 @@ program TiledP5 {
var yy = 0
while yy < 32 and painted == 0 {
var xx = 0
while xx < 32 and painted == 0 { if rt_get_px(xx, yy) != 0 { painted = 1 }; xx = xx + 1 }
yy = yy + 1
while xx < 32 and painted == 0 { if rt_get_px(xx, yy) != 0 { painted = 1 }; xx += 1 }
yy += 1
}
if painted == 1 { print(14) }
}

View file

@ -40,7 +40,7 @@ program TiledP6 {
var i = 0
while i < Value.count(csv) {
if Value.as_int(Value.at(csv, i)) != Value.as_int(Value.at(zstd, i)) { same = 0 }
i = i + 1
i += 1
}
if same == 1 { print(12) }
}

View file

@ -8,7 +8,7 @@
program WorldBounds {
import "ludic.core/components.ludic"
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i = i + 1 } }
function tick_n(n: int) -> void { var i = 0; while i < n { tick_fixed(); i += 1 } }
function bi(b: bool) -> int { if b { return 1 }; return 0 }
function clear_all() -> void {
@ -57,7 +57,7 @@ program WorldBounds {
tick_n(1)
var live = 0
var q = World.query_next(PBody, 0)
while q >= 0 { live = live + 1; q = World.query_next(PBody, q + 1) }
while q >= 0 { live += 1; q = World.query_next(PBody, q + 1) }
print(live) # 0 — the out-of-bounds body was killed
}
}

View file

@ -23,7 +23,7 @@ program NetDemo {
@ToServer event Move { dx: int = 0 } # client → server RPC
@On(Move) handler DoMove { # the authority applies input
for (Pos) in query [Pos, {Ship}] { Pos.x = Pos.x + dx }
for (Pos) in query [Pos, {Ship}] { Pos.x += dx }
}
entry {

View file

@ -9,16 +9,16 @@
program NetEcho {
entry {
let out = bytes(4)
out[0] = 10
out[0] = '\n'
out[1] = 20
out[2] = 30
out[3] = 42
out[3] = '*'
net_send(0, out, 4) # onto the wire (the built-in loopback)
let inb = bytes(64)
let n = net_poll(inb, 64) # take the next datagram back off
print(n) # 4
var i = 0
while i < n { print(inb[i]); i = i + 1 } # 10 20 30 42
while i < n { print(inb[i]); i += 1 } # 10 20 30 42
}
}

View file

@ -16,8 +16,8 @@ program NetRoles {
property Score { n: int = 0 }
model Board { Score }
handler Both phase Update { for (Score) in query [Score] { Score.n = Score.n + 1 } } # runs everywhere
@Server handler ServerOnly phase Update { for (Score) in query [Score] { Score.n = Score.n + 100 } } # authority only
handler Both phase Update { for (Score) in query [Score] { Score.n += 1 } } # runs everywhere
@Server handler ServerOnly phase Update { for (Score) in query [Score] { Score.n += 100 } } # authority only
entry {
spawn Board { Score { n: 0 } }

View file

@ -15,7 +15,7 @@ program NetRpc {
@ToServer event Fire { dir: int = 0 }
@On(Fire) handler OnFire {
for (Log) in query [Log] { Log.hits = Log.hits + dir }
for (Log) in query [Log] { Log.hits += dir }
}
entry {