@almadar/std 16.153.0 → 16.155.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/behaviors/lolo/ui/game/molecules/std-crew-board-2d.lolo +4 -4
  2. package/behaviors/lolo/ui/game/molecules/std-dungeon-board-2d.lolo +6 -6
  3. package/behaviors/lolo/ui/game/molecules/std-tactics-board-2d.lolo +74 -28
  4. package/behaviors/lolo/ui/game/molecules/std-worldmap-board-2d.lolo +2 -2
  5. package/behaviors/lolo/ui/game/molecules/ui-canvas.lolo +5 -0
  6. package/behaviors/lolo/ui/game/organisms/ui-arena-board.lolo +2 -2
  7. package/behaviors/lolo/ui/game/organisms/ui-battle-board.lolo +159 -8
  8. package/behaviors/lolo/ui/game/organisms/ui-builder-board.lolo +5 -20
  9. package/behaviors/lolo/ui/game/organisms/ui-card-battler-board.lolo +2 -0
  10. package/behaviors/lolo/ui/game/organisms/ui-castle-board.lolo +3 -3
  11. package/behaviors/lolo/ui/game/organisms/ui-city-builder-board.lolo +9 -7
  12. package/behaviors/lolo/ui/game/organisms/ui-grid-tactics-demo-board.lolo +2 -2
  13. package/behaviors/lolo/ui/game/organisms/ui-hex-strategy-board.lolo +158 -10
  14. package/behaviors/lolo/ui/game/organisms/ui-holiday-runner-board.lolo +1 -1
  15. package/behaviors/lolo/ui/game/organisms/ui-minigolf-board.lolo +22 -22
  16. package/behaviors/lolo/ui/game/organisms/ui-pinball-board.lolo +7 -7
  17. package/behaviors/lolo/ui/game/organisms/ui-pirate-board.lolo +14 -14
  18. package/behaviors/lolo/ui/game/organisms/ui-platformer-board.lolo +4 -0
  19. package/behaviors/lolo/ui/game/organisms/ui-sports-board.lolo +10 -7
  20. package/behaviors/lolo/ui/game/organisms/ui-tactics-demo-2d.lolo +135 -9
  21. package/behaviors/lolo/ui/game/organisms/ui-tanks-board.lolo +152 -8
  22. package/behaviors/lolo/ui/game/organisms/ui-topdown-rts-board.lolo +293 -17
  23. package/behaviors/lolo/ui/game/organisms/ui-tower-defense-board.lolo +8 -8
  24. package/behaviors/lolo/ui/game/organisms/ui-uncontrolled-battle-board.lolo +5 -5
  25. package/behaviors/lolo/ui/game/organisms/ui-world-map-board.lolo +3 -3
  26. package/behaviors/registry/factory-signatures.json +1893 -2294
  27. package/behaviors/registry/ui/game/molecules/std-crew-board-2d.orb +10 -12
  28. package/behaviors/registry/ui/game/molecules/std-dungeon-board-2d.orb +15 -18
  29. package/behaviors/registry/ui/game/molecules/std-tactics-board-2d.orb +1726 -395
  30. package/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +7 -60
  31. package/behaviors/registry/ui/game/molecules/ui-canvas.orb +7 -0
  32. package/behaviors/registry/ui/game/organisms/ui-arena-board.orb +5 -6
  33. package/behaviors/registry/ui/game/organisms/ui-battle-board.orb +103 -100
  34. package/behaviors/registry/ui/game/organisms/ui-builder-board.orb +21 -226
  35. package/behaviors/registry/ui/game/organisms/ui-card-battler-board.orb +42 -0
  36. package/behaviors/registry/ui/game/organisms/ui-castle-board.orb +30 -9
  37. package/behaviors/registry/ui/game/organisms/ui-city-builder-board.orb +49 -0
  38. package/behaviors/registry/ui/game/organisms/ui-grid-tactics-demo-board.orb +5 -6
  39. package/behaviors/registry/ui/game/organisms/ui-hex-strategy-board.orb +95 -1187
  40. package/behaviors/registry/ui/game/organisms/ui-holiday-runner-board.orb +2 -3
  41. package/behaviors/registry/ui/game/organisms/ui-minigolf-board.orb +55 -11
  42. package/behaviors/registry/ui/game/organisms/ui-pinball-board.orb +8 -8
  43. package/behaviors/registry/ui/game/organisms/ui-pirate-board.orb +14 -0
  44. package/behaviors/registry/ui/game/organisms/ui-platformer-board.orb +72 -0
  45. package/behaviors/registry/ui/game/organisms/ui-sports-board.orb +60 -0
  46. package/behaviors/registry/ui/game/organisms/ui-tactics-demo-2d.orb +222 -332
  47. package/behaviors/registry/ui/game/organisms/ui-tanks-board.orb +127 -95
  48. package/behaviors/registry/ui/game/organisms/ui-topdown-rts-board.orb +1501 -264
  49. package/behaviors/registry/ui/game/organisms/ui-tower-defense-board.orb +8 -0
  50. package/behaviors/registry/ui/game/organisms/ui-uncontrolled-battle-board.orb +5 -0
  51. package/behaviors/registry/ui/game/organisms/ui-world-map-board.orb +8 -12
  52. package/dist/behaviors/behaviors-embeddings.json +244 -236
  53. package/dist/behaviors/behaviors-registry.json +193 -812
  54. package/dist/behaviors/exports-reader.js +1099 -2090
  55. package/dist/behaviors/functions/index.d.ts +33 -26
  56. package/dist/behaviors/functions/index.js +1099 -2091
  57. package/dist/behaviors/index.d.ts +1 -1
  58. package/dist/behaviors/index.js +1100 -2091
  59. package/dist/behaviors/knob-embeddings.json +1 -1
  60. package/dist/behaviors/query.js +1099 -2090
  61. package/dist/behaviors/registry/factory-signatures.json +1893 -2294
  62. package/dist/behaviors/registry/ui/game/molecules/std-crew-board-2d.orb +10 -12
  63. package/dist/behaviors/registry/ui/game/molecules/std-dungeon-board-2d.orb +15 -18
  64. package/dist/behaviors/registry/ui/game/molecules/std-tactics-board-2d.orb +1726 -395
  65. package/dist/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +7 -60
  66. package/dist/behaviors/registry/ui/game/molecules/ui-canvas.orb +7 -0
  67. package/dist/behaviors/registry/ui/game/organisms/ui-arena-board.orb +5 -6
  68. package/dist/behaviors/registry/ui/game/organisms/ui-battle-board.orb +103 -100
  69. package/dist/behaviors/registry/ui/game/organisms/ui-builder-board.orb +21 -226
  70. package/dist/behaviors/registry/ui/game/organisms/ui-card-battler-board.orb +42 -0
  71. package/dist/behaviors/registry/ui/game/organisms/ui-castle-board.orb +30 -9
  72. package/dist/behaviors/registry/ui/game/organisms/ui-city-builder-board.orb +49 -0
  73. package/dist/behaviors/registry/ui/game/organisms/ui-grid-tactics-demo-board.orb +5 -6
  74. package/dist/behaviors/registry/ui/game/organisms/ui-hex-strategy-board.orb +95 -1187
  75. package/dist/behaviors/registry/ui/game/organisms/ui-holiday-runner-board.orb +2 -3
  76. package/dist/behaviors/registry/ui/game/organisms/ui-minigolf-board.orb +55 -11
  77. package/dist/behaviors/registry/ui/game/organisms/ui-pinball-board.orb +8 -8
  78. package/dist/behaviors/registry/ui/game/organisms/ui-pirate-board.orb +14 -0
  79. package/dist/behaviors/registry/ui/game/organisms/ui-platformer-board.orb +72 -0
  80. package/dist/behaviors/registry/ui/game/organisms/ui-sports-board.orb +60 -0
  81. package/dist/behaviors/registry/ui/game/organisms/ui-tactics-demo-2d.orb +222 -332
  82. package/dist/behaviors/registry/ui/game/organisms/ui-tanks-board.orb +127 -95
  83. package/dist/behaviors/registry/ui/game/organisms/ui-topdown-rts-board.orb +1501 -264
  84. package/dist/behaviors/registry/ui/game/organisms/ui-tower-defense-board.orb +8 -0
  85. package/dist/behaviors/registry/ui/game/organisms/ui-uncontrolled-battle-board.orb +5 -0
  86. package/dist/behaviors/registry/ui/game/organisms/ui-world-map-board.orb +8 -12
  87. package/dist/behaviors-embeddings.json +244 -236
  88. package/dist/behaviors-registry.json +193 -812
  89. package/dist/index.d.ts +1 -1
  90. package/dist/index.js +1100 -2091
  91. package/dist/knob-embeddings.json +1 -1
  92. package/dist/registry/factory-signatures.json +1893 -2294
  93. package/package.json +2 -2
@@ -1,5 +1,123 @@
1
1
  app ui-tactics-demo-2d "1.0.0"
2
- "Reference thin organism: a 2D grid-tactics game composed entirely from the std-tactics-board-2d frame moleculesupplies only units/theme via config."
2
+ "Micro grid-tactics demo: thin organism composing the std-tactics-board-2d frame + std-score. Two soldiers vs one raider on a 3x3 flat grid the minimal reference consumer of the tactics frame. Flat hand-authored svg theme supplied via config."
3
+ ;; @capabilities "turn-based combat, strategy gameplay"
4
+
5
+ ;; ============================================================================
6
+ ;; PERSONA + USER STORIES
7
+ ;;
8
+ ;; Read these before changing anything here, and walk them end to end after.
9
+ ;; One persona: the PLAYER, commanding two soldiers on a 3x3 flat grid
10
+ ;; against a single raider. Statuses verified 2026-07-30 by guard-truth
11
+ ;; probes (almadar-tools play_transition, 8/8) and a scripted playthrough
12
+ ;; (29/29 checks) driving the real game on the runtime-verify catalog
13
+ ;; through window.__orbitalVerification — events in, canvas drawables +
14
+ ;; HUD DOM + bus event log out. Asset sprites 404 until the CDN push
15
+ ;; lands; they are verified at the drawable-descriptor (URL) level.
16
+ ;;
17
+ ;; THE GENRE (what a grid-tactics player expects, at demo scale): you command
18
+ ;; a tiny squad, not an army. Positioning is the whole game — you read the
19
+ ;; grid, step each soldier inside the raider's reach, and commit to the
20
+ ;; strike. Every End Turn hands the board to the raider AI, which closes the
21
+ ;; distance and punishes an adjacent soldier. The kill is the economy: it
22
+ ;; scores, and wiping the enemy wins the skirmish. A round looks like:
23
+ ;; select a soldier -> green reach cells -> move -> the soldier STAYS
24
+ ;; selected and auto-arms (red cells = enemies in reach) -> strike for 2 ->
25
+ ;; the soldier is SPENT (dims, cannot act again this turn) -> activate the
26
+ ;; next soldier -> End Turn -> the raider strikes an adjacent soldier for 1
27
+ ;; or steps 1 cell toward the nearest soldier -> repeat. Each unit may move
28
+ ;; ONCE and attack ONCE per turn (movedIds/actedIds track the activation;
29
+ ;; End Turn resets both). The raider has 2 hp, so one 2-damage strike kills
30
+ ;; it; a kill scores 10 and flips the Result to victory.
31
+ ;; ============================================================================
32
+ ;;
33
+ ;; PLAYER — "I maneuver my two soldiers and take the raider down."
34
+ ;;
35
+ ;; 1. I open the board and see the whole skirmish framed on screen —
36
+ ;; grass under every cell, my soldiers on the west column, the raider
37
+ ;; on the east, and the HUD showing Result/Phase/Turn/Score.
38
+ ;; WORKS. Boot paints 9 flat cells whose terrain drawables all point
39
+ ;; at svg/flat/terrain/grass.svg, 3 units (2v1: soldiers x=0, raider
40
+ ;; x=2), HUD reads "none / observation / 0 / 0". Verified in the
41
+ ;; catalog playthrough (s01-boot.png).
42
+ ;; 2. Every unit on the board is a visible sprite with a name label and a
43
+ ;; health bar I can read at a glance.
44
+ ;; WORKS. Each unit's drawable url-rewrites to its svg idle frame
45
+ ;; (4-frame breathing, cycled by the AnimClock tick — descriptors
46
+ ;; showed soldier.idle-N.svg cycling) anchored "ground", with the
47
+ ;; black backing + green health fraction above and the name label.
48
+ ;; All 3 bars read full (1.00) at boot; labels Soldier A / Soldier B
49
+ ;; / Raider all painted.
50
+ ;; 3. I click one of MY soldiers and the cells it can reach light up.
51
+ ;; WORKS. Clicking p1 sets phase "selection" and lights exactly 4
52
+ ;; green cells — the range-2 manhattan diamond from (0,0) minus the
53
+ ;; two occupied cells (s03-select.png). SPENT soldiers (in actedIds)
54
+ ;; are not selectable: play_transition on the attack arm with
55
+ ;; actedIds ["p1"] returns guard=fail.
56
+ ;; 4. I click a lit cell and the soldier moves there — and stays selected,
57
+ ;; auto-armed, so I can strike immediately. One move per turn.
58
+ ;; WORKS. p1 walked (0,0)->(1,1): the walk animation plays (caught
59
+ ;; mid-cycle, s04-walk.png), the authority re-validates and writes
60
+ ;; the position, and the unit lands in phase "action". A second
61
+ ;; TILE_CLICK the same turn is denied (movedIds guard — unit held
62
+ ;; at (1,1)), and re-clicking the moved unit lights zero green
63
+ ;; cells. Guard truth: play_transition move pass / already-moved
64
+ ;; fail / out-of-bounds fail.
65
+ ;; 5. Adjacent enemies I can strike light up red — right after my move,
66
+ ;; or when I click my selected soldier again.
67
+ ;; WORKS. Post-move auto-arm computes red cells from the NEW position:
68
+ ;; after p1 -> (1,1) the raider's cell (2,1) lit red (s05-armed.png).
69
+ ;; Living enemies at manhattan distance <= attackRange (config,
70
+ ;; default 1) light red.
71
+ ;; 6. I click a red-lit enemy — or any enemy in reach, even without arming
72
+ ;; first — and it takes a hit; my soldier is then spent for the turn.
73
+ ;; WORKS. The attack arm fires whenever a living selected unit has a
74
+ ;; living enemy in reach, regardless of phase — a direct click on the
75
+ ;; raider from phase "selection" landed the 2-damage kill. A "-2"
76
+ ;; damage number + hit fx sprite spawn at the target (BURST, ttl 3),
77
+ ;; and the attacker joins actedIds: it dimmed to 0.45 opacity.
78
+ ;; Guard truth via play_transition: in-range pass, out-of-range
79
+ ;; fail, already-acted fail. (On this 2hp-raider board the first
80
+ ;; strike also ends the game, so spent-select denial is proven by
81
+ ;; the guard probe rather than a mid-game click.)
82
+ ;; 7. I change my mind mid-selection and back out cleanly.
83
+ ;; WORKS. CANCEL clears selection, highlights, and phase back to
84
+ ;; "observation" (green 4 -> 0). Guard truth: play_transition pass.
85
+ ;; 8. I end my turn and the raider takes theirs — it closes in and fights
86
+ ;; back, and my soldiers are fresh again.
87
+ ;; WORKS. END_TURN bumps Turn (0 -> 1), clears selection + both
88
+ ;; activation lists, and emits AI_TURN; the authority struck the
89
+ ;; adjacent p1 for 1 (hp 3 -> 2, health bar shrank) — the raider was
90
+ ;; already in reach, so no step was needed (s08-ai-turn.png). After
91
+ ;; the reset p1 was selectable again (phase "selection").
92
+ ;; 9. When I kill the raider I am rewarded — score ticks up and the
93
+ ;; corpse stays on the field.
94
+ ;; WORKS. The kill scored +10 (ScoreKeeper on UNITS_CHANGED; HUD
95
+ ;; Score 0 -> 10), the corpse renders its death frame
96
+ ;; (raider.death-0.svg descriptor), and its health bar + name label
97
+ ;; dropped from the canvas (s09-kill.png).
98
+ ;; 10. When the raider falls, I win and the game tells me — then the
99
+ ;; board freezes.
100
+ ;; WORKS. Result flips to "victory" (RoundLogic on UNITS_CHANGED),
101
+ ;; GAME_END fires on the bus, and every UNIT_CLICK / TILE_CLICK arm
102
+ ;; is guarded on result == "none" — a post-game click on p2 selected
103
+ ;; nothing and lit zero cells (s10-victory.png).
104
+ ;;
105
+ ;; ASSET INVENTORY (hand-authored svg, almadar-kflow-assets.web.app
106
+ ;; /shared/ui-tactics-demo-2d/svg/flat/, generated by tools/asset-workflow
107
+ ;; svg-generator from the sibling .md descriptions)
108
+ ;;
109
+ ;; Terrain terrain/grass.svg — full-cell flat tile, drawn at
110
+ ;; width/height 1 so no gaps show between cells.
111
+ ;; Units units/{soldier,raider}.svg + animation frames
112
+ ;; .{idle-0..3, walk-0..1, attack-0, hit-0, death-0}.svg; the
113
+ ;; frame swaps url-rewrites name.svg -> name.<anim>-<i>.svg and a
114
+ ;; hidden warm-up layer preloads every frame so swaps never pop.
115
+ ;; player: p1 Soldier A 3hp (0,0) p2 Soldier B 3hp (0,2)
116
+ ;; enemy: e1 Raider 2hp (2,1)
117
+ ;; Effects effects/hit.svg (BURST "hit" fx at the struck cell).
118
+ ;; UI chrome ui/panel-frame.svg = game-shell background + HUD panel.
119
+ ;; ============================================================================
120
+
3
121
  orbital TacticsDemo2DOrbital {
4
122
  uses Frame from "std/behaviors/std-tactics-board-2d"
5
123
  uses Score from "std/behaviors/std-score"
@@ -68,13 +186,15 @@ orbital TacticsDemo2DOrbital {
68
186
 
69
187
  entity GameState [runtime, shared] {
70
188
  id : string!
71
- units : [Unit] = [ { id: "p1", position: { x: 0, y: 0 }, x: 0, y: 0, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier A", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "blue_body_square.png", role: "player", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "player-unit", thumbnailUrl: "" } }, { id: "p2", position: { x: 0, y: 2 }, x: 0, y: 2, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier B", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "blue_body_square.png", role: "player", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "player-unit", thumbnailUrl: "" } }, { id: "e1", position: { x: 2, y: 1 }, x: 2, y: 1, z: 0, team: "enemy", health: 2, maxHealth: 2, unitType: "raider", name: "Raider", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "red_body_square.png", role: "enemy", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "enemy-unit", thumbnailUrl: "" } } ]
189
+ units : [Unit] = [ { id: "p1", position: { x: 0, y: 0 }, x: 0, y: 0, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier A", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/soldier.svg", role: "npc", category: "soldier", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "soldier", thumbnailUrl: "" } }, { id: "p2", position: { x: 0, y: 2 }, x: 0, y: 2, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier B", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/soldier.svg", role: "npc", category: "soldier", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "soldier", thumbnailUrl: "" } }, { id: "e1", position: { x: 2, y: 1 }, x: 2, y: 1, z: 0, team: "enemy", health: 2, maxHealth: 2, unitType: "raider", name: "Raider", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/raider.svg", role: "npc", category: "raider", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "raider", thumbnailUrl: "" } } ]
72
190
  fx : [Fx] = []
73
- tiles : [Tile] = [ { id: "t-0-0", x: 0, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-0", x: 1, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-0", x: 2, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-0-1", x: 0, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-1", x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-1", x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-0-2", x: 0, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-2", x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-2", x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } } ]
191
+ tiles : [Tile] = [ { id: "t-0-0", x: 0, y: 0, terrain: "grass", passable: true }, { id: "t-1-0", x: 1, y: 0, terrain: "grass", passable: true }, { id: "t-2-0", x: 2, y: 0, terrain: "grass", passable: true }, { id: "t-0-1", x: 0, y: 1, terrain: "grass", passable: true }, { id: "t-1-1", x: 1, y: 1, terrain: "grass", passable: true }, { id: "t-2-1", x: 2, y: 1, terrain: "grass", passable: true }, { id: "t-0-2", x: 0, y: 2, terrain: "grass", passable: true }, { id: "t-1-2", x: 1, y: 2, terrain: "grass", passable: true }, { id: "t-2-2", x: 2, y: 2, terrain: "grass", passable: true } ]
74
192
  result : string = "none"
