@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
@@ -123,11 +123,11 @@ orbital CrewBoard2DOrbital {
123
123
  hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: [ { label: "Crew", value: @entity.crewCount, iconUrl: @config.crewIcon }, { label: "Oxygen", value: @entity.oxygen, iconUrl: @config.oxygenIcon } ] }, { type: stack, direction: horizontal, gap: xs, align: center, children: [ { type: typography, variant: caption, content: "Status" }, { type: badge, label: @entity.phase, variant: (if (== @entity.phase "movement") "info" "neutral") } ] }, { type: timer-display, value: @entity.oxygen, label: "O2" }, { type: button, action: CANCEL, label: "Cancel", iconAsset: @config.cancelIcon, variant: secondary } ] }
124
124
  children: [ { type: canvas, camera: { zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
125
125
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
126
- { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) }
126
+ { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) }
127
127
  { type: draw-sprite-layer, items: (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl) })) }
128
128
  { type: draw-shape-layer, items: (array/map @entity.validMoves (fn m { type: draw-shape, shape: "cell", position: { x: (object/get @m x), y: (object/get @m z) }, fill: "rgba(74,222,128,0.4)" })) }
129
129
  { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (== (object/get @u id) @entity.selectedUnitId))) (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.5, radiusY: 0.2, stroke: "rgba(0,200,255,0.8)", strokeWidth: 3 })) }
130
- { type: draw-sprite-layer, items: (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5 })) }
130
+ { type: draw-sprite-layer, items: (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 1, height: 1 })) }
131
131
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
132
132
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
133
133
  { type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
@@ -166,11 +166,11 @@ orbital CrewBoard2DOrbital {
166
166
  hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: [ { label: "Crew", value: @entity.crewCount, iconUrl: @config.crewIcon }, { label: "Oxygen", value: @entity.oxygen, iconUrl: @config.oxygenIcon } ] }, { type: stack, direction: horizontal, gap: xs, align: center, children: [ { type: typography, variant: caption, content: "Status" }, { type: badge, label: @entity.phase, variant: (if (== @entity.phase "movement") "info" "neutral") } ] }, { type: timer-display, value: @entity.oxygen, label: "O2" }, { type: button, action: CANCEL, label: "Cancel", iconAsset: @config.cancelIcon, variant: secondary } ] }
167
167
  children: [ { type: canvas, camera: { zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
168
168
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
169
- { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) }
169
+ { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) }
170
170
  { type: draw-sprite-layer, items: (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl) })) }
171
171
  { type: draw-shape-layer, items: (array/map @entity.validMoves (fn m { type: draw-shape, shape: "cell", position: { x: (object/get @m x), y: (object/get @m z) }, fill: "rgba(74,222,128,0.4)" })) }
172
172
  { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (== (object/get @u id) @entity.selectedUnitId))) (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.5, radiusY: 0.2, stroke: "rgba(0,200,255,0.8)", strokeWidth: 3 })) }
173
- { type: draw-sprite-layer, items: (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5 })) }
173
+ { type: draw-sprite-layer, items: (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 1, height: 1 })) }
174
174
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
175
175
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
176
176
  { type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
@@ -195,11 +195,11 @@ orbital DungeonBoard2DOrbital {
195
195
  hud: { type: game-hud, stats: [ { label: "HP", value: @entity.hp, max: @entity.maxHp, format: "hearts" }, { label: "Score", value: (+ @entity.score @entity.combatScore) } ] }
196
196
  controls: { type: control-grid, kind: "dpad", directionEvents: { up: "MOVE_UP", down: "MOVE_DOWN", left: "MOVE_LEFT", right: "MOVE_RIGHT" }, directionAssets: @config.directionAssets }
197
197
  overlay: (if (!= @entity.result "none") { type: game-menu, title: "VICTORY", subtitle: "You found the chest!", options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } (if (!= @entity.combatResult "none") { type: game-menu, title: "DEFEAT", subtitle: "The dungeon claims another hero", options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } null))
198
- children: [ { type: canvas, camera: { zoom: @config.scale }, projection: "flat", showMinimap: false, keyMap: { ArrowUp: "MOVE_UP", KeyW: "MOVE_UP", ArrowDown: "MOVE_DOWN", KeyS: "MOVE_DOWN", ArrowLeft: "MOVE_LEFT", KeyA: "MOVE_LEFT", ArrowRight: "MOVE_RIGHT", KeyD: "MOVE_RIGHT" }, children: [
198
+ children: [ { type: canvas, fit: true, camera: { zoom: @config.scale }, projection: "flat", showMinimap: false, keyMap: { ArrowUp: "MOVE_UP", KeyW: "MOVE_UP", ArrowDown: "MOVE_DOWN", KeyS: "MOVE_DOWN", ArrowLeft: "MOVE_LEFT", KeyA: "MOVE_LEFT", ArrowRight: "MOVE_RIGHT", KeyD: "MOVE_RIGHT" }, children: [
199
199
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc" })) }
200
- { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) }
200
+ { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) }
201
201
  { type: draw-sprite-layer, items: (array/map @entity.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f sprite) })) }
202
- { type: draw-sprite-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u sprite) url) (object/get @u sprite) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), anchor: "ground", width: 0.5, height: 0.5 })) }
202
+ { type: draw-sprite-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u sprite) url) (object/get @u sprite) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), anchor: "ground", width: 1, height: 1 })) }
203
203
  { type: draw-shape-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
204
204
  { type: draw-shape-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
205
205
  { type: draw-text-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
@@ -261,11 +261,11 @@ orbital DungeonBoard2DOrbital {
261
261
  hud: { type: game-hud, stats: [ { label: "HP", value: @entity.hp, max: @entity.maxHp, format: "hearts" }, { label: "Score", value: (+ @entity.score @entity.combatScore) } ] }
262
262
  controls: { type: control-grid, kind: "dpad", directionEvents: { up: "MOVE_UP", down: "MOVE_DOWN", left: "MOVE_LEFT", right: "MOVE_RIGHT" }, directionAssets: @config.directionAssets }
263
263
  overlay: (if (!= @entity.result "none") { type: game-menu, title: "VICTORY", subtitle: "You found the chest!", options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } (if (!= @entity.combatResult "none") { type: game-menu, title: "DEFEAT", subtitle: "The dungeon claims another hero", options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } null))
264
- children: [ { type: canvas, camera: { zoom: @config.scale }, projection: "flat", showMinimap: false, keyMap: { ArrowUp: "MOVE_UP", KeyW: "MOVE_UP", ArrowDown: "MOVE_DOWN", KeyS: "MOVE_DOWN", ArrowLeft: "MOVE_LEFT", KeyA: "MOVE_LEFT", ArrowRight: "MOVE_RIGHT", KeyD: "MOVE_RIGHT" }, children: [
264
+ children: [ { type: canvas, fit: true, camera: { zoom: @config.scale }, projection: "flat", showMinimap: false, keyMap: { ArrowUp: "MOVE_UP", KeyW: "MOVE_UP", ArrowDown: "MOVE_DOWN", KeyS: "MOVE_DOWN", ArrowLeft: "MOVE_LEFT", KeyA: "MOVE_LEFT", ArrowRight: "MOVE_RIGHT", KeyD: "MOVE_RIGHT" }, children: [
265
265
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc" })) }
266
- { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) }
266
+ { type: draw-sprite-layer, items: (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) }
267
267
  { type: draw-sprite-layer, items: (array/map @entity.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f sprite) })) }
268
- { type: draw-sprite-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u sprite) url) (object/get @u sprite) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), anchor: "ground", width: 0.5, height: 0.5 })) }
268
+ { type: draw-sprite-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u sprite) url) (object/get @u sprite) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), anchor: "ground", width: 1, height: 1 })) }
269
269
  { type: draw-shape-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
270
270
  { type: draw-shape-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
271
271
  { type: draw-text-layer, items: (array/map (array/concat @entity.units @entity.enemies) (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
@@ -5,6 +5,7 @@ orbital TacticsBoard2DOrbital {
5
5
  uses Particles from "std/behaviors/std-fx-particles"
6
6
  uses Round from "std/behaviors/std-round-flow"
7
7
  uses Score from "std/behaviors/std-score"
8
+ uses AnimTick from "std/behaviors/std-anim-tick"
8
9
 
9
10
  type Asset = {
10
11
  url : asset
@@ -70,6 +71,7 @@ orbital TacticsBoard2DOrbital {
70
71
  type HudStat = {
71
72
  label : string
72
73
  value : number
74
+ format : string
73
75
  }
74
76
  type HudButton = {
75
77
  type : string
@@ -96,10 +98,19 @@ orbital TacticsBoard2DOrbital {
96
98
  selectedUnitId : string = ""
97
99
  validMoves : [ValidMove] = []
98
100
  attackTargets : [ValidMove] = []
101
+ movedIds : [string] = []
102
+ actedIds : [string] = []
99
103
  uiPhase : string = "observation"
100
104
  turn : number = 0
101
105
  score : number = 0
102
106
  scoreBaseline : number = 0
107
+ animCounter : number = 0
108
+ frameName : string = "0"
109
+ animUnitId : string = ""
110
+ animState : string = "idle"
111
+ animTicksLeft : number = 0
112
+ animAttackerId : string = ""
113
+ animAtkTicksLeft : number = 0
103
114
  }
104
115
 
105
116
  trait TacticsAuthority = Tactics.traits.GridTactics -> GameState {
@@ -133,6 +144,10 @@ orbital TacticsBoard2DOrbital {
133
144
  config { running: true, pointsPerKill: 10, startScore: 0, initialEnemies: 0 }
134
145
  }
135
146
 
147
+ trait AnimClock = AnimTick.traits.AnimTick -> GameState {
148
+ config { frames: [] }
149
+ }
150
+
136
151
  trait PlayerIntent -> GameState [interaction, instance] {
137
152
  initial: playing
138
153
 
@@ -147,47 +162,52 @@ orbital TacticsBoard2DOrbital {
147
162
  type: game-shell
148
163
  backgroundAsset: @config.backgroundAsset
149
164
  hudBackgroundAsset: @config.hudBackgroundAsset
150
- hud: { type: game-hud, stats: (array/concat [ { label: "Result", value: @entity.result }, { label: "Phase", value: @entity.uiPhase }, { label: "Turn", value: @entity.turn }, { label: "Score", value: @entity.score } ] @config.extraStats) }
165
+ hud: { type: game-hud, stats: (array/concat [ { label: "Result", value: @entity.result, format: "text" }, { label: "Phase", value: @entity.uiPhase, format: "text" }, { label: "Turn", value: @entity.turn }, { label: "Score", value: @entity.score } ] @config.extraStats) }
151
166
  addons: { type: stack, direction: vertical, gap: sm, children: (array/concat [ { type: button, action: END_TURN, label: "End Turn", variant: primary }, { type: button, action: CANCEL, label: "Cancel", variant: secondary } ] @config.extraAddons) }
152
- children: [ { type: canvas, camera: { zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
167
+ children: [ { type: canvas, fit: true, camera: { pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
153
168
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
154
- { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
155
- { type: draw-sprite-layer, items: (array/filter (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl) })) (fn s (object/get (object/get @s asset) url))) }
169
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) (fn s (object/get (object/get @s asset) url))) }
170
+ { type: draw-sprite-layer, items: (array/filter (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl), anchor: "ground", width: 1, height: 1 })) (fn s (object/get (object/get @s asset) url))) }
156
171
  { type: draw-shape-layer, items: (array/map @entity.validMoves (fn m { type: draw-shape, shape: "cell", position: { x: (object/get @m x), y: (object/get @m z) }, fill: "rgba(74,222,128,0.4)" })) }
157
172
  { type: draw-shape-layer, items: (array/map @entity.attackTargets (fn a { type: draw-shape, shape: "cell", position: { x: (object/get @a x), y: (object/get @a z) }, fill: "rgba(239,68,68,0.4)" })) }
158
173
  { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (== (object/get @u id) @entity.selectedUnitId))) (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.5, radiusY: 0.2, stroke: "rgba(0,200,255,0.8)", strokeWidth: 3 })) }
159
- { type: draw-sprite-layer, items: (array/filter (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
160
- { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
161
- { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
162
- { type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
163
- { type: draw-text-layer, items: (array/map @entity.fx (fn f { type: draw-text, text: (object/get @f message), position: { x: (object/get @f x), y: (object/get @f y) }, color: "#ffe066" })) }
174
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.units (fn u (let ((unitAsset (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType)))) (unitUrl (object/get @unitAsset url)) (unitAnim (if (<= (object/get @u health) 0) "death" (if (and (== (object/get @u id) @entity.animAttackerId) (> @entity.animAtkTicksLeft 0)) "attack" (if (== (object/get @u id) @entity.animUnitId) @entity.animState "idle"))))) { type: draw-sprite, position: (object/get @u position), asset: (if (and @unitUrl (str/includes @unitUrl ".svg")) (object/merge @unitAsset { url: (str/concat (array/nth (str/split @unitUrl ".svg") 0) (str/concat "." (str/concat @unitAnim (str/concat "-" (str/concat (if (== @unitAnim "walk") (% @entity.animCounter 2) (if (== @unitAnim "idle") (% (math/floor (/ @entity.animCounter 3)) 4) 0))) ".svg")))) }) @unitAsset), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5, opacity: (if (> (array/len (array/filter @entity.actedIds (fn aid (== @aid (object/get @u id))))) 0) 0.45 1) }))) (fn s (object/get (object/get @s asset) url))) }
175
+ { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
176
+ { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
177
+ { type: draw-text-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
178
+ { type: draw-text-layer, items: (array/map @entity.fx (fn f { type: draw-text, text: (object/get @f message), position: { x: (object/get @f x), y: (object/get @f z) }, color: "#ffe066" })) }
179
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.fx (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f z) }, asset: (object/get (object/get @config.assetManifest effects) (object/get @f type)), anchor: "center", width: 0.6, height: 0.6 })) (fn s (object/get (object/get @s asset) url))) }
180
+ { type: draw-sprite-layer, items: (array/map (array/filter (array/flatten (array/map @entity.units (fn u (array/flatten (array/map [ ["idle", 4], ["walk", 2], ["attack", 1], ["hit", 1], ["death", 1] ] (fn st (array/map (array/range 0 (array/nth @st 1)) (fn i (if (and (object/get (object/get @u modelUrl) url) (str/includes (object/get (object/get @u modelUrl) url) ".svg")) (str/concat (array/nth (str/split (object/get (object/get @u modelUrl) url) ".svg") 0) (str/concat "." (str/concat (array/nth @st 0) (str/concat "-" (str/concat @i ".svg"))))) ""))))))))) (fn w (!= @w ""))) (fn warmUrl { type: draw-sprite, position: { x: 0, y: 0 }, asset: { url: @warmUrl }, opacity: 0, width: 0.5, height: 0.5 })) }
164
181
  ] } ]
165
182
  })
166
183
 
167
- UNIT_CLICK -> playing when (and (== @entity.uiPhase "action") (and (== (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) team) "enemy") (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id @entity.selectedUnitId))) position) (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) position)) 1)))
184
+ UNIT_CLICK -> playing when (and (== @entity.result "none") (and (!= @entity.selectedUnitId "") (and (== (array/len (array/filter @entity.actedIds (fn aid (== @aid @entity.selectedUnitId)))) 0) (and (== (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) team) "enemy") (and (> (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) health) 0) (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id @entity.selectedUnitId))) position) (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) position)) @config.attackRange))))))
168
185
  (emit ATTACK { targetId: ?unitId })
186
+ (set @entity.actedIds (array/append @entity.actedIds @entity.selectedUnitId))
187
+ (set @entity.animAttackerId @entity.selectedUnitId)
188
+ (set @entity.animAtkTicksLeft 4)
169
189
  (set @entity.selectedUnitId "")
170
190
  (set @entity.validMoves [])
171
191
  (set @entity.attackTargets [])
172
192
  (set @entity.uiPhase "observation")
173
193
 
174
- UNIT_CLICK -> playing when (== @entity.selectedUnitId ?unitId)
194
+ UNIT_CLICK -> playing when (and (== @entity.result "none") (== @entity.selectedUnitId ?unitId))
175
195
  (set @entity.uiPhase "action")
176
196
  (set @entity.validMoves [])
177
- (set @entity.attackTargets (array/map (array/filter @entity.units (fn u (and (== @u.team "enemy") (and (> @u.health 0) (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn s (== @s.id @entity.selectedUnitId))) position) (object/get @u position)) 1))))) (fn u { x: (object/get (object/get @u position) x), z: (object/get (object/get @u position) y) })))
197
+ (set @entity.attackTargets (array/map (array/filter @entity.units (fn u (and (== @u.team "enemy") (and (> @u.health 0) (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn s (== @s.id @entity.selectedUnitId))) position) (object/get @u position)) @config.attackRange))))) (fn u { x: (object/get (object/get @u position) x), z: (object/get (object/get @u position) y) })))
178
198
 
179
- UNIT_CLICK -> playing when (== (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) team) "player")
199
+ UNIT_CLICK -> playing when (and (== @entity.result "none") (and (> (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) health) 0) (and (== (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) team) "player") (== (array/len (array/filter @entity.actedIds (fn aid (== @aid ?unitId)))) 0))))
180
200
  (set @entity.selectedUnitId ?unitId)
181
201
  (set @entity.uiPhase "selection")
182
202
  (set @entity.attackTargets [])
183
- (set @entity.validMoves (array/map (array/filter @entity.tiles (fn t (and (== (object/get @t passable) true) (and (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) position) { x: (object/get @t x), y: (object/get @t y) }) @config.movementRange) (== (array/len (array/filter @entity.units (fn u (and (== (object/get (object/get @u position) x) (object/get @t x)) (== (object/get (object/get @u position) y) (object/get @t y)))))) 0))))) (fn t { x: (object/get @t x), z: (object/get @t y) })))
203
+ (set @entity.validMoves (if (> (array/len (array/filter @entity.movedIds (fn aid (== @aid ?unitId)))) 0) [] (array/map (array/filter @entity.tiles (fn t (and (== (object/get @t passable) true) (and (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id ?unitId))) position) { x: (object/get @t x), y: (object/get @t y) }) @config.movementRange) (== (array/len (array/filter @entity.units (fn u (and (> (object/get @u health) 0) (and (== (object/get (object/get @u position) x) (object/get @t x)) (== (object/get (object/get @u position) y) (object/get @t y))))))) 0))))) (fn t { x: (object/get @t x), z: (object/get @t y) }))))
184
204
 
185
- TILE_CLICK -> playing when (and (!= @entity.selectedUnitId "") (and (grid/in-bounds { x: ?x, y: ?y } @config.gridWidth @config.gridHeight) (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id @entity.selectedUnitId))) position) { x: ?x, y: ?y }) @config.movementRange)))
205
+ TILE_CLICK -> playing when (and (== @entity.result "none") (and (!= @entity.selectedUnitId "") (and (== (array/len (array/filter @entity.movedIds (fn aid (== @aid @entity.selectedUnitId)))) 0) (and (> (object/get (array/find @entity.units (fn u (== @u.id @entity.selectedUnitId))) health) 0) (and (grid/in-bounds { x: ?x, y: ?y } @config.gridWidth @config.gridHeight) (and (<= (grid/manhattan-distance (object/get (array/find @entity.units (fn u (== @u.id @entity.selectedUnitId))) position) { x: ?x, y: ?y }) @config.movementRange) (and (== (array/len (array/filter @entity.units (fn u (and (> (object/get @u health) 0) (and (== (object/get (object/get @u position) x) ?x) (== (object/get (object/get @u position) y) ?y)))))) 0) (== (object/get (array/find @entity.tiles (fn t (and (== (object/get @t x) ?x) (== (object/get @t y) ?y)))) passable) true))))))))
186
206
  (emit MOVE { unitId: @entity.selectedUnitId, x: ?x, y: ?y })
187
- (set @entity.selectedUnitId "")
207
+ (set @entity.movedIds (array/append @entity.movedIds @entity.selectedUnitId))
208
+ (set @entity.uiPhase "action")
188
209
  (set @entity.validMoves [])
189
- (set @entity.attackTargets [])
190
- (set @entity.uiPhase "observation")
210
+ (set @entity.attackTargets (array/map (array/filter @entity.units (fn u (and (== @u.team "enemy") (and (> @u.health 0) (<= (grid/manhattan-distance { x: ?x, y: ?y } (object/get @u position)) @config.attackRange))))) (fn u { x: (object/get (object/get @u position) x), z: (object/get (object/get @u position) y) })))
191
211
 
192
212
  CANCEL -> playing when (!= @entity.selectedUnitId "")
193
213
  (set @entity.selectedUnitId "")
@@ -197,29 +217,51 @@ orbital TacticsBoard2DOrbital {
197
217
 
198
218
  END_TURN -> playing when (== @entity.result "none")
199
219
  (set @entity.turn (+ @entity.turn 1))
220
+ (set @entity.movedIds [])
221
+ (set @entity.actedIds [])
222
+ (set @entity.selectedUnitId "")
223
+ (set @entity.validMoves [])
224
+ (set @entity.attackTargets [])
225
+ (set @entity.uiPhase "observation")
200
226
  (emit AI_TURN { turn: @entity.turn })
227
+
228
+ MOVE -> playing
229
+ (set @entity.animUnitId ?unitId)
230
+ (set @entity.animState "walk")
231
+ (set @entity.animTicksLeft 6)
232
+
233
+ ATTACK -> playing
234
+ (set @entity.animUnitId ?targetId)
235
+ (set @entity.animState "hit")
236
+ (set @entity.animTicksLeft 4)
201
237
  }
202
238
 
203
239
  ticks {
204
240
  renderTick every 100ms
241
+ (set @entity.animTicksLeft (if (> @entity.animTicksLeft 0) (- @entity.animTicksLeft 1) 0))
242
+ (set @entity.animAtkTicksLeft (if (> @entity.animAtkTicksLeft 0) (- @entity.animAtkTicksLeft 1) 0))
243
+ (set @entity.animState (if (== @entity.animTicksLeft 0) "idle" @entity.animState))
244
+ (set @entity.animUnitId (if (== @entity.animTicksLeft 0) "" @entity.animUnitId))
245
+ (set @entity.animAttackerId (if (== @entity.animAtkTicksLeft 0) "" @entity.animAttackerId))
205
246
  (render-ui main {
206
247
  type: game-shell
207
248
  backgroundAsset: @config.backgroundAsset
208
249
  hudBackgroundAsset: @config.hudBackgroundAsset
209
- hud: { type: game-hud, stats: (array/concat [ { label: "Result", value: @entity.result }, { label: "Phase", value: @entity.uiPhase }, { label: "Turn", value: @entity.turn }, { label: "Score", value: @entity.score } ] @config.extraStats) }
250
+ hud: { type: game-hud, stats: (array/concat [ { label: "Result", value: @entity.result, format: "text" }, { label: "Phase", value: @entity.uiPhase, format: "text" }, { label: "Turn", value: @entity.turn }, { label: "Score", value: @entity.score } ] @config.extraStats) }
210
251
  addons: { type: stack, direction: vertical, gap: sm, children: (array/concat [ { type: button, action: END_TURN, label: "End Turn", variant: primary }, { type: button, action: CANCEL, label: "Cancel", variant: secondary } ] @config.extraAddons) }
211
- children: [ { type: canvas, camera: { zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
252
+ children: [ { type: canvas, fit: true, camera: { pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: @config.projection, showMinimap: @config.showMinimap, tileClickEvent: TILE_CLICK, unitClickEvent: UNIT_CLICK, children: [
212
253
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
213
- { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
214
- { type: draw-sprite-layer, items: (array/filter (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl) })) (fn s (object/get (object/get @s asset) url))) }
254
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 1, height: 1 })) (fn s (object/get (object/get @s asset) url))) }
255
+ { type: draw-sprite-layer, items: (array/filter (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f assetUrl), anchor: "ground", width: 1, height: 1 })) (fn s (object/get (object/get @s asset) url))) }
215
256
  { type: draw-shape-layer, items: (array/map @entity.validMoves (fn m { type: draw-shape, shape: "cell", position: { x: (object/get @m x), y: (object/get @m z) }, fill: "rgba(74,222,128,0.4)" })) }
216
257
  { type: draw-shape-layer, items: (array/map @entity.attackTargets (fn a { type: draw-shape, shape: "cell", position: { x: (object/get @a x), y: (object/get @a z) }, fill: "rgba(239,68,68,0.4)" })) }
217
258
  { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (== (object/get @u id) @entity.selectedUnitId))) (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.5, radiusY: 0.2, stroke: "rgba(0,200,255,0.8)", strokeWidth: 3 })) }
218
- { type: draw-sprite-layer, items: (array/filter (array/map @entity.units (fn u { type: draw-sprite, position: (object/get @u position), asset: (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType))), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
219
- { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
220
- { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
221
- { type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
222
- { type: draw-text-layer, items: (array/map @entity.fx (fn f { type: draw-text, text: (object/get @f message), position: { x: (object/get @f x), y: (object/get @f y) }, color: "#ffe066" })) }
259
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.units (fn u (let ((unitAsset (if (object/get (object/get @u modelUrl) url) (object/get @u modelUrl) (object/get (object/get @config.assetManifest units) (object/get @u unitType)))) (unitUrl (object/get @unitAsset url)) (unitAnim (if (<= (object/get @u health) 0) "death" (if (and (== (object/get @u id) @entity.animAttackerId) (> @entity.animAtkTicksLeft 0)) "attack" (if (== (object/get @u id) @entity.animUnitId) @entity.animState "idle"))))) { type: draw-sprite, position: (object/get @u position), asset: (if (and @unitUrl (str/includes @unitUrl ".svg")) (object/merge @unitAsset { url: (str/concat (array/nth (str/split @unitUrl ".svg") 0) (str/concat "." (str/concat @unitAnim (str/concat "-" (str/concat (if (== @unitAnim "walk") (% @entity.animCounter 2) (if (== @unitAnim "idle") (% (math/floor (/ @entity.animCounter 3)) 4) 0))) ".svg")))) }) @unitAsset), id: (object/get @u id), anchor: "ground", width: 0.5, height: 0.5, opacity: (if (> (array/len (array/filter @entity.actedIds (fn aid (== @aid (object/get @u id))))) 0) 0.45 1) }))) (fn s (object/get (object/get @s asset) url))) }
260
+ { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: 0.5, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
261
+ { type: draw-shape-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), anchor: "ground", offsetX: -0.25, offsetY: -0.6, width: (* 0.5 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
262
+ { type: draw-text-layer, items: (array/map (array/filter @entity.units (fn u (> (object/get @u health) 0))) (fn u { type: draw-text, text: (object/get @u name), position: (object/get @u position), offsetY: -0.85, color: "#ffffff" })) }
263
+ { type: draw-text-layer, items: (array/map @entity.fx (fn f { type: draw-text, text: (object/get @f message), position: { x: (object/get @f x), y: (object/get @f z) }, color: "#ffe066" })) }
264
+ { type: draw-sprite-layer, items: (array/filter (array/map @entity.fx (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f z) }, asset: (object/get (object/get @config.assetManifest effects) (object/get @f type)), anchor: "center", width: 0.6, height: 0.6 })) (fn s (object/get (object/get @s asset) url))) }
223
265
  ] } ]
224
266
  })
225
267
  }
@@ -278,6 +320,10 @@ orbital TacticsBoard2DOrbital {
278
320
  @label "Movement Range"
279
321
  @description "Max manhattan distance a selected unit may move."
280
322
  @tier "domain"
323
+ attackRange : number = 1
324
+ @label "Attack Range"
325
+ @description "Max manhattan distance a selected unit may strike an enemy."
326
+ @tier "domain"
281
327
  features : [Feature] = []
282
328
  @label "Features"
283
329
  @description "Decorative environment features drawn on the board."
@@ -321,5 +367,5 @@ orbital TacticsBoard2DOrbital {
321
367
  }
322
368
  }
323
369
 
324
- page "/tactics-board-2d" as TacticsBoard2DPage -> TacticsAuthority, FxDecay, RoundLogic, ScoreKeeper, PlayerIntent
370
+ page "/tactics-board-2d" as TacticsBoard2DPage -> TacticsAuthority, FxDecay, RoundLogic, ScoreKeeper, PlayerIntent, AnimClock
325
371
  }
@@ -147,7 +147,7 @@ orbital WorldMapBoard2DOrbital {
147
147
  hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: (array/concat [ { label: "Region", value: @config.region, iconUrl: @config.regionIconAsset }, { label: "Turn", value: @entity.turn, iconUrl: @config.turnIconAsset }, { label: "Gold", value: @entity.gold, iconUrl: @config.goldIconAsset } ] @config.extraStats) }, { type: stack, direction: horizontal, gap: xs, align: center, children: [ { type: icon, name: map-pin }, { type: typography, variant: caption, content: "Waypoints" } ] } ] }
148
148
  addons: { type: stack, direction: vertical, gap: sm, children: (array/concat [ { type: button, action: ADVANCE_TURN, label: "Pass Turn", iconAsset: @config.travelIconAsset, variant: primary } ] @config.extraAddons) }
149
149
  overlay: (if (!= @entity.result "none") { type: game-menu, title: (if (== @entity.result "victory") "VICTORY" "DEFEAT"), subtitle: (if (== @entity.result "victory") "The realm is secured" "Your heroes have fallen"), options: [] } null)
150
- children: [ { type: canvas, camera: { mode: "isometric", zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: "isometric", showMinimap: true, tileClickEvent: HERO_MOVE, unitClickEvent: HERO_SELECT, children: [
150
+ children: [ { type: canvas, fit: true, camera: { pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: "isometric", showMinimap: true, tileClickEvent: HERO_MOVE, unitClickEvent: HERO_SELECT, children: [
151
151
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
152
152
  { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
153
153
  { type: draw-sprite-layer, items: (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f sprite) })) }
@@ -195,7 +195,7 @@ orbital WorldMapBoard2DOrbital {
195
195
  hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: (array/concat [ { label: "Region", value: @config.region, iconUrl: @config.regionIconAsset }, { label: "Turn", value: @entity.turn, iconUrl: @config.turnIconAsset }, { label: "Gold", value: @entity.gold, iconUrl: @config.goldIconAsset } ] @config.extraStats) }, { type: stack, direction: horizontal, gap: xs, align: center, children: [ { type: icon, name: map-pin }, { type: typography, variant: caption, content: "Waypoints" } ] } ] }
196
196
  addons: { type: stack, direction: vertical, gap: sm, children: (array/concat [ { type: button, action: ADVANCE_TURN, label: "Pass Turn", iconAsset: @config.travelIconAsset, variant: primary } ] @config.extraAddons) }
197
197
  overlay: (if (!= @entity.result "none") { type: game-menu, title: (if (== @entity.result "victory") "VICTORY" "DEFEAT"), subtitle: (if (== @entity.result "victory") "The realm is secured" "Your heroes have fallen"), options: [] } null)
198
- children: [ { type: canvas, camera: { mode: "isometric", zoom: (math/min 1 (/ 5 (+ 1 (math/max (array/max @entity.tiles "x") (array/max @entity.tiles "y"))))), pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: "isometric", showMinimap: true, tileClickEvent: HERO_MOVE, unitClickEvent: HERO_SELECT, children: [
198
+ children: [ { type: canvas, fit: true, camera: { pos: { x: (/ (+ (array/min @entity.tiles "x") (array/max @entity.tiles "x")) 2), y: (/ (+ (array/min @entity.tiles "y") (array/max @entity.tiles "y")) 2) } }, projection: "isometric", showMinimap: true, tileClickEvent: HERO_MOVE, unitClickEvent: HERO_SELECT, children: [
199
199
  { type: draw-shape-layer, items: (array/map @entity.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#5c94fc", stroke: "rgba(255,255,255,0.35)", strokeWidth: 1 })) }
200
200
  { type: draw-sprite-layer, items: (array/filter (array/map @entity.tiles (fn t { type: draw-sprite, position: { x: (object/get @t x), y: (object/get @t y) }, asset: (if (object/get (object/get @t terrainSprite) url) (object/get @t terrainSprite) (object/get (object/get @config.assetManifest terrains) (object/get @t terrain))), anchor: "center", width: 0.5, height: 0.5 })) (fn s (object/get (object/get @s asset) url))) }
201
201
  { type: draw-sprite-layer, items: (array/map @config.features (fn f { type: draw-sprite, position: { x: (object/get @f x), y: (object/get @f y) }, asset: (object/get @f sprite) })) }
@@ -13,6 +13,7 @@ orbital CanvasOrbital {
13
13
  (render-ui main {
14
14
  mode: @config.mode
15
15
  drawables: @config.drawables
16
+ children: @config.children
16
17
  camera: @config.camera
17
18
  projection: @config.projection
18
19
  className: @config.className
@@ -82,6 +83,10 @@ orbital CanvasOrbital {
82
83
  @label "Drawables"
83
84
  @description "Neutral drawable descriptors — the `draw-*` children. Routed to the host's painter."
84
85
  @tier "presentation"
86
+ children : node
87
+ @label "Children (drawable composition)"
88
+ @description "Declarative JSX drawable children — semantic atom invocations (`<SvgPrimitive/>`, `<SvgShading/>`, …) composed in paint order. The canvas is a draw-host: these children are routed to the painter as drawable descriptors, not DOM-wrapped. Use this to author a high-fidelity asset as a readable, nested JSX tree an LLM can rasterize."
89
+ @tier "presentation"
85
90
  camera : json = {}
86
91
  @label "Camera"
87
92
  @description "Neutral camera pose. Mapped to the underlying host's framing + zoom."
@@ -42,7 +42,7 @@ orbital ArenaBoardOrbital {
42
42
  (render-ui main {
43
43
  type: game-shell
44
44
  hud: { type: game-hud, stats: [ { label: "Result", value: @entity.result } ] }
45
- children: [ { type: canvas, projection: "flat", showMinimap: false, children: [
45
+ children: [ { type: canvas, fit: true, projection: "flat", showMinimap: false, children: [
46
46
  { type: draw-shape-layer, items: (array/map @config.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#4ade80", stroke: "rgba(0,0,0,0.2)" })) }
47
47
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.4, radiusY: 0.4, fill: (if (== (object/get @u team) "player") "#3b82f6" "#ef4444") })) }
48
48
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), offsetY: -0.6, width: 0.8, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }
@@ -57,7 +57,7 @@ orbital ArenaBoardOrbital {
57
57
  (render-ui main {
58
58
  type: game-shell
59
59
  hud: { type: game-hud, stats: [ { label: "Result", value: @entity.result } ] }
60
- children: [ { type: canvas, projection: "flat", showMinimap: false, children: [
60
+ children: [ { type: canvas, fit: true, projection: "flat", showMinimap: false, children: [
61
61
  { type: draw-shape-layer, items: (array/map @config.tiles (fn t { type: draw-shape, shape: "cell", position: { x: (object/get @t x), y: (object/get @t y) }, fill: "#4ade80", stroke: "rgba(0,0,0,0.2)" })) }
62
62
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "ellipse", position: (object/get @u position), radiusX: 0.4, radiusY: 0.4, fill: (if (== (object/get @u team) "player") "#3b82f6" "#ef4444") })) }
63
63
  { type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: (object/get @u position), offsetY: -0.6, width: 0.8, height: 0.12, fill: "rgba(0,0,0,0.6)" })) }