@almadar/std 16.178.0 → 16.180.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.
- package/behaviors/lolo/ui/core/atoms/std-detail-layout.lolo +82 -0
- package/behaviors/lolo/ui/core/molecules/ui-breadcrumb.lolo +20 -2
- package/behaviors/lolo/ui/core/molecules/ui-fx-overlay.lolo +63 -0
- package/behaviors/lolo/ui/core/molecules/ui-page-header.lolo +1 -1
- package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +6 -0
- package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +2 -2
- package/behaviors/lolo/ui/core/organisms/ui-form.lolo +2 -2
- package/behaviors/lolo/ui/core/templates/ui-dashboard-layout.lolo +2 -2
- package/behaviors/lolo/ui/game/atoms/std-fx-particles.lolo +23 -4
- package/behaviors/lolo/ui/game/atoms/std-platformer-body.lolo +251 -0
- package/behaviors/lolo/ui/game/atoms/ui-draw-fx-layer.lolo +109 -0
- package/behaviors/lolo/ui/game/organisms/std-arcade-board-3d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-crew-board-2d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-dungeon-board-2d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-market-board-3d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-objective-board-3d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-platformer-board-2d.lolo +391 -0
- package/behaviors/lolo/ui/game/organisms/std-survival-board-3d.lolo +32 -2
- package/behaviors/lolo/ui/game/organisms/std-tactics-board-2d.lolo +34 -6
- package/behaviors/lolo/ui/game/organisms/std-tactics-board-3d.lolo +34 -4
- package/behaviors/lolo/ui/learning/atoms/std-learn-arrangement.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +9 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-fx-cues.lolo +147 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +9 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +8 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +9 -0
- package/behaviors/registry/ui/core/atoms/std-detail-layout.orb +265 -0
- package/behaviors/registry/ui/core/molecules/ui-breadcrumb.orb +55 -17
- package/behaviors/registry/ui/core/molecules/ui-fx-overlay.orb +281 -0
- package/behaviors/registry/ui/core/molecules/ui-page-header.orb +1 -1
- package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +8 -0
- package/behaviors/registry/ui/core/organisms/ui-form-section.orb +6 -0
- package/behaviors/registry/ui/core/organisms/ui-form.orb +6 -0
- package/behaviors/registry/ui/core/templates/ui-dashboard-layout.orb +2 -2
- package/behaviors/registry/ui/game/atoms/std-fx-particles.orb +265 -4
- package/behaviors/registry/ui/game/atoms/std-platformer-body.orb +2136 -0
- package/behaviors/registry/ui/game/atoms/ui-draw-fx-layer.orb +523 -0
- package/behaviors/registry/ui/game/organisms/std-arcade-board-3d.orb +159 -70
- package/behaviors/registry/ui/game/organisms/std-crew-board-2d.orb +148 -58
- package/behaviors/registry/ui/game/organisms/std-dungeon-board-2d.orb +169 -58
- package/behaviors/registry/ui/game/organisms/std-market-board-3d.orb +160 -70
- package/behaviors/registry/ui/game/organisms/std-objective-board-3d.orb +151 -60
- package/behaviors/registry/ui/game/organisms/std-platformer-board-2d.orb +2981 -0
- package/behaviors/registry/ui/game/organisms/std-survival-board-3d.orb +150 -60
- package/behaviors/registry/ui/game/organisms/std-tactics-board-2d.orb +163 -316
- package/behaviors/registry/ui/game/organisms/std-tactics-board-3d.orb +156 -184
- package/behaviors/registry/ui/learning/atoms/std-learn-arrangement.orb +62 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +73 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +66 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +71 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +72 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-fx-cues.orb +1546 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +58 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +72 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +72 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +71 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +72 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +176 -0
- package/canonical-operators.json +14 -2
- package/dist/behaviors/exports-reader.js +8655 -5239
- package/dist/behaviors/functions/index.d.ts +686 -16
- package/dist/behaviors/functions/index.js +8637 -5240
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +8656 -5240
- package/dist/behaviors/query.js +8655 -5239
- package/dist/behaviors-embeddings.hash.json +3 -3
- package/dist/behaviors-embeddings.json +381 -353
- package/dist/behaviors-registry.json +2415 -443
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8656 -5240
- package/dist/knob-embeddings.hash.json +3 -3
- package/dist/knob-embeddings.json +1 -1
- package/dist/registry/factory-signatures.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
app std-platformer-board-2d "1.0.0"
|
|
2
|
+
"2D run/jump platformer board FRAME: composes platformer-body (run/jump physics, one-way AABB collision, hazard/goal/fall signals) + fx-particles + objective-flow (win/lose gate) onto one shared GameState with a side-view canvas render. The intent trait owns only game RULES — input forwarding (LEFT/RIGHT/STOP/JUMP → STEER/JUMP intents), goal scoring, hazard/pit lives with respawn, PLAY_AGAIN reset — physics, particles, and the win gate belong to the atoms. Games consume this organism and supply level geometry (platforms, in both BodyAuthority.platforms for collision and the render-side platforms knob), theme/assets, and physics feel via config/traitOverrides."
|
|
3
|
+
orbital PlatformerBoard2DOrbital {
|
|
4
|
+
uses Body from "std/behaviors/std-platformer-body"
|
|
5
|
+
uses Particles from "std/behaviors/std-fx-particles"
|
|
6
|
+
uses Gate from "std/behaviors/std-objective-flow"
|
|
7
|
+
|
|
8
|
+
type Asset = {
|
|
9
|
+
url : asset
|
|
10
|
+
atlas : asset
|
|
11
|
+
sprite : string
|
|
12
|
+
role : string
|
|
13
|
+
category : string
|
|
14
|
+
animations : [string]
|
|
15
|
+
style : string
|
|
16
|
+
variant : string
|
|
17
|
+
dimension : string
|
|
18
|
+
aspect : string
|
|
19
|
+
name : string
|
|
20
|
+
thumbnailUrl : string
|
|
21
|
+
}
|
|
22
|
+
type PlatformerBody = {
|
|
23
|
+
x : number!
|
|
24
|
+
y : number!
|
|
25
|
+
width : number!
|
|
26
|
+
height : number!
|
|
27
|
+
vx : number
|
|
28
|
+
vy : number
|
|
29
|
+
grounded : boolean
|
|
30
|
+
facingRight : boolean
|
|
31
|
+
}
|
|
32
|
+
type PlatformerPlatform = {
|
|
33
|
+
id : string
|
|
34
|
+
x : number!
|
|
35
|
+
y : number!
|
|
36
|
+
width : number!
|
|
37
|
+
height : number!
|
|
38
|
+
type : "ground" | "platform" | "hazard" | "goal"
|
|
39
|
+
}
|
|
40
|
+
type Fx = {
|
|
41
|
+
id : string!
|
|
42
|
+
type : string!
|
|
43
|
+
x : number!
|
|
44
|
+
z : number
|
|
45
|
+
y : number
|
|
46
|
+
message : string
|
|
47
|
+
ttl : number!
|
|
48
|
+
maxTtl : number
|
|
49
|
+
bornAt : number
|
|
50
|
+
space : string
|
|
51
|
+
effect : string
|
|
52
|
+
color : string
|
|
53
|
+
size : number
|
|
54
|
+
vx : number
|
|
55
|
+
vy : number
|
|
56
|
+
vz : number
|
|
57
|
+
particleCount : number
|
|
58
|
+
}
|
|
59
|
+
type FxPreset = {
|
|
60
|
+
type : string!
|
|
61
|
+
color : string
|
|
62
|
+
color2 : string
|
|
63
|
+
size : number
|
|
64
|
+
shape : string
|
|
65
|
+
count : number
|
|
66
|
+
glow : number
|
|
67
|
+
gravity : number
|
|
68
|
+
vx : number
|
|
69
|
+
vy : number
|
|
70
|
+
vz : number
|
|
71
|
+
space : string
|
|
72
|
+
effect : string
|
|
73
|
+
particleCount : number
|
|
74
|
+
}
|
|
75
|
+
type DirectionAssets = {
|
|
76
|
+
up : Asset
|
|
77
|
+
down : Asset
|
|
78
|
+
left : Asset
|
|
79
|
+
right : Asset
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
entity GameState [runtime, shared] {
|
|
83
|
+
id : string!
|
|
84
|
+
body : PlatformerBody = { x: 80, y: 320, width: 32, height: 48, vx: 0, vy: 0, grounded: false, facingRight: true }
|
|
85
|
+
fell : boolean = false
|
|
86
|
+
hazardId : string = ""
|
|
87
|
+
goalId : string = ""
|
|
88
|
+
skating : boolean = false
|
|
89
|
+
fx : [Fx] = []
|
|
90
|
+
result : string = "none"
|
|
91
|
+
score : number = 0
|
|
92
|
+
lives : number = 3
|
|
93
|
+
objectivesLeft : number = 1
|
|
94
|
+
frame : number = 0
|
|
95
|
+
celebrated : boolean = false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
trait BodyAuthority = Body.traits.PlatformerBody -> GameState {
|
|
99
|
+
events {
|
|
100
|
+
STEER: PLAT_STEER
|
|
101
|
+
JUMP: PLAT_JUMP
|
|
102
|
+
PLACE: PLAT_PLACE
|
|
103
|
+
BODY_MOVED: PLAT_MOVED
|
|
104
|
+
BODY_JUMPED: PLAT_JUMPED
|
|
105
|
+
BODY_LANDED: PLAT_LANDED
|
|
106
|
+
BODY_HAZARD: PLAT_HAZARD
|
|
107
|
+
BODY_GOAL: PLAT_GOAL
|
|
108
|
+
BODY_FELL: PLAT_FELL
|
|
109
|
+
}
|
|
110
|
+
config {
|
|
111
|
+
running: true
|
|
112
|
+
spawn: { x: 80, y: 320, width: 32, height: 48, vx: 0, vy: 0, grounded: false, facingRight: true }
|
|
113
|
+
platforms: [
|
|
114
|
+
{ id: "g0", x: 0, y: 368, width: 800, height: 32, type: "ground" }
|
|
115
|
+
{ id: "p1", x: 150, y: 280, width: 160, height: 16, type: "platform" }
|
|
116
|
+
{ id: "p2", x: 420, y: 220, width: 160, height: 16, type: "platform" }
|
|
117
|
+
{ id: "h1", x: 580, y: 300, width: 80, height: 16, type: "hazard" }
|
|
118
|
+
{ id: "goal", x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
119
|
+
]
|
|
120
|
+
gravity: 550
|
|
121
|
+
jumpStrength: 365
|
|
122
|
+
moveSpeed: 120
|
|
123
|
+
maxFallSpeed: 600
|
|
124
|
+
xMin: 0
|
|
125
|
+
xMax: 768
|
|
126
|
+
killY: 460
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
trait FxDecay = Particles.traits.FxParticles -> GameState {
|
|
131
|
+
config { running: true }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
trait RoundGate = Gate.traits.ObjectiveFlow -> GameState {
|
|
135
|
+
config { running: true }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
trait PlatformerIntent -> GameState [interaction, instance] {
|
|
139
|
+
initial: playing
|
|
140
|
+
|
|
141
|
+
state playing {
|
|
142
|
+
INIT -> playing
|
|
143
|
+
(set @entity.score 0)
|
|
144
|
+
(set @entity.lives @config.startLives)
|
|
145
|
+
(set @entity.objectivesLeft 1)
|
|
146
|
+
(set @entity.celebrated false)
|
|
147
|
+
(emit RESTART { id: "board" })
|
|
148
|
+
(render-ui main {
|
|
149
|
+
type: game-shell
|
|
150
|
+
backgroundAsset: @config.backgroundAsset
|
|
151
|
+
hudBackgroundAsset: @config.hudBackgroundAsset
|
|
152
|
+
hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: [ { label: "Score", value: @entity.score }, { label: "Lives", value: @entity.lives } ] }, { type: control-grid, kind: "dpad", directionEvents: { left: "LEFT", right: "RIGHT", up: "JUMP" }, directionReleaseEvents: { left: "STOP", right: "STOP" }, directionAssets: @config.directionAssets } ] }
|
|
153
|
+
overlay: (if (== @entity.result "won") { type: game-menu, title: @config.clearTitle, subtitle: (str/concat "Score: " @entity.score), options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } (if (== @entity.result "lost") { type: game-menu, title: @config.lostTitle, subtitle: (str/concat "Score: " @entity.score), options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } null))
|
|
154
|
+
children: [ { type: canvas, camera: { mode: "follow", zoom: 1, pos: { x: (/ @config.worldWidth 2), y: (/ @config.worldHeight 2) }, target: { x: @entity.body.x, y: @entity.body.y } }, projection: "side", worldWidth: @config.worldWidth, worldHeight: @config.worldHeight, backgroundColor: @config.bgColor, backgroundImage: @config.backgroundImage, keyMap: { ArrowLeft: "LEFT", KeyA: "LEFT", ArrowRight: "RIGHT", KeyD: "RIGHT", ArrowUp: "JUMP", KeyW: "JUMP", Space: "JUMP" }, keyUpMap: { ArrowLeft: "STOP", KeyA: "STOP", ArrowRight: "STOP", KeyD: "STOP" }, drawables: [
|
|
155
|
+
{ type: draw-sprite-layer, items: (array/map @config.platforms (fn p { type: draw-sprite, position: { x: (object/get @p x), y: (object/get @p y) }, width: (object/get @p width), height: (object/get @p height), asset: (object/get @config.tileSprites (object/get @p type)) })) }
|
|
156
|
+
{ type: draw-sprite-layer, items: [ (object/merge { type: "draw-sprite", position: { x: @entity.body.x, y: @entity.body.y }, width: @entity.body.width, height: @entity.body.height, frame: @entity.frame, flipX: (not @entity.body.facingRight) } { asset: (object/merge @config.playerSprite { sprite: (if @entity.body.grounded (if (!= @entity.body.vx 0) (if (== (% @entity.frame 2) 0) "adventurer_walk1.png" "adventurer_walk2.png") "adventurer_idle.png") "adventurer_jump.png") }) }) ] }
|
|
157
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets }
|
|
158
|
+
] } ]
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
LEFT -> playing when (== @entity.result "none")
|
|
162
|
+
(emit PLAT_STEER { dir: -1 })
|
|
163
|
+
|
|
164
|
+
RIGHT -> playing when (== @entity.result "none")
|
|
165
|
+
(emit PLAT_STEER { dir: 1 })
|
|
166
|
+
|
|
167
|
+
STOP -> playing when (== @entity.result "none")
|
|
168
|
+
(emit PLAT_STEER { dir: 0 })
|
|
169
|
+
|
|
170
|
+
JUMP -> playing when (== @entity.result "none")
|
|
171
|
+
(emit PLAT_JUMP {})
|
|
172
|
+
|
|
173
|
+
PLAT_GOAL -> playing when (== @entity.result "none")
|
|
174
|
+
(set @entity.score (+ @entity.score @config.goalScore))
|
|
175
|
+
(set @entity.objectivesLeft 0)
|
|
176
|
+
(emit BURST { particles: (array/map (array/filter @config.platforms (fn p (== (object/get @p "id" "") @payload.id))) (fn p { id: (str/concat "fx-goal-" @payload.id), type: "score", x: @payload.x, z: @payload.y, message: (str/concat "+" @config.goalScore), ttl: 3 })) })
|
|
177
|
+
(emit PROGRESS { objectivesLeft: 0, movesLeft: @entity.lives })
|
|
178
|
+
|
|
179
|
+
PLAT_HAZARD -> playing when (== @entity.result "none")
|
|
180
|
+
(if (<= (- @entity.lives 1) 0)
|
|
181
|
+
(do
|
|
182
|
+
(set @entity.lives 0)
|
|
183
|
+
(emit PROGRESS { objectivesLeft: 1, movesLeft: 0 }))
|
|
184
|
+
(do
|
|
185
|
+
(set @entity.lives (- @entity.lives 1))
|
|
186
|
+
(emit BURST { particles: (array/map (array/filter @config.platforms (fn p (== (object/get @p "id" "") @payload.id))) (fn p { id: (str/concat "fx-hazard-" @payload.id), type: "damage", x: @payload.x, z: @payload.y, message: "-1", ttl: 3 })) })
|
|
187
|
+
(emit PLAT_PLACE { x: @config.respawnX, y: @config.respawnY })))
|
|
188
|
+
|
|
189
|
+
PLAT_FELL -> playing when (== @entity.result "none")
|
|
190
|
+
(if (<= (- @entity.lives 1) 0)
|
|
191
|
+
(do
|
|
192
|
+
(set @entity.lives 0)
|
|
193
|
+
(emit PROGRESS { objectivesLeft: 1, movesLeft: 0 }))
|
|
194
|
+
(do
|
|
195
|
+
(set @entity.lives (- @entity.lives 1))
|
|
196
|
+
(emit BURST { particles: (array/map [ "fx-fell" ] (fn fid { id: @fid, type: "damage", x: @payload.x, z: @config.worldHeight, message: "-1", ttl: 3 })) })
|
|
197
|
+
(emit PLAT_PLACE { x: @config.respawnX, y: @config.respawnY })))
|
|
198
|
+
|
|
199
|
+
GAME_END -> playing when (== @entity.celebrated false)
|
|
200
|
+
(set @entity.celebrated true)
|
|
201
|
+
|
|
202
|
+
PLAY_AGAIN -> playing
|
|
203
|
+
(set @entity.score 0)
|
|
204
|
+
(set @entity.lives @config.startLives)
|
|
205
|
+
(set @entity.objectivesLeft 1)
|
|
206
|
+
(set @entity.celebrated false)
|
|
207
|
+
(emit RESTART { id: "board" })
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
ticks {
|
|
211
|
+
renderTick every 100ms
|
|
212
|
+
(set @entity.frame (% (+ @entity.frame 1) 8))
|
|
213
|
+
(render-ui main {
|
|
214
|
+
type: game-shell
|
|
215
|
+
backgroundAsset: @config.backgroundAsset
|
|
216
|
+
hudBackgroundAsset: @config.hudBackgroundAsset
|
|
217
|
+
hud: { type: stack, direction: horizontal, gap: md, justify: between, children: [ { type: game-hud, stats: [ { label: "Score", value: @entity.score }, { label: "Lives", value: @entity.lives } ] }, { type: control-grid, kind: "dpad", directionEvents: { left: "LEFT", right: "RIGHT", up: "JUMP" }, directionReleaseEvents: { left: "STOP", right: "STOP" }, directionAssets: @config.directionAssets } ] }
|
|
218
|
+
overlay: (if (== @entity.result "won") { type: game-menu, title: @config.clearTitle, subtitle: (str/concat "Score: " @entity.score), options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } (if (== @entity.result "lost") { type: game-menu, title: @config.lostTitle, subtitle: (str/concat "Score: " @entity.score), options: [ { id: "again", label: "Play Again", event: "PLAY_AGAIN", variant: "primary" } ] } null))
|
|
219
|
+
children: [ { type: canvas, camera: { mode: "follow", zoom: 1, pos: { x: (/ @config.worldWidth 2), y: (/ @config.worldHeight 2) }, target: { x: @entity.body.x, y: @entity.body.y } }, projection: "side", worldWidth: @config.worldWidth, worldHeight: @config.worldHeight, backgroundColor: @config.bgColor, backgroundImage: @config.backgroundImage, keyMap: { ArrowLeft: "LEFT", KeyA: "LEFT", ArrowRight: "RIGHT", KeyD: "RIGHT", ArrowUp: "JUMP", KeyW: "JUMP", Space: "JUMP" }, keyUpMap: { ArrowLeft: "STOP", KeyA: "STOP", ArrowRight: "STOP", KeyD: "STOP" }, drawables: [
|
|
220
|
+
{ type: draw-sprite-layer, items: (array/map @config.platforms (fn p { type: draw-sprite, position: { x: (object/get @p x), y: (object/get @p y) }, width: (object/get @p width), height: (object/get @p height), asset: (object/get @config.tileSprites (object/get @p type)) })) }
|
|
221
|
+
{ type: draw-sprite-layer, items: [ (object/merge { type: "draw-sprite", position: { x: @entity.body.x, y: @entity.body.y }, width: @entity.body.width, height: @entity.body.height, frame: @entity.frame, flipX: (not @entity.body.facingRight) } { asset: (object/merge @config.playerSprite { sprite: (if @entity.body.grounded (if (!= @entity.body.vx 0) (if (== (% @entity.frame 2) 0) "adventurer_walk1.png" "adventurer_walk2.png") "adventurer_idle.png") "adventurer_jump.png") }) }) ] }
|
|
222
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets }
|
|
223
|
+
] } ]
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
emits {
|
|
228
|
+
LEFT -> external {
|
|
229
|
+
id : string
|
|
230
|
+
}
|
|
231
|
+
@description "Move-left intent from ArrowLeft/KeyA keydown or d-pad (forwarded to the body authority as STEER dir −1)"
|
|
232
|
+
@tier "domain"
|
|
233
|
+
RIGHT -> external {
|
|
234
|
+
id : string
|
|
235
|
+
}
|
|
236
|
+
@description "Move-right intent from ArrowRight/KeyD keydown or d-pad (forwarded to the body authority as STEER dir +1)"
|
|
237
|
+
@tier "domain"
|
|
238
|
+
STOP -> external {
|
|
239
|
+
id : string
|
|
240
|
+
}
|
|
241
|
+
@description "Horizontal key release (keyUpMap or d-pad directionReleaseEvents); forwarded as STEER dir 0 so the body stops"
|
|
242
|
+
@tier "domain"
|
|
243
|
+
JUMP -> external {
|
|
244
|
+
id : string
|
|
245
|
+
}
|
|
246
|
+
@description "Jump intent from Space/ArrowUp/KeyW or d-pad up; forwarded to the body authority, which accepts it only while grounded"
|
|
247
|
+
@tier "domain"
|
|
248
|
+
PLAT_STEER -> external {
|
|
249
|
+
dir : number!
|
|
250
|
+
}
|
|
251
|
+
@description "Renamed STEER consumed by the platformer-body authority: −1/0/+1 × moveSpeed horizontal velocity"
|
|
252
|
+
@tier "domain"
|
|
253
|
+
PLAT_JUMP -> external {
|
|
254
|
+
id : string
|
|
255
|
+
}
|
|
256
|
+
@description "Renamed JUMP consumed by the platformer-body authority (grounded-gated vertical kick)"
|
|
257
|
+
@tier "domain"
|
|
258
|
+
PLAT_PLACE -> external {
|
|
259
|
+
x : number!
|
|
260
|
+
y : number!
|
|
261
|
+
}
|
|
262
|
+
@description "Renamed PLACE consumed by the platformer-body authority; hazard/pit respawn teleport"
|
|
263
|
+
@tier "domain"
|
|
264
|
+
PLAT_GOAL -> external {
|
|
265
|
+
id : string!
|
|
266
|
+
x : number!
|
|
267
|
+
y : number!
|
|
268
|
+
}
|
|
269
|
+
@description "Goal-tile contact signal from the platformer-body authority (renamed BODY_GOAL); scores and trips the win gate"
|
|
270
|
+
@tier "domain"
|
|
271
|
+
PLAT_HAZARD -> external {
|
|
272
|
+
id : string!
|
|
273
|
+
x : number!
|
|
274
|
+
y : number!
|
|
275
|
+
}
|
|
276
|
+
@description "Hazard-tile contact signal from the platformer-body authority (renamed BODY_HAZARD); costs a life and respawns"
|
|
277
|
+
@tier "domain"
|
|
278
|
+
PLAT_FELL -> external {
|
|
279
|
+
x : number!
|
|
280
|
+
y : number!
|
|
281
|
+
}
|
|
282
|
+
@description "Pit-fall signal from the platformer-body authority (renamed BODY_FELL); costs a life and respawns"
|
|
283
|
+
@tier "domain"
|
|
284
|
+
BURST -> external {
|
|
285
|
+
particles : [Fx]
|
|
286
|
+
}
|
|
287
|
+
@description "Floating-text particle batch spawned on score/damage; fx-particles fades it"
|
|
288
|
+
@tier "domain"
|
|
289
|
+
PROGRESS -> external {
|
|
290
|
+
objectivesLeft : number!
|
|
291
|
+
movesLeft : number!
|
|
292
|
+
}
|
|
293
|
+
@description "Sparse win/lose signal consumed by the objective-flow gate: objectivesLeft 0 = won, movesLeft 0 = lost"
|
|
294
|
+
@tier "domain"
|
|
295
|
+
GAME_END -> external {
|
|
296
|
+
result : string!
|
|
297
|
+
}
|
|
298
|
+
@description "Win/lose resolution from the objective-flow gate; guarded by celebrated so it lands once"
|
|
299
|
+
@tier "domain"
|
|
300
|
+
RESTART -> external {
|
|
301
|
+
id : string
|
|
302
|
+
}
|
|
303
|
+
@description "Broadcast reset consumed by the platformer-body and objective-flow authorities on INIT/PLAY_AGAIN"
|
|
304
|
+
@tier "domain"
|
|
305
|
+
PLAY_AGAIN -> external {
|
|
306
|
+
id : string
|
|
307
|
+
}
|
|
308
|
+
@description "End-overlay restart button; reseeds score/lives/objectives and broadcasts RESTART"
|
|
309
|
+
@tier "domain"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
config {
|
|
313
|
+
platforms : [PlatformerPlatform] = [
|
|
314
|
+
{ id: "g0", x: 0, y: 368, width: 800, height: 32, type: "ground" }
|
|
315
|
+
{ id: "p1", x: 150, y: 280, width: 160, height: 16, type: "platform" }
|
|
316
|
+
{ id: "p2", x: 420, y: 220, width: 160, height: 16, type: "platform" }
|
|
317
|
+
{ id: "h1", x: 580, y: 300, width: 80, height: 16, type: "hazard" }
|
|
318
|
+
{ id: "goal", x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
319
|
+
]
|
|
320
|
+
@label "Platforms"
|
|
321
|
+
@description "Level geometry rendered by the canvas (must match BodyAuthority.platforms, which owns collision — override both together)"
|
|
322
|
+
@tier "domain"
|
|
323
|
+
respawnX : number = 80
|
|
324
|
+
@label "Respawn X"
|
|
325
|
+
@description "X the body teleports to after hazard/pit damage (matches BodyAuthority spawn.x)"
|
|
326
|
+
@tier "domain"
|
|
327
|
+
respawnY : number = 320
|
|
328
|
+
@label "Respawn Y"
|
|
329
|
+
@description "Y the body teleports to after hazard/pit damage (matches BodyAuthority spawn.y)"
|
|
330
|
+
@tier "domain"
|
|
331
|
+
goalScore : number = 100
|
|
332
|
+
@label "Goal Score"
|
|
333
|
+
@description "Points awarded when the body touches the goal tile"
|
|
334
|
+
@tier "domain"
|
|
335
|
+
startLives : number = 3
|
|
336
|
+
@label "Start Lives"
|
|
337
|
+
@description "Lives seeded on INIT/PLAY_AGAIN; hazard or pit contact costs one, zero loses the round"
|
|
338
|
+
@tier "domain"
|
|
339
|
+
worldWidth : number = 800
|
|
340
|
+
@label "World Width"
|
|
341
|
+
@description "World pixel width (camera + canvas bounds)"
|
|
342
|
+
@tier "presentation"
|
|
343
|
+
fxPresets : [FxPreset] = [ { type: "score", color: "#ffe066", color2: "#fff4cc", shape: "spark", size: 22, count: 6, glow: 0.15, vy: -35 }, { type: "damage", color: "#ef4444", color2: "#7f1d1d", shape: "spark", size: 20, count: 5, glow: 0.12, gravity: 40 } ]
|
|
344
|
+
@label "Fx Presets"
|
|
345
|
+
@description "This board's effect vocabulary — one look/recipe row per fx type its intent trait emits (goal score, hazard/fell damage)."
|
|
346
|
+
@tier "presentation"
|
|
347
|
+
worldHeight : number = 400
|
|
348
|
+
@label "World Height"
|
|
349
|
+
@description "World pixel height (camera + canvas bounds)"
|
|
350
|
+
@tier "presentation"
|
|
351
|
+
bgColor : string = "#5c94fc"
|
|
352
|
+
@label "Bg Color"
|
|
353
|
+
@description "Canvas background color"
|
|
354
|
+
@tier "presentation"
|
|
355
|
+
clearTitle : string = "YOU WIN!"
|
|
356
|
+
@label "Clear Title"
|
|
357
|
+
@description "Overlay title when the goal tile is reached"
|
|
358
|
+
@tier "presentation"
|
|
359
|
+
lostTitle : string = "GAME OVER"
|
|
360
|
+
@label "Lost Title"
|
|
361
|
+
@description "Overlay title when lives run out"
|
|
362
|
+
@tier "presentation"
|
|
363
|
+
playerSprite : Asset = {}
|
|
364
|
+
@label "Player Sprite"
|
|
365
|
+
@description "Player sprite Asset; its atlas must expose adventurer_idle/walk1/walk2/jump frames (or override the sprite-selection expression)"
|
|
366
|
+
@tier "presentation"
|
|
367
|
+
tileSprites : Map string Asset = {}
|
|
368
|
+
@label "Tile Sprites"
|
|
369
|
+
@description "Map of platform type (ground/platform/hazard/goal) to tile sprite Asset"
|
|
370
|
+
@tier "presentation"
|
|
371
|
+
backgroundImage : Asset = {}
|
|
372
|
+
@label "Background Image"
|
|
373
|
+
@description "Canvas background image Asset"
|
|
374
|
+
@tier "presentation"
|
|
375
|
+
backgroundAsset : Asset = {}
|
|
376
|
+
@label "Background Asset"
|
|
377
|
+
@description "Game-shell background panel sprite (theme chrome)"
|
|
378
|
+
@tier "presentation"
|
|
379
|
+
hudBackgroundAsset : Asset = {}
|
|
380
|
+
@label "HUD Background Asset"
|
|
381
|
+
@description "HUD panel background sprite (theme chrome)"
|
|
382
|
+
@tier "presentation"
|
|
383
|
+
directionAssets : DirectionAssets = {}
|
|
384
|
+
@label "Direction Assets"
|
|
385
|
+
@description "D-pad direction icon sprites"
|
|
386
|
+
@tier "presentation"
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
page "/platformer-board-2d" as PlatformerBoard2DPage -> BodyAuthority, FxDecay, RoundGate, PlatformerIntent
|
|
391
|
+
}
|
|
@@ -50,6 +50,32 @@ orbital SurvivalBoard3DOrbital {
|
|
|
50
50
|
y : number
|
|
51
51
|
message : string
|
|
52
52
|
ttl : number!
|
|
53
|
+
maxTtl : number
|
|
54
|
+
bornAt : number
|
|
55
|
+
space : string
|
|
56
|
+
effect : string
|
|
57
|
+
color : string
|
|
58
|
+
size : number
|
|
59
|
+
vx : number
|
|
60
|
+
vy : number
|
|
61
|
+
vz : number
|
|
62
|
+
particleCount : number
|
|
63
|
+
}
|
|
64
|
+
type FxPreset = {
|
|
65
|
+
type : string!
|
|
66
|
+
color : string
|
|
67
|
+
color2 : string
|
|
68
|
+
size : number
|
|
69
|
+
shape : string
|
|
70
|
+
count : number
|
|
71
|
+
glow : number
|
|
72
|
+
gravity : number
|
|
73
|
+
vx : number
|
|
74
|
+
vy : number
|
|
75
|
+
vz : number
|
|
76
|
+
space : string
|
|
77
|
+
effect : string
|
|
78
|
+
particleCount : number
|
|
53
79
|
}
|
|
54
80
|
type ValidMove = { x : number! z : number! }
|
|
55
81
|
type Feature = {
|
|
@@ -149,7 +175,7 @@ orbital SurvivalBoard3DOrbital {
|
|
|
149
175
|
{ 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)" })) }
|
|
150
176
|
{ 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 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
|
|
151
177
|
{ 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" })) }
|
|
152
|
-
{ type: draw-
|
|
178
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets }
|
|
153
179
|
] } ]
|
|
154
180
|
})
|
|
155
181
|
|
|
@@ -195,7 +221,7 @@ orbital SurvivalBoard3DOrbital {
|
|
|
195
221
|
{ 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)" })) }
|
|
196
222
|
{ 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 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
|
|
197
223
|
{ 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" })) }
|
|
198
|
-
{ type: draw-
|
|
224
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets }
|
|
199
225
|
] } ]
|
|
200
226
|
})
|
|
201
227
|
|
|
@@ -288,6 +314,10 @@ orbital SurvivalBoard3DOrbital {
|
|
|
288
314
|
@label "Bg Color"
|
|
289
315
|
@description "Canvas background color."
|
|
290
316
|
@tier "presentation"
|
|
317
|
+
fxPresets : [FxPreset] = [ { type: "pickup", color: "#84a98c", color2: "#6b4226", size: 0.45, shape: "spark", count: 5, vy: -0.2 } ]
|
|
318
|
+
@label "Fx Presets"
|
|
319
|
+
@description "This board's effect vocabulary — one look/recipe row per fx type its mechanics emit; unknown types fall back to a neutral spark."
|
|
320
|
+
@tier "presentation"
|
|
291
321
|
assetManifest : AssetManifest = { terrains: {}, units: {}, features: {}, effects: {} }
|
|
292
322
|
@label "Asset Manifest"
|
|
293
323
|
@description "Per-role model manifest resolving tile/unit/feature models by terrain/category key."
|
|
@@ -51,6 +51,32 @@ orbital TacticsBoard2DOrbital {
|
|
|
51
51
|
y : number
|
|
52
52
|
message : string
|
|
53
53
|
ttl : number!
|
|
54
|
+
maxTtl : number
|
|
55
|
+
bornAt : number
|
|
56
|
+
space : string
|
|
57
|
+
effect : string
|
|
58
|
+
color : string
|
|
59
|
+
size : number
|
|
60
|
+
vx : number
|
|
61
|
+
vy : number
|
|
62
|
+
vz : number
|
|
63
|
+
particleCount : number
|
|
64
|
+
}
|
|
65
|
+
type FxPreset = {
|
|
66
|
+
type : string!
|
|
67
|
+
color : string
|
|
68
|
+
color2 : string
|
|
69
|
+
size : number
|
|
70
|
+
shape : string
|
|
71
|
+
count : number
|
|
72
|
+
glow : number
|
|
73
|
+
gravity : number
|
|
74
|
+
vx : number
|
|
75
|
+
vy : number
|
|
76
|
+
vz : number
|
|
77
|
+
space : string
|
|
78
|
+
effect : string
|
|
79
|
+
particleCount : number
|
|
54
80
|
}
|
|
55
81
|
type ValidMove = { x : number! z : number! }
|
|
56
82
|
type Feature = {
|
|
@@ -188,10 +214,9 @@ orbital TacticsBoard2DOrbital {
|
|
|
188
214
|
{ 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)" })) }
|
|
189
215
|
{ 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" })) }
|
|
190
216
|
{ 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" })) }
|
|
191
|
-
{ type: draw-
|
|
192
|
-
{ type: draw-sprite-layer, items: (array/filter (array/map (array/filter @entity.fx (fn f (not (object/get @entity.svgAssetArt (object/get @f type))))) (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))) }
|
|
217
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets, art: @entity.svgAssetArt, sprites: (object/get @config.assetManifest effects) }
|
|
193
218
|
] (array/concat
|
|
194
|
-
|
|
219
|
+
[]
|
|
195
220
|
[
|
|
196
221
|
{ 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 })) }
|
|
197
222
|
])))))))) } ]
|
|
@@ -285,9 +310,8 @@ orbital TacticsBoard2DOrbital {
|
|
|
285
310
|
{ 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)" })) }
|
|
286
311
|
{ 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" })) }
|
|
287
312
|
{ 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" })) }
|
|
288
|
-
{ type: draw-
|
|
289
|
-
|
|
290
|
-
] (array/map (array/filter @entity.fx (fn f (object/get @entity.svgAssetArt (object/get @f type)))) (fn f { type: draw-group, position: { x: (object/get @f x), y: (object/get @f z) }, items: (object/get (object/get @entity.svgAssetArt (object/get @f type)) "idle") }))))))))) } ]
|
|
313
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets, art: @entity.svgAssetArt, sprites: (object/get @config.assetManifest effects) }
|
|
314
|
+
] []))))))) } ]
|
|
291
315
|
})
|
|
292
316
|
}
|
|
293
317
|
|
|
@@ -357,6 +381,10 @@ orbital TacticsBoard2DOrbital {
|
|
|
357
381
|
@label "Asset Manifest"
|
|
358
382
|
@description "Terrain/unit/feature/effect sprite manifest."
|
|
359
383
|
@tier "presentation"
|
|
384
|
+
fxPresets : [FxPreset] = [ { type: "hit", color: "#ff6b6b", color2: "#ffb347", shape: "spark", size: 0.45, count: 6, glow: 0.12, vy: -0.3 } ]
|
|
385
|
+
@label "Fx Presets"
|
|
386
|
+
@description "This board's effect vocabulary — one look/recipe row per fx type its mechanics emit; unknown types fall back to a neutral spark."
|
|
387
|
+
@tier "presentation"
|
|
360
388
|
projection : string = "flat"
|
|
361
389
|
@label "Projection"
|
|
362
390
|
@description "canvas-2d projection: flat or isometric."
|
|
@@ -80,6 +80,32 @@ orbital TacticsBoard3DOrbital {
|
|
|
80
80
|
y : number
|
|
81
81
|
message : string
|
|
82
82
|
ttl : number!
|
|
83
|
+
maxTtl : number
|
|
84
|
+
bornAt : number
|
|
85
|
+
space : string
|
|
86
|
+
effect : string
|
|
87
|
+
color : string
|
|
88
|
+
size : number
|
|
89
|
+
vx : number
|
|
90
|
+
vy : number
|
|
91
|
+
vz : number
|
|
92
|
+
particleCount : number
|
|
93
|
+
}
|
|
94
|
+
type FxPreset = {
|
|
95
|
+
type : string!
|
|
96
|
+
color : string
|
|
97
|
+
color2 : string
|
|
98
|
+
size : number
|
|
99
|
+
shape : string
|
|
100
|
+
count : number
|
|
101
|
+
glow : number
|
|
102
|
+
gravity : number
|
|
103
|
+
vx : number
|
|
104
|
+
vy : number
|
|
105
|
+
vz : number
|
|
106
|
+
space : string
|
|
107
|
+
effect : string
|
|
108
|
+
particleCount : number
|
|
83
109
|
}
|
|
84
110
|
type ValidMove = { x : number! z : number! }
|
|
85
111
|
type Feature = {
|
|
@@ -184,9 +210,9 @@ orbital TacticsBoard3DOrbital {
|
|
|
184
210
|
{ type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: { x: (object/get @u x), y: (object/get @u y), z: (object/get @u z) }, offsetY: -0.6, width: (* 0.8 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
|
|
185
211
|
{ type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: { x: (object/get @u x), y: (object/get @u y), z: (object/get @u z) }, offsetY: -0.85, color: "#ffffff" })) }
|
|
186
212
|
] (array/concat
|
|
187
|
-
|
|
213
|
+
[]
|
|
188
214
|
[
|
|
189
|
-
{ type: draw-
|
|
215
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets, art: @entity.meshAssetArt }
|
|
190
216
|
])))))))) } ]
|
|
191
217
|
})
|
|
192
218
|
|
|
@@ -252,9 +278,9 @@ orbital TacticsBoard3DOrbital {
|
|
|
252
278
|
{ type: draw-shape-layer, items: (array/map @entity.units (fn u { type: draw-shape, shape: "rect", position: { x: (object/get @u x), y: (object/get @u y), z: (object/get @u z) }, offsetY: -0.6, width: (* 0.8 (/ (object/get @u health) (object/get @u maxHealth))), height: 0.12, fill: "#22c55e" })) }
|
|
253
279
|
{ type: draw-text-layer, items: (array/map @entity.units (fn u { type: draw-text, text: (object/get @u name), position: { x: (object/get @u x), y: (object/get @u y), z: (object/get @u z) }, offsetY: -0.85, color: "#ffffff" })) }
|
|
254
280
|
] (array/concat
|
|
255
|
-
|
|
281
|
+
[]
|
|
256
282
|
[
|
|
257
|
-
{ type: draw-
|
|
283
|
+
{ type: draw-fx-layer, items: @entity.fx, tickMs: 500, presets: @config.fxPresets, art: @entity.meshAssetArt }
|
|
258
284
|
])))))))) } ]
|
|
259
285
|
})
|
|
260
286
|
}
|
|
@@ -315,6 +341,10 @@ orbital TacticsBoard3DOrbital {
|
|
|
315
341
|
@label "Features"
|
|
316
342
|
@description "Decorative environment features drawn on the board."
|
|
317
343
|
@tier "presentation"
|
|
344
|
+
fxPresets : [FxPreset] = [ { type: "hit", color: "#ff5252", color2: "#7a1010", size: 0.5, shape: "spark", count: 6, glow: 0.12, gravity: 0.6, vy: -0.2 } ]
|
|
345
|
+
@label "Fx Presets"
|
|
346
|
+
@description "This board's effect vocabulary — one look/recipe row per fx type its mechanics emit; unknown types fall back to a neutral spark."
|
|
347
|
+
@tier "presentation"
|
|
318
348
|
assetManifest : AssetManifest = { terrains: {}, units: {}, features: {}, effects: {} }
|
|
319
349
|
@label "Asset Manifest"
|
|
320
350
|
@description "Terrain/unit/feature/effect model manifest."
|
|
@@ -301,6 +301,7 @@ orbital LearnArrangementOrbital {
|
|
|
301
301
|
]
|
|
302
302
|
})
|
|
303
303
|
(emit ARRANGEMENT_CHECKED { correctCount: @entity.correctCount, total: @entity.totalItems })
|
|
304
|
+
(if (== @entity.correctCount @entity.totalItems) (emit LEARN_ROUND_COMPLETE { score: @entity.correctCount, total: @entity.totalItems }) null)
|
|
304
305
|
|
|
305
306
|
RETRY -> idle when (== @entity.active true)
|
|
306
307
|
(set @entity.placedIds [])
|
|
@@ -388,6 +389,13 @@ orbital LearnArrangementOrbital {
|
|
|
388
389
|
@description "Signals the arrangement was cleared back to empty by Retry — a composed listener can subscribe; a harmless no-op when the atom runs standalone."
|
|
389
390
|
@synonyms "arrangement reset, retry, restart"
|
|
390
391
|
@tier "domain"
|
|
392
|
+
LEARN_ROUND_COMPLETE -> external {
|
|
393
|
+
score : number
|
|
394
|
+
total : number
|
|
395
|
+
}
|
|
396
|
+
@description "Fired when a Check grades the arrangement fully correct — the standard learning completion broadcast a celebration/progression sibling (fx cues, badges) rides."
|
|
397
|
+
@synonyms "round complete, finished, done, completion, all correct"
|
|
398
|
+
@tier "domain"
|
|
391
399
|
}
|
|
392
400
|
|
|
393
401
|
config {
|
|
@@ -382,6 +382,7 @@ orbital LearnClassifyOrbital {
|
|
|
382
382
|
(emit CLASSIFY_UPDATED { itemIndex: @entity.itemIndex, correctCount: @entity.correctCount, attemptedCount: @entity.attemptedCount, selectedCategory: @entity.selectedCategory, verdict: @entity.verdict, roundComplete: @entity.roundComplete })
|
|
383
383
|
|
|
384
384
|
ADVANCE -> idle when (and (== @entity.active true) (== @entity.roundComplete false))
|
|
385
|
+
(if (and (not @config.loopRound) (>= (+ @entity.itemIndex @config.orderStride) @entity.totalItems)) (emit LEARN_ROUND_COMPLETE { score: @entity.correctCount, total: @entity.totalItems }) null)
|
|
385
386
|
(let (
|
|
386
387
|
(isLast (and (not @config.loopRound) (>= (+ @entity.itemIndex @config.orderStride) @entity.totalItems)))
|
|
387
388
|
)
|
|
@@ -650,6 +651,13 @@ orbital LearnClassifyOrbital {
|
|
|
650
651
|
@description "The classification state after each learner action — a composed listener (readout, grader) can subscribe; a harmless no-op when the atom runs standalone."
|
|
651
652
|
@synonyms "classify update, pick result, score, running score, verdict"
|
|
652
653
|
@tier "domain"
|
|
654
|
+
LEARN_ROUND_COMPLETE -> external {
|
|
655
|
+
score : number
|
|
656
|
+
total : number
|
|
657
|
+
}
|
|
658
|
+
@description "Fired once when the round finishes (the advance past the final item) — the standard learning completion broadcast a celebration/progression sibling (fx cues, badges) rides."
|
|
659
|
+
@synonyms "round complete, finished, done, completion"
|
|
660
|
+
@tier "domain"
|
|
653
661
|
}
|
|
654
662
|
|
|
655
663
|
config {
|