75
193
  selectedUnitId : string = ""
76
194
  validMoves : [ValidMove] = []
77
195
  attackTargets : [ValidMove] = []
196
+ movedIds : [string] = []
197
+ actedIds : [string] = []
78
198
  uiPhase : string = "observation"
79
199
  turn : number = 0
80
200
  score : number = 0
@@ -87,8 +207,8 @@ orbital TacticsDemo2DOrbital {
87
207
  gridWidth: 3
88
208
  gridHeight: 3
89
209
  attackDamage: 2
90
- units: [ { id: "p1", position: { x: 0, y: 0 }, x: 0, y: 0, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier A", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "blue_body_square.png", role: "player", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "player-unit", thumbnailUrl: "" } }, { id: "p2", position: { x: 0, y: 2 }, x: 0, y: 2, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier B", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "blue_body_square.png", role: "player", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "player-unit", thumbnailUrl: "" } }, { id: "e1", position: { x: 2, y: 1 }, x: 2, y: 1, z: 0, team: "enemy", health: 2, maxHealth: 2, unitType: "raider", name: "Raider", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "red_body_square.png", role: "enemy", category: "unit", animations: ["idle"], style: "vector", dimension: "2d", aspect: "1:1", name: "enemy-unit", thumbnailUrl: "" } } ]
91
- tiles: [ { id: "t-0-0", x: 0, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-0", x: 1, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-0", x: 2, y: 0, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-0-1", x: 0, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-1", x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-1", x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-0-2", x: 0, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-1-2", x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, { id: "t-2-2", x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-shape-characters/units/spritesheet_default.json", sprite: "grey_body_square.png", role: "tile", category: "grass", animations: ["static"], style: "vector", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } } ]
210
+ units: [ { id: "p1", position: { x: 0, y: 0 }, x: 0, y: 0, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier A", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/soldier.svg", role: "npc", category: "soldier", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "soldier", thumbnailUrl: "" } }, { id: "p2", position: { x: 0, y: 2 }, x: 0, y: 2, z: 0, team: "player", health: 3, maxHealth: 3, unitType: "soldier", name: "Soldier B", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/soldier.svg", role: "npc", category: "soldier", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "soldier", thumbnailUrl: "" } }, { id: "e1", position: { x: 2, y: 1 }, x: 2, y: 1, z: 0, team: "enemy", health: 2, maxHealth: 2, unitType: "raider", name: "Raider", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/raider.svg", role: "npc", category: "raider", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "raider", thumbnailUrl: "" } } ]
211
+ tiles: [ { id: "t-0-0", x: 0, y: 0, terrain: "grass", passable: true }, { id: "t-1-0", x: 1, y: 0, terrain: "grass", passable: true }, { id: "t-2-0", x: 2, y: 0, terrain: "grass", passable: true }, { id: "t-0-1", x: 0, y: 1, terrain: "grass", passable: true }, { id: "t-1-1", x: 1, y: 1, terrain: "grass", passable: true }, { id: "t-2-1", x: 2, y: 1, terrain: "grass", passable: true }, { id: "t-0-2", x: 0, y: 2, terrain: "grass", passable: true }, { id: "t-1-2", x: 1, y: 2, terrain: "grass", passable: true }, { id: "t-2-2", x: 2, y: 2, terrain: "grass", passable: true } ]
92
212
  }
93
213
  }
94
214
 
@@ -101,7 +221,11 @@ orbital TacticsDemo2DOrbital {
101
221
  }
102
222
 
103
223
  trait ScoreKeeper = Score.traits.ScoreKeeper -> GameState {
104
- config { running: true, initialEnemies: 1 }
224
+ config { running: true, pointsPerKill: 10, startScore: 0, initialEnemies: 1 }
225
+ }
226
+
227
+ trait AnimClock = Frame.traits.AnimClock -> GameState {
228
+ config { frames: [] }
105
229
  }
106
230
 
107
231
  trait Player = Frame.traits.PlayerIntent -> GameState {
@@ -109,13 +233,15 @@ orbital TacticsDemo2DOrbital {
109
233
  gridWidth: 3
110
234
  gridHeight: 3
111
235
  movementRange: 2
112
- features: []
113
- assetManifest: { terrains: {}, units: {}, features: {}, effects: {} }
114
236
  projection: "flat"
115
237
  showMinimap: false
116
238
  scale: 1
239
+ backgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/ui/panel-frame.svg", role: "ui", category: "ui", animations: [], style: "adventure", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
240
+ hudBackgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/ui/panel-frame.svg", role: "ui", category: "ui", animations: [], style: "adventure", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
241
+ features: []
242
+ assetManifest: { terrains: { grass: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/terrain/grass.svg", role: "tile", category: "grass", animations: ["static"], style: "", dimension: "2d", aspect: "1:1", name: "grass", thumbnailUrl: "" } }, units: { soldier: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/soldier.svg", role: "npc", category: "soldier", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "soldier", thumbnailUrl: "" }, raider: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/units/raider.svg", role: "npc", category: "raider", animations: ["idle","walk","attack","hit","death"], style: "", dimension: "2d", aspect: "1:1", name: "raider", thumbnailUrl: "" } }, features: {}, effects: { hit: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/effects/hit.svg", role: "effect", category: "hit", animations: ["static"], style: "", dimension: "2d", aspect: "1:1", name: "hit", thumbnailUrl: "" } }, ui: { panel-frame: { url: "https://almadar-kflow-assets.web.app/shared/ui-tactics-demo-2d/svg/flat/ui/panel-frame.svg", role: "ui", category: "ui", animations: [], style: "adventure", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" } } }
117
243
  }
118
244
  }
119
245
 
120
- page "/tactics-demo-2d" as TacticsDemo2DPage -> Authority, FxDecay, RoundLogic, ScoreKeeper, Player
246
+ page "/tactics-demo-2d" as TacticsDemo2DPage -> Authority, FxDecay, RoundLogic, ScoreKeeper, Player, AnimClock
121
247
  }
@@ -1,6 +1,143 @@
1
1
  app ui-tanks-board "1.0.0"
2
- "Sci-fi tank grid-tactics game: thin organism composing the std-tactics-board-2d frame with a tank theme (Kenney topdown-tanks assets, sci-fi UI panels) supplied entirely via config."
2
+ "Sci-fi tank grid-tactics skirmish: thin organism composing the std-tactics-board-2d frame + std-score. Flat-projection hand-authored svg theme (desert ground, sand strip, sandbag cover, tank sprites) supplied entirely via config."
3
3
  ;; @capabilities "tank grid-tactics combat, top-down tank movement, sci-fi themed battle, turn-based tank strategy"
4
+
5
+ ;; ============================================================================
6
+ ;; PERSONA + USER STORIES
7
+ ;;
8
+ ;; Read these before changing anything here, and walk them end to end after.
9
+ ;; One persona: the PLAYER, commanding the lone tank Alpha on an 8x6 flat
10
+ ;; desert grid against two enemy tanks (Bravo, Charlie). Status re-verified
11
+ ;; 2026-07-30 by scripted playthroughs driving the real game on the
12
+ ;; runtime-verify catalog through window.__orbitalVerification — events in,
13
+ ;; canvas drawables + HUD DOM + bus event log out: victory run 33/35 (the 2
14
+ ;; anim-frame misses were 400ms-sampling artifacts, both proven by the
15
+ ;; focused probe), focused probe 7/7, defeat run 4/4.
16
+ ;;
17
+ ;; THE GENRE (what a tank-tactics player expects): armored warfare in
18
+ ;; miniature — one precious hull, not a swarm. You maneuver for firing
19
+ ;; position: your cannon reaches TWO cells (attackRange 2), so you can shell
20
+ ;; an enemy before it can answer, because the enemy AI only fires at
21
+ ;; point-blank (1 cell) and otherwise rolls 1 cell toward you each turn.
22
+ ;; Every shot and every move is a commitment: each tank moves ONCE and fires
23
+ ;; ONCE per turn (movedIds/actedIds), then sits spent until End Turn.
24
+ ;; Kills are the economy: each enemy destroyed scores 10. You win by
25
+ ;; destroying both enemy tanks; you lose the moment Alpha is destroyed.
26
+ ;; A round looks like: select Alpha -> green reach cells -> move -> Alpha
27
+ ;; STAYS selected and auto-arms (red cells = enemies in cannon reach) ->
28
+ ;; fire for 3 -> Alpha is SPENT (dims, cannot act again this turn) ->
29
+ ;; End Turn -> each living enemy strikes Alpha for 1 if adjacent, else
30
+ ;; steps 1 cell toward Alpha -> repeat.
31
+ ;; ============================================================================
32
+ ;;
33
+ ;; PLAYER — "I outmaneuver the enemy armor and destroy it before it closes
34
+ ;; to point-blank."
35
+ ;;
36
+ ;; 1. I open the board and see the whole battlefield framed on screen —
37
+ ;; terrain under every cell (desert ground with a sand strip), my tank
38
+ ;; at the west, the enemy tanks at the east, sandbag cover in the
39
+ ;; middle, and the HUD showing Result/Phase/Turn/Score.
40
+ ;; WORKS. Boot paints 48 full-cell flat terrain sprites (ground with
41
+ ;; a sand strip), 3 tanks (Alpha (1,1) west; Bravo (6,0), Charlie
42
+ ;; (6,4) east), 6 sandbag features ground-anchored 1x1 at
43
+ ;; (2,1)(3,2)(4,3)(5,3)(5,4)(2,4); HUD reads
44
+ ;; "none / observation / 0 / 0". Drawable descriptors point at the
45
+ ;; svg/flat CDN URLs (browser 404s expected this wave — the CDN push
46
+ ;; is coordinator-handled; verified at descriptor level).
47
+ ;; 2. Every tank on the board is a visible sprite with a name label and a
48
+ ;; health bar I can read at a glance.
49
+ ;; WORKS. Each tank paints its svg idle frame (4-frame breathing
50
+ ;; squash/stretch anchored at the feet, cycled by the AnimClock) with
51
+ ;; the black backing + green health fraction above and the name
52
+ ;; label. All 3 bars read 1.00 at boot, all 3 labels paint. Dead
53
+ ;; tanks drop the bar and label and switch to the death frame
54
+ ;; (verified on both kills and on Alpha's own defeat).
55
+ ;; 3. I click Alpha and the cells it can reach light up green.
56
+ ;; WORKS. Clicking t1 sets phase "selection" and lights exactly 10
57
+ ;; green cells — the range-2 manhattan diamond from (1,1) minus
58
+ ;; out-of-bounds cells (none occupied at boot).
59
+ ;; 4. I click a lit cell and Alpha moves there — and stays selected,
60
+ ;; auto-armed, so I can fire immediately; it cannot move again this
61
+ ;; turn.
62
+ ;; WORKS. t1 walked (1,1)->(3,1): the walk animation plays (2-frame
63
+ ;; cycle), the authority writes the position, phase lands on
64
+ ;; "action", and the move is SPENT — a second TILE_CLICK is ignored
65
+ ;; (tank stays at (3,1)) and a re-select lights zero green cells
66
+ ;; (movedIds guard). One move per turn, no chaining.
67
+ ;; 5. Enemies inside my cannon reach (2 cells) light up red — right after
68
+ ;; my move, or when I click Alpha again.
69
+ ;; WORKS. With Alpha at (2,2) and Charlie at (4,2), the post-move
70
+ ;; auto-arm lit exactly {(4,2)} red — every living enemy within
71
+ ;; attackRange 2 of the NEW cell — and the manual re-click arm
72
+ ;; recomputed the same red set (phase "action").
73
+ ;; 6. I click a red-lit enemy — or any enemy in reach, even without arming
74
+ ;; first — and it takes a shell; Alpha is then spent for the turn. An
75
+ ;; enemy OUT of reach does nothing when clicked.
76
+ ;; WORKS. Direct enemy-click attack: t2 frac 1.00 -> 0.70 (10hp, -3),
77
+ ;; BURST "-3" + hit.svg fx sprite at the target, attack frame on
78
+ ;; Alpha, hit frame on the target (both caught by 100ms polling), and
79
+ ;; Alpha joins actedIds: dims to 0.45 opacity, unselectable until End
80
+ ;; Turn. Negative probe: clicking far-away t3 (distance 5) changed
81
+ ;; nothing — phase and score untouched.
82
+ ;; 7. I change my mind mid-selection and back out cleanly.
83
+ ;; WORKS. CANCEL clears selection, highlights, and phase — verified
84
+ ;; green cells 10 -> 0 and phase back to "observation".
85
+ ;; 8. I end my turn and the enemy takes theirs — they close in and fight
86
+ ;; back, and Alpha is fresh again.
87
+ ;; WORKS. END_TURN bumps Turn, clears selection + both activation
88
+ ;; lists (Alpha selectable again next turn), and emits AI_TURN; the
89
+ ;; authority steps each living enemy 1 cell (diagonally — both axes)
90
+ ;; toward Alpha and strikes for 1 when adjacent (verified: enemy
91
+ ;; positions changed every turn; the defeat run ground Alpha 10->0).
92
+ ;; LIMITATION: enemies POP to their new cell — the authority applies
93
+ ;; the step silently and the single walk-anim slot is player-facing;
94
+ ;; per-enemy MOVE fan-out is atom surgery, deferred.
95
+ ;; 9. When I destroy an enemy I am rewarded — score ticks up, and the
96
+ ;; wreck stays on the field without blocking movement.
97
+ ;; WORKS x2. Each kill: Score +10 (verified 10 then 20 on the HUD)
98
+ ;; and the wreck renders its death frame. Corpses are excluded from
99
+ ;; the occupancy filters (health > 0) so their cells stay walkable —
100
+ ;; inherited from the frame molecule's guards.
101
+ ;; 10. When the last enemy tank falls, I win and the game tells me — then
102
+ ;; the board freezes.
103
+ ;; WORKS. Both enemies destroyed: Result flips to "victory"
104
+ ;; (RoundLogic on UNITS_CHANGED), GAME_END fires, and the board is
105
+ ;; FROZEN — post-game UNIT_CLICK selects nothing (phase stays
106
+ ;; "observation", zero highlights). Reload to play again.
107
+ ;; 11. If Alpha is destroyed, I lose and the game tells me — then the
108
+ ;; board freezes.
109
+ ;; WORKS. Defeat run (Alpha marches to point-blank and never fires):
110
+ ;; adjacent enemy fire ground Alpha to 0, Result flipped to "defeat",
111
+ ;; GAME_END fired, and the board froze (post-game clicks do nothing).
112
+ ;;
113
+ ;; MULTIPLAYER NOTE: GameState is [runtime, shared], so every connected
114
+ ;; client watches the same skirmish on the same frame; intents serialize
115
+ ;; through the server authority. There is no per-seat turn ownership yet —
116
+ ;; any client can drive Alpha.
117
+ ;;
118
+ ;; ASSET INVENTORY (hand-authored svg, almadar-kflow-assets.web.app
119
+ ;; /shared/ui-tanks-board/svg/flat/, generated by tools/asset-workflow
120
+ ;; svg-generator from the sibling .md descriptions)
121
+ ;;
122
+ ;; Terrain terrain/ground.svg, terrain/sand.svg — full-cell flat tiles
123
+ ;; (rounded-rect desert ground; sandy strip) drawn at
124
+ ;; width/height 1.
125
+ ;; Units units/{tank,tank_enemy}.svg + animation frames
126
+ ;; .{idle-0..3, walk-0..1, attack-0, hit-0, death-0}.svg; the
127
+ ;; frame swaps url-rewrite name.svg -> name.<anim>-<i>.svg and a
128
+ ;; hidden warm-up layer preloads every frame so swaps never pop.
129
+ ;; player: t1 Alpha 10hp (1,1) -> tank.svg
130
+ ;; enemy: t2 Bravo 10hp (6,0) -> tank_enemy.svg
131
+ ;; t3 Charlie 8hp (6,4) -> tank_enemy.svg
132
+ ;; Features features/obstacle.svg — six sandbag-cover cells at
133
+ ;; (2,1) (3,2) (4,3) (5,3) (5,4) (2,4), drawn from
134
+ ;; config.features anchored "ground" at 1x1 so they sit ON their
135
+ ;; cell. Decorative: they do not block movement (all tiles
136
+ ;; passable).
137
+ ;; Effects effects/hit.svg (BURST "hit" fx on every landed shell),
138
+ ;; effects/explosion.svg (manifest-only).
139
+ ;; UI chrome ui/panel-frame.svg = game-shell background + HUD panel.
140
+ ;; ============================================================================
4
141
  orbital TanksBoardOrbital {
5
142
  uses Frame from "std/behaviors/std-tactics-board-2d"
6
143
  uses Score from "std/behaviors/std-score"
@@ -69,13 +206,15 @@ orbital TanksBoardOrbital {
69
206
 
70
207
  entity GameState [runtime, shared] {
71
208
  id : string!
72
- units : [Unit] = [ { id: "t1", position: { x: 1, y: 1 }, x: 1, y: 1, z: 0, team: "player", health: 10, maxHealth: 10, unitType: "tank", name: "Alpha", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_green.png", role: "npc", category: "tankBody_green", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_green", thumbnailUrl: "" } }, { id: "t2", position: { x: 6, y: 0 }, x: 6, y: 0, z: 0, team: "enemy", health: 10, maxHealth: 10, unitType: "tank", name: "Bravo", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_dark.png", role: "npc", category: "tankBody_dark", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_dark", thumbnailUrl: "" } }, { id: "t3", position: { x: 6, y: 4 }, x: 6, y: 4, z: 0, team: "enemy", health: 8, maxHealth: 10, unitType: "tank", name: "Charlie", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_dark.png", role: "npc", category: "tankBody_dark", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_dark", thumbnailUrl: "" } } ]
209
+ units : [Unit] = [ { id: "t1", position: { x: 1, y: 1 }, x: 1, y: 1, z: 0, team: "player", health: 10, maxHealth: 10, unitType: "tank", name: "Alpha", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank.svg", role: "npc", category: "tank", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank", thumbnailUrl: "" } }, { id: "t2", position: { x: 6, y: 0 }, x: 6, y: 0, z: 0, team: "enemy", health: 10, maxHealth: 10, unitType: "tank", name: "Bravo", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank_enemy.svg", role: "npc", category: "tank_enemy", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank_enemy", thumbnailUrl: "" } }, { id: "t3", position: { x: 6, y: 4 }, x: 6, y: 4, z: 0, team: "enemy", health: 8, maxHealth: 8, unitType: "tank", name: "Charlie", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank_enemy.svg", role: "npc", category: "tank_enemy", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank_enemy", thumbnailUrl: "" } } ]
73
210
  fx : [Fx] = []
74
211
  tiles : [Tile] = [ { id: "t-0-0", x: 0, y: 0, terrain: "ground", passable: true }, { id: "t-1-0", x: 1, y: 0, terrain: "ground", passable: true }, { id: "t-2-0", x: 2, y: 0, terrain: "ground", passable: true }, { id: "t-3-0", x: 3, y: 0, terrain: "ground", passable: true }, { id: "t-4-0", x: 4, y: 0, terrain: "ground", passable: true }, { id: "t-5-0", x: 5, y: 0, terrain: "ground", passable: true }, { id: "t-6-0", x: 6, y: 0, terrain: "ground", passable: true }, { id: "t-7-0", x: 7, y: 0, terrain: "ground", passable: true }, { id: "t-0-1", x: 0, y: 1, terrain: "ground", passable: true }, { id: "t-1-1", x: 1, y: 1, terrain: "ground", passable: true }, { id: "t-2-1", x: 2, y: 1, terrain: "ground", passable: true }, { id: "t-3-1", x: 3, y: 1, terrain: "ground", passable: true }, { id: "t-4-1", x: 4, y: 1, terrain: "ground", passable: true }, { id: "t-5-1", x: 5, y: 1, terrain: "ground", passable: true }, { id: "t-6-1", x: 6, y: 1, terrain: "ground", passable: true }, { id: "t-7-1", x: 7, y: 1, terrain: "ground", passable: true }, { id: "t-0-2", x: 0, y: 2, terrain: "ground", passable: true }, { id: "t-1-2", x: 1, y: 2, terrain: "ground", passable: true }, { id: "t-2-2", x: 2, y: 2, terrain: "ground", passable: true }, { id: "t-3-2", x: 3, y: 2, terrain: "sand", passable: true }, { id: "t-4-2", x: 4, y: 2, terrain: "sand", passable: true }, { id: "t-5-2", x: 5, y: 2, terrain: "sand", passable: true }, { id: "t-6-2", x: 6, y: 2, terrain: "ground", passable: true }, { id: "t-7-2", x: 7, y: 2, terrain: "ground", passable: true }, { id: "t-0-3", x: 0, y: 3, terrain: "ground", passable: true }, { id: "t-1-3", x: 1, y: 3, terrain: "ground", passable: true }, { id: "t-2-3", x: 2, y: 3, terrain: "sand", passable: true }, { id: "t-3-3", x: 3, y: 3, terrain: "sand", passable: true }, { id: "t-4-3", x: 4, y: 3, terrain: "sand", passable: true }, { id: "t-5-3", x: 5, y: 3, terrain: "sand", passable: true }, { id: "t-6-3", x: 6, y: 3, terrain: "ground", passable: true }, { id: "t-7-3", x: 7, y: 3, terrain: "ground", passable: true }, { id: "t-0-4", x: 0, y: 4, terrain: "ground", passable: true }, { id: "t-1-4", x: 1, y: 4, terrain: "ground", passable: true }, { id: "t-2-4", x: 2, y: 4, terrain: "ground", passable: true }, { id: "t-3-4", x: 3, y: 4, terrain: "sand", passable: true }, { id: "t-4-4", x: 4, y: 4, terrain: "sand", passable: true }, { id: "t-5-4", x: 5, y: 4, terrain: "ground", passable: true }, { id: "t-6-4", x: 6, y: 4, terrain: "ground", passable: true }, { id: "t-7-4", x: 7, y: 4, terrain: "ground", passable: true }, { id: "t-0-5", x: 0, y: 5, terrain: "ground", passable: true }, { id: "t-1-5", x: 1, y: 5, terrain: "ground", passable: true }, { id: "t-2-5", x: 2, y: 5, terrain: "ground", passable: true }, { id: "t-3-5", x: 3, y: 5, terrain: "ground", passable: true }, { id: "t-4-5", x: 4, y: 5, terrain: "ground", passable: true }, { id: "t-5-5", x: 5, y: 5, terrain: "ground", passable: true }, { id: "t-6-5", x: 6, y: 5, terrain: "ground", passable: true }, { id: "t-7-5", x: 7, y: 5, terrain: "ground", passable: true } ]
75
212
  result : string = "none"
76
213
  selectedUnitId : string = ""
77
214
  validMoves : [ValidMove] = []
78
215
  attackTargets : [ValidMove] = []
216
+ movedIds : [string] = []
217
+ actedIds : [string] = []
79
218
  uiPhase : string = "observation"
80
219
  turn : number = 0
81
220
  score : number = 0
@@ -88,7 +227,7 @@ orbital TanksBoardOrbital {
88
227
  gridWidth: 8
89
228
  gridHeight: 6
90
229
  attackDamage: 3
91
- units: [ { id: "t1", position: { x: 1, y: 1 }, x: 1, y: 1, z: 0, team: "player", health: 10, maxHealth: 10, unitType: "tank", name: "Alpha", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_green.png", role: "npc", category: "tankBody_green", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_green", thumbnailUrl: "" } }, { id: "t2", position: { x: 6, y: 0 }, x: 6, y: 0, z: 0, team: "enemy", health: 10, maxHealth: 10, unitType: "tank", name: "Bravo", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_dark.png", role: "npc", category: "tankBody_dark", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_dark", thumbnailUrl: "" } }, { id: "t3", position: { x: 6, y: 4 }, x: 6, y: 4, z: 0, team: "enemy", health: 8, maxHealth: 10, unitType: "tank", name: "Charlie", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_dark.png", role: "npc", category: "tankBody_dark", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tankBody_dark", thumbnailUrl: "" } } ]
230
+ units: [ { id: "t1", position: { x: 1, y: 1 }, x: 1, y: 1, z: 0, team: "player", health: 10, maxHealth: 10, unitType: "tank", name: "Alpha", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank.svg", role: "npc", category: "tank", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank", thumbnailUrl: "" } }, { id: "t2", position: { x: 6, y: 0 }, x: 6, y: 0, z: 0, team: "enemy", health: 10, maxHealth: 10, unitType: "tank", name: "Bravo", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank_enemy.svg", role: "npc", category: "tank_enemy", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank_enemy", thumbnailUrl: "" } }, { id: "t3", position: { x: 6, y: 4 }, x: 6, y: 4, z: 0, team: "enemy", health: 8, maxHealth: 8, unitType: "tank", name: "Charlie", modelUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank_enemy.svg", role: "npc", category: "tank_enemy", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank_enemy", thumbnailUrl: "" } } ]
92
231
  tiles: [ { id: "t-0-0", x: 0, y: 0, terrain: "ground", passable: true }, { id: "t-1-0", x: 1, y: 0, terrain: "ground", passable: true }, { id: "t-2-0", x: 2, y: 0, terrain: "ground", passable: true }, { id: "t-3-0", x: 3, y: 0, terrain: "ground", passable: true }, { id: "t-4-0", x: 4, y: 0, terrain: "ground", passable: true }, { id: "t-5-0", x: 5, y: 0, terrain: "ground", passable: true }, { id: "t-6-0", x: 6, y: 0, terrain: "ground", passable: true }, { id: "t-7-0", x: 7, y: 0, terrain: "ground", passable: true }, { id: "t-0-1", x: 0, y: 1, terrain: "ground", passable: true }, { id: "t-1-1", x: 1, y: 1, terrain: "ground", passable: true }, { id: "t-2-1", x: 2, y: 1, terrain: "ground", passable: true }, { id: "t-3-1", x: 3, y: 1, terrain: "ground", passable: true }, { id: "t-4-1", x: 4, y: 1, terrain: "ground", passable: true }, { id: "t-5-1", x: 5, y: 1, terrain: "ground", passable: true }, { id: "t-6-1", x: 6, y: 1, terrain: "ground", passable: true }, { id: "t-7-1", x: 7, y: 1, terrain: "ground", passable: true }, { id: "t-0-2", x: 0, y: 2, terrain: "ground", passable: true }, { id: "t-1-2", x: 1, y: 2, terrain: "ground", passable: true }, { id: "t-2-2", x: 2, y: 2, terrain: "ground", passable: true }, { id: "t-3-2", x: 3, y: 2, terrain: "sand", passable: true }, { id: "t-4-2", x: 4, y: 2, terrain: "sand", passable: true }, { id: "t-5-2", x: 5, y: 2, terrain: "sand", passable: true }, { id: "t-6-2", x: 6, y: 2, terrain: "ground", passable: true }, { id: "t-7-2", x: 7, y: 2, terrain: "ground", passable: true }, { id: "t-0-3", x: 0, y: 3, terrain: "ground", passable: true }, { id: "t-1-3", x: 1, y: 3, terrain: "ground", passable: true }, { id: "t-2-3", x: 2, y: 3, terrain: "sand", passable: true }, { id: "t-3-3", x: 3, y: 3, terrain: "sand", passable: true }, { id: "t-4-3", x: 4, y: 3, terrain: "sand", passable: true }, { id: "t-5-3", x: 5, y: 3, terrain: "sand", passable: true }, { id: "t-6-3", x: 6, y: 3, terrain: "ground", passable: true }, { id: "t-7-3", x: 7, y: 3, terrain: "ground", passable: true }, { id: "t-0-4", x: 0, y: 4, terrain: "ground", passable: true }, { id: "t-1-4", x: 1, y: 4, terrain: "ground", passable: true }, { id: "t-2-4", x: 2, y: 4, terrain: "ground", passable: true }, { id: "t-3-4", x: 3, y: 4, terrain: "sand", passable: true }, { id: "t-4-4", x: 4, y: 4, terrain: "sand", passable: true }, { id: "t-5-4", x: 5, y: 4, terrain: "ground", passable: true }, { id: "t-6-4", x: 6, y: 4, terrain: "ground", passable: true }, { id: "t-7-4", x: 7, y: 4, terrain: "ground", passable: true }, { id: "t-0-5", x: 0, y: 5, terrain: "ground", passable: true }, { id: "t-1-5", x: 1, y: 5, terrain: "ground", passable: true }, { id: "t-2-5", x: 2, y: 5, terrain: "ground", passable: true }, { id: "t-3-5", x: 3, y: 5, terrain: "ground", passable: true }, { id: "t-4-5", x: 4, y: 5, terrain: "ground", passable: true }, { id: "t-5-5", x: 5, y: 5, terrain: "ground", passable: true }, { id: "t-6-5", x: 6, y: 5, terrain: "ground", passable: true }, { id: "t-7-5", x: 7, y: 5, terrain: "ground", passable: true } ]
93
232
  }
94
233
  }
@@ -105,20 +244,25 @@ orbital TanksBoardOrbital {
105
244
  config { running: true, pointsPerKill: 10, startScore: 0, initialEnemies: 2 }
106
245
  }
107
246
 
247
+ trait AnimClock = Frame.traits.AnimClock -> GameState {
248
+ config { frames: [] }
249
+ }
250
+
108
251
  trait Player = Frame.traits.PlayerIntent -> GameState {
109
252
  config {
110
253
  gridWidth: 8
111
254
  gridHeight: 6
112
255
  movementRange: 2
256
+ attackRange: 2
113
257
  projection: "flat"
114
258
  showMinimap: false
115
259
  scale: 0.45
116
- backgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-ui-pack-sci-fi/ui/uipackSpace_sheet.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-ui-pack-sci-fi/ui/uipackSpace_sheet.json", sprite: "metalPanel_blue.png", role: "ui", category: "ui", animations: [], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
117
- hudBackgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-ui-pack-sci-fi/ui/uipackSpace_sheet.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-ui-pack-sci-fi/ui/uipackSpace_sheet.json", sprite: "metalPanel_blue.png", role: "ui", category: "ui", animations: [], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
118
- features: [ { id: "f1", x: 2, y: 1, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } }, { id: "f2", x: 3, y: 2, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } }, { id: "f3", x: 4, y: 3, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } }, { id: "f4", x: 5, y: 3, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } }, { id: "f5", x: 5, y: 4, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } }, { id: "f6", x: 2, y: 4, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "sandbagBeige", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sandbagBeige", thumbnailUrl: "" } } ]
119
- assetManifest: { terrains: { ground: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/terrain/terrainTiles_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/terrain/Tilesheet_terrainTiles_default.tiles.json", sprite: "0,0", role: "tile", category: "ground", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "ground", thumbnailUrl: "" }, sand: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/terrain/terrainTiles_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/terrain/Tilesheet_terrainTiles_default.tiles.json", sprite: "0,2", role: "tile", category: "sand", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sand", thumbnailUrl: "" } }, units: { tank: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "tankBody_green.png", role: "npc", category: "tank", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank", thumbnailUrl: "" } }, features: { obstacle: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.png", atlas: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/kenney-topdown-tanks-redux/features/allSprites_default.json", sprite: "sandbagBeige.png", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, effects: { explosion: { url: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-explosion-pack/effects/spritesheet_regularExplosion.png", atlas: "https://almadar-kflow-assets.web.app/shared/_shared/kenney-explosion-pack/effects/spritesheet_regularExplosion.json", sprite: "regularExplosion04.png", role: "effect", category: "explosion", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "8:1", name: "explosion", thumbnailUrl: "" } } }
260
+ backgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/ui/panel-frame.svg", role: "ui", category: "ui", animations: [], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
261
+ hudBackgroundAsset: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/ui/panel-frame.svg", role: "ui", category: "ui", animations: [], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "panel-frame", thumbnailUrl: "" }
262
+ features: [ { id: "f1", x: 2, y: 1, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, { id: "f2", x: 3, y: 2, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, { id: "f3", x: 4, y: 3, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, { id: "f4", x: 5, y: 3, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, { id: "f5", x: 5, y: 4, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, { id: "f6", x: 2, y: 4, type: "obstacle", assetUrl: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } } ]
263
+ assetManifest: { terrains: { ground: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/terrain/ground.svg", role: "tile", category: "ground", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "ground", thumbnailUrl: "" }, sand: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/terrain/sand.svg", role: "tile", category: "sand", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "sand", thumbnailUrl: "" } }, units: { tank: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank.svg", role: "npc", category: "tank", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank", thumbnailUrl: "" }, tank_enemy: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/units/tank_enemy.svg", role: "npc", category: "tank_enemy", animations: ["idle","walk","attack","hit","death"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "tank_enemy", thumbnailUrl: "" } }, features: { obstacle: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/features/obstacle.svg", role: "decoration", category: "obstacle", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "obstacle", thumbnailUrl: "" } }, effects: { hit: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/effects/hit.svg", role: "effect", category: "hit", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "hit", thumbnailUrl: "" }, explosion: { url: "https://almadar-kflow-assets.web.app/shared/ui-tanks-board/svg/flat/effects/explosion.svg", role: "effect", category: "explosion", animations: ["static"], style: "sci-fi", dimension: "2d", aspect: "1:1", name: "explosion", thumbnailUrl: "" } } }
120
264
  }
121
265
  }
122
266
 
123
- page "/tanks-board" as TanksBoardPage -> Authority, FxDecay, RoundLogic, ScoreKeeper, Player
267
+ page "/tanks-board" as TanksBoardPage -> Authority, FxDecay, RoundLogic, ScoreKeeper, Player, AnimClock
124
268
  }