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:
parent
e3e1bc7784
commit
bf36bc8a8f
103 changed files with 1256 additions and 1252 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 } }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.*) ---
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 } }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue