@jgengine/shell 0.11.0 → 0.12.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/CHANGELOG.md +26 -0
- package/dist/CombatPresentation.d.ts +5 -0
- package/dist/CombatPresentation.js +8 -0
- package/dist/GameHost.d.ts +25 -1
- package/dist/GameHost.js +60 -4
- package/dist/GamePlayer.d.ts +5 -0
- package/dist/GamePlayer.js +5 -0
- package/dist/GamePlayerShell.d.ts +5 -0
- package/dist/GamePlayerShell.js +48 -358
- package/dist/GameUiPreview.js +3 -0
- package/dist/Shell3dPresentation.d.ts +63 -0
- package/dist/Shell3dPresentation.js +302 -0
- package/dist/ShellChrome.d.ts +51 -0
- package/dist/ShellChrome.js +51 -0
- package/dist/ShellHudPresentation.d.ts +41 -0
- package/dist/ShellHudPresentation.js +22 -0
- package/dist/audio/audioEngine.d.ts +4 -0
- package/dist/audio/audioEngine.js +111 -26
- package/dist/audio/audioWire.d.ts +22 -4
- package/dist/audio/audioWire.js +46 -3
- package/dist/audio/loopParams.d.ts +20 -0
- package/dist/audio/loopParams.js +28 -0
- package/dist/camera/GameCameraRig.js +6 -1
- package/dist/camera/cameraRigs.d.ts +3 -1
- package/dist/camera/cameraRigs.js +21 -3
- package/dist/camera/rigMath.d.ts +20 -0
- package/dist/camera/rigMath.js +29 -0
- package/dist/defineGame.d.ts +15 -1
- package/dist/defineGame.js +26 -4
- package/dist/devtools/DevtoolsOverlay.d.ts +4 -4
- package/dist/devtools/DevtoolsOverlay.js +28 -12
- package/dist/devtools/agentBridge.js +4 -0
- package/dist/devtools/perfDiagnose.js +1 -1
- package/dist/diagnostics/RuntimeDiagnostics.d.ts +7 -0
- package/dist/diagnostics/RuntimeDiagnostics.js +23 -12
- package/dist/environment/EnvironmentScene.js +12 -3
- package/dist/environment/terrainGroundColor.d.ts +9 -0
- package/dist/environment/terrainGroundColor.js +22 -0
- package/dist/gameKit.d.ts +27 -0
- package/dist/gameKit.js +29 -0
- package/dist/map/MapMarkerBeacons.d.ts +3 -2
- package/dist/postfx/PostProcessing.d.ts +9 -1
- package/dist/postfx/PostProcessing.js +11 -4
- package/dist/presentationResolve.d.ts +35 -0
- package/dist/presentationResolve.js +51 -0
- package/dist/registry.d.ts +1 -0
- package/dist/render/assetBase.d.ts +17 -0
- package/dist/render/assetBase.js +35 -0
- package/dist/render/modelLoad.js +4 -1
- package/dist/render/resolveModel.d.ts +15 -0
- package/dist/render/resolveModel.js +37 -2
- package/dist/scatter/InstancedScatter.js +5 -0
- package/dist/scatter/scatterProxies.js +24 -0
- package/dist/scene/AuthoredScene.d.ts +3 -1
- package/dist/scene/AuthoredScene.js +7 -2
- package/dist/scene/CityRenderer.d.ts +2 -0
- package/dist/scene/CityRenderer.js +1031 -0
- package/dist/scene/GrassFieldRenderer.js +53 -5
- package/dist/scene/PoleLineRenderer.d.ts +2 -0
- package/dist/scene/PoleLineRenderer.js +287 -0
- package/dist/scene/SoilPatchRenderer.js +70 -7
- package/dist/scene/WaterRenderer.js +34 -10
- package/dist/scene/builtinSceneKindRenderers.js +4 -0
- package/dist/scene/cityWindowMaterial.d.ts +16 -0
- package/dist/scene/cityWindowMaterial.js +81 -0
- package/dist/scene/sceneKindRenderers.d.ts +2 -0
- package/dist/settings/appliedSettings.d.ts +2 -1
- package/dist/settings/appliedSettings.js +4 -2
- package/dist/structures/TransformGizmo.d.ts +11 -1
- package/dist/structures/TransformGizmo.js +9 -5
- package/dist/terrain/GrassField.d.ts +11 -3
- package/dist/terrain/GrassField.js +16 -6
- package/dist/terrain/grassGeometry.d.ts +33 -1
- package/dist/terrain/grassGeometry.js +83 -21
- package/dist/terrain/grassMaterial.d.ts +18 -0
- package/dist/terrain/grassMaterial.js +66 -13
- package/dist/terrain/heightfieldGeometry.d.ts +34 -0
- package/dist/terrain/heightfieldGeometry.js +109 -0
- package/dist/terrain/index.d.ts +1 -0
- package/dist/terrain/index.js +1 -0
- package/dist/terrain/soilPatchMaterial.d.ts +15 -2
- package/dist/terrain/soilPatchMaterial.js +46 -8
- package/dist/terrain/terrainDetailMaterial.js +15 -2
- package/dist/water/Ocean.d.ts +7 -1
- package/dist/water/Ocean.js +16 -3
- package/dist/water/OceanConfig.d.ts +9 -0
- package/dist/water/OceanConfig.js +6 -0
- package/dist/water/OceanMaterial.d.ts +12 -0
- package/dist/water/OceanMaterial.js +9 -0
- package/dist/water/OceanShader.d.ts +2 -2
- package/dist/water/OceanShader.js +78 -10
- package/dist/world/PlaceScene.d.ts +11 -0
- package/dist/world/PlaceScene.js +27 -0
- package/dist/world/WorldScene.js +29 -2
- package/dist/worldSky.d.ts +7 -1
- package/dist/worldSky.js +11 -1
- package/package.json +4 -4
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +7 -4
- package/skills/jgengine/api.md +172 -79
- package/skills/jgengine/capabilities.md +104 -2
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-ui/SKILL.md +15 -10
- package/skills/jgengine-ui/api.md +202 -47
- package/skills/jgengine-ui/capabilities.md +71 -2
- package/skills/jgengine-ui/recipes/portable-minimap.md +150 -0
- package/skills/jgengine-ui/reference.md +20 -7
- package/skills/jgengine-ui/references/visual-scorecard.md +1 -1
- package/skills/jgengine-world/SKILL.md +13 -0
- package/skills/jgengine-world/api.md +419 -15
- package/skills/jgengine-world/capabilities.md +108 -2
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +43 -4
- package/skills/level-design/SKILL.md +54 -0
- package/skills/level-design/agents/openai.yaml +4 -0
- package/skills/level-design/references/application-playbook.md +192 -0
- package/skills/level-design/references/field-guide.md +337 -0
- package/skills/level-design/references/live-editor-loop.md +179 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/cartridge.d.ts +0 -145
- package/dist/cartridge.js +0 -246
- package/skills/jgengine/reference-cartridge.md +0 -72
|
@@ -63,16 +63,30 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
63
63
|
|
|
64
64
|
- `defineAttackMoveOrder` (function) · `import { defineAttackMoveOrder } from "@jgengine/core/world"`
|
|
65
65
|
|
|
66
|
-
## authored-
|
|
66
|
+
## authored-entities — spawn authored mob/boss markers from the editor document at runtime
|
|
67
67
|
|
|
68
|
+
- `authoredEntitySpawns` (function) · `import { authoredEntitySpawns } from "@jgengine/core/world/authoredEntities"`
|
|
69
|
+
|
|
70
|
+
## authored-objects — marker kinds resolved as entity spawns, not static props
|
|
71
|
+
|
|
72
|
+
- `ENTITY_MARKER_KINDS` (const) · `import { ENTITY_MARKER_KINDS } from "@jgengine/core/world/authoredObjects"`
|
|
68
73
|
- `markerCatalogId` (function) · `import { markerCatalogId } from "@jgengine/core/world"`
|
|
69
74
|
- `placeAuthoredObjects` (function) · `import { placeAuthoredObjects } from "@jgengine/core/world"`
|
|
70
75
|
- `resolveAuthoredObjects` (function) · `import { resolveAuthoredObjects } from "@jgengine/core/world"`
|
|
71
76
|
|
|
72
|
-
## authored-
|
|
77
|
+
## authored-spawn — read spawn points and markers from the editor document
|
|
78
|
+
|
|
79
|
+
- `authoredSpawnPosition` (function) · `import { authoredSpawnPosition } from "@jgengine/core/world/authoredSpawn"`
|
|
80
|
+
- `authoredSpawnRotation` (function) · `import { authoredSpawnRotation } from "@jgengine/core/world/authoredSpawn"`
|
|
81
|
+
- `markersOfKind` (function) · `import { markersOfKind } from "@jgengine/core/world/authoredSpawn"`
|
|
73
82
|
|
|
83
|
+
## authored-triggers — built-in announce/win/advance actions authorable on any marker or volume
|
|
84
|
+
|
|
85
|
+
- `BUILTIN_TRIGGER_ACTIONS` (const) · `import { BUILTIN_TRIGGER_ACTIONS } from "@jgengine/core/scene/authoredTriggers"`
|
|
74
86
|
- `collectAuthoredTriggers` (function) · `import { collectAuthoredTriggers } from "@jgengine/core/world"`
|
|
75
87
|
- `createAuthoredTriggerRuntime` (function) · `import { createAuthoredTriggerRuntime } from "@jgengine/core/world"`
|
|
88
|
+
- `createTriggerOutcome` (function) · `import { createTriggerOutcome } from "@jgengine/core/scene/authoredTriggers"`
|
|
89
|
+
- `registerBuiltinTriggerActions` (function) · `import { registerBuiltinTriggerActions } from "@jgengine/core/scene/authoredTriggers"`
|
|
76
90
|
- `registerTriggerAction` (function) · `import { registerTriggerAction } from "@jgengine/core/world"`
|
|
77
91
|
|
|
78
92
|
## behavior-control — pause/disable/resume/seek/inspect behavior instances per entity
|
|
@@ -87,6 +101,27 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
87
101
|
|
|
88
102
|
- `createBodyBind` (function) · `import { createBodyBind } from "@jgengine/core/world"`
|
|
89
103
|
|
|
104
|
+
## build-footprint-cells — grid cells a snapped build piece footprint covers (footprintGrid occupancy bridge)
|
|
105
|
+
|
|
106
|
+
- `footprintCells` (function) · `import { footprintCells } from "@jgengine/core/world/buildSockets"`
|
|
107
|
+
|
|
108
|
+
## build-socket-catalog — data-driven named-socket snap catalog for modular base building
|
|
109
|
+
|
|
110
|
+
- `createBuildSocketCatalog` (function) · `import { createBuildSocketCatalog } from "@jgengine/core/world/buildSockets"`
|
|
111
|
+
|
|
112
|
+
## city-district — polygon math for road-derived blocks, parcels, and buildable footprints
|
|
113
|
+
|
|
114
|
+
- `Vec2` (type) · `import { Vec2 } from "@jgengine/core/world/cityGeometry"`
|
|
115
|
+
- `resolveCityObject` (function) · `import { resolveCityObject } from "@jgengine/core/world"`
|
|
116
|
+
|
|
117
|
+
## companion-intent — resolve a companion's command + threat context into a per-tick follow/hold/engage/assist intent
|
|
118
|
+
|
|
119
|
+
- `resolveCompanionIntent` (function) · `import { resolveCompanionIntent } from "@jgengine/core/scene/companion"`
|
|
120
|
+
|
|
121
|
+
## companion-roster — manage tamed companions — allegiance, commands, per-companion stats/leveling, and mobBrain-shaped intent
|
|
122
|
+
|
|
123
|
+
- `createCompanionRoster` (function) · `import { createCompanionRoster } from "@jgengine/core/scene/companion"`
|
|
124
|
+
|
|
90
125
|
## contour-interval — readable auto-spacing for elevation contours
|
|
91
126
|
|
|
92
127
|
- `chooseContourInterval` (function) · `import { chooseContourInterval } from "@jgengine/core/world"`
|
|
@@ -111,6 +146,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
111
146
|
|
|
112
147
|
- `entityMetaOf` (function) · `import { entityMetaOf } from "@jgengine/core/world"`
|
|
113
148
|
|
|
149
|
+
## flight-dynamics — simulate fixed-wing, helicopter, and VTOL aircraft
|
|
150
|
+
|
|
151
|
+
- `createAircraftDynamics` (function) · `import { createAircraftDynamics } from "@jgengine/core/world"`
|
|
152
|
+
|
|
114
153
|
## follow-trail — trailing follower/snake formation that chases a leader
|
|
115
154
|
|
|
116
155
|
- `createLeaderTrail` (function) · `import { createLeaderTrail } from "@jgengine/core/world"`
|
|
@@ -159,6 +198,12 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
159
198
|
|
|
160
199
|
- `createGrappleSwing` (function) · `import { createGrappleSwing } from "@jgengine/core/world"`
|
|
161
200
|
|
|
201
|
+
## gravity-field — sample configurable uniform, radial, or combined gravity
|
|
202
|
+
|
|
203
|
+
- `combineGravity` (function) · `import { combineGravity } from "@jgengine/core/world"`
|
|
204
|
+
- `pointGravity` (function) · `import { pointGravity } from "@jgengine/core/world"`
|
|
205
|
+
- `uniformGravity` (function) · `import { uniformGravity } from "@jgengine/core/world"`
|
|
206
|
+
|
|
162
207
|
## hold-order — stand-ground order that holds position until preempted or canceled
|
|
163
208
|
|
|
164
209
|
- `defineHoldOrder` (function) · `import { defineHoldOrder } from "@jgengine/core/world"`
|
|
@@ -191,6 +236,23 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
191
236
|
|
|
192
237
|
- `generateLock` (function) · `import { generateLock } from "@jgengine/core/world"`
|
|
193
238
|
|
|
239
|
+
## mesh-hitboxes — shots pass through holes in concave models — opted-in catalog assets raycast their actual triangles instead of the fitted box.
|
|
240
|
+
|
|
241
|
+
- `ColliderShape` (type) · `import { ColliderShape } from "@jgengine/core/scene/colliders"`
|
|
242
|
+
|
|
243
|
+
## mesh-movement — the walking player slides against a mesh collider's compound `boxes` — a capsule walks THROUGH an opted-in archway opening and is stopped by its pillars/lintel instead of one outer box.
|
|
244
|
+
|
|
245
|
+
- `ColliderShape` (type) · `import { ColliderShape } from "@jgengine/core/scene/colliders"`
|
|
246
|
+
|
|
247
|
+
## minimap-bake — rasterize authored terrain + zones into a top-down minimap image
|
|
248
|
+
|
|
249
|
+
- `bakeMinimapImage` (function) · `import { bakeMinimapImage } from "@jgengine/core/world"`
|
|
250
|
+
- `minimapBakeToPngDataUri` (function) · `import { minimapBakeToPngDataUri } from "@jgengine/core/world"`
|
|
251
|
+
|
|
252
|
+
## minimap-track — project a world point to a 0..1 fraction along a linear track axis
|
|
253
|
+
|
|
254
|
+
- `trackFraction` (function) · `import { trackFraction } from "@jgengine/core/world/minimapTrack"`
|
|
255
|
+
|
|
194
256
|
## model-sockets — named attachment points read from a model
|
|
195
257
|
|
|
196
258
|
- `ModelNode` (interface) · `import { ModelNode } from "@jgengine/core/world"`
|
|
@@ -219,6 +281,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
219
281
|
|
|
220
282
|
- `pathLength` (function) · `import { pathLength } from "@jgengine/core/world"`
|
|
221
283
|
|
|
284
|
+
## path-network — build a deterministic road/track graph (nodes, edges, streets, junctions, bridges, tunnels) from sliders
|
|
285
|
+
|
|
286
|
+
- `buildPathNetwork` (function) · `import { buildPathNetwork } from "@jgengine/core/world/pathNetwork"`
|
|
287
|
+
|
|
222
288
|
## path-terrain — apply flatten/grade/carve/retaining path profiles to a terrain field
|
|
223
289
|
|
|
224
290
|
- `applyPathProfiles` (function) · `import { applyPathProfiles } from "@jgengine/core/world"`
|
|
@@ -228,6 +294,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
228
294
|
|
|
229
295
|
- `definePatrolOrder` (function) · `import { definePatrolOrder } from "@jgengine/core/world"`
|
|
230
296
|
|
|
297
|
+
## piece-sockets-from-model — derive build-piece snap sockets from a model's named attach points
|
|
298
|
+
|
|
299
|
+
- `pieceSocketsFromModel` (function) · `import { pieceSocketsFromModel } from "@jgengine/core/world/buildSockets"`
|
|
300
|
+
|
|
231
301
|
## place-asset — resolve a placement commit into a shared asset placement payload
|
|
232
302
|
|
|
233
303
|
- `PlaceAssetResult` (interface) · `import { PlaceAssetResult } from "@jgengine/core/world"`
|
|
@@ -248,10 +318,26 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
248
318
|
- `parseRotationPolicy` (function) · `import { parseRotationPolicy } from "@jgengine/core/world"`
|
|
249
319
|
- `snapHeading` (function) · `import { snapHeading } from "@jgengine/core/world"`
|
|
250
320
|
|
|
321
|
+
## pole-line — editor-authorable poles + sagging cables along a path
|
|
322
|
+
|
|
323
|
+
- `POLE_LINE_KIND` (const) · `import { POLE_LINE_KIND } from "@jgengine/core/world/poleLineKind"`
|
|
324
|
+
|
|
325
|
+
## population-director — ambient per-region creature population manager with timed weighted respawns and caps
|
|
326
|
+
|
|
327
|
+
- `createPopulationDirector` (function) · `import { createPopulationDirector } from "@jgengine/core/ai/populationDirector"`
|
|
328
|
+
|
|
329
|
+
## portable-marker-source — project caller-owned arrays or subscribable stores into cached minimap marker snapshots without a MarkerSet
|
|
330
|
+
|
|
331
|
+
- `createMarkerSource` (function) · `import { createMarkerSource } from "@jgengine/core/world"`
|
|
332
|
+
|
|
251
333
|
## proximity-prompt — a "press E" contextual prompt shown near an interactable
|
|
252
334
|
|
|
253
335
|
- `resolveActivePrompt` (function) · `import { resolveActivePrompt } from "@jgengine/core/world"`
|
|
254
336
|
|
|
337
|
+
## pursuit — chase-to-reach cooldown-gated attack with optional leash-to-home; owns the attack cooldown
|
|
338
|
+
|
|
339
|
+
- `advancePursuit` (function) · `import { advancePursuit } from "@jgengine/core/ai/pursuit"`
|
|
340
|
+
|
|
255
341
|
## qte — a quick-time-event timed input sequence with hit windows
|
|
256
342
|
|
|
257
343
|
- `evaluateQteSequence` (function) · `import { evaluateQteSequence } from "@jgengine/core/world"`
|
|
@@ -260,6 +346,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
260
346
|
|
|
261
347
|
- `tierForStanding` (function) · `import { tierForStanding } from "@jgengine/core/world"`
|
|
262
348
|
|
|
349
|
+
## resource-node — depletable, respawning harvest nodes with tool-dependent yields
|
|
350
|
+
|
|
351
|
+
- `createResourceNodeField` (function) · `import { createResourceNodeField } from "@jgengine/core/world/resourceNode"`
|
|
352
|
+
|
|
263
353
|
## rts-control-groups — map Ctrl+digit / digit / double-tap presses onto selection-bookmark bind / recall / focus intents
|
|
264
354
|
|
|
265
355
|
- `controlGroupKey` (function) · `import { controlGroupKey } from "@jgengine/core/world"`
|
|
@@ -357,6 +447,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
357
447
|
|
|
358
448
|
- `terrainContourGuides` (function) · `import { terrainContourGuides } from "@jgengine/core/world"`
|
|
359
449
|
|
|
450
|
+
## terrain-raycast — pick a point on a heightfield along a camera/pointer ray
|
|
451
|
+
|
|
452
|
+
- `raycastHeightField` (function) · `import { raycastHeightField } from "@jgengine/core/world/terrain"`
|
|
453
|
+
|
|
360
454
|
## touch-controls — default on-screen button silhouette for a touch action
|
|
361
455
|
|
|
362
456
|
- `touchButtonShape` (function) · `import { touchButtonShape } from "@jgengine/core/input/touchScheme"`
|
|
@@ -368,3 +462,15 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
368
462
|
## volumetric-clouds — raymarched cloud layer sky option
|
|
369
463
|
|
|
370
464
|
- `VolumetricCloudsConfig` (interface) · `import { VolumetricCloudsConfig } from "@jgengine/core/world"`
|
|
465
|
+
|
|
466
|
+
## world-environment — compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
|
|
467
|
+
|
|
468
|
+
- `environment` (function) · `import { environment } from "@jgengine/core/world"`
|
|
469
|
+
|
|
470
|
+
## world-place — declare the place a game happens in — flat/round/voxel/board ground, surface laws, per-place physics
|
|
471
|
+
|
|
472
|
+
- `world` (function) · `import { world } from "@jgengine/core/world"`
|
|
473
|
+
|
|
474
|
+
## world-seed — derive a deterministic generator/scatter seed from world id + save/run seed
|
|
475
|
+
|
|
476
|
+
- `seedForPlace` (function) · `import { seedForPlace } from "@jgengine/core/world"`
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Recipe — commanding entities (select → order → tick → resolve)
|
|
2
|
+
|
|
3
|
+
**What this wires:** direct command over a group of entities — pick some, issue
|
|
4
|
+
intent, watch them carry it out and interact on arrival. This is a *composition*,
|
|
5
|
+
not a genre. The exact same wiring is the command layer of a base-defense wave, a
|
|
6
|
+
squad-tactics turn, a creature-herding puzzle, or the topdown-survival phase of a
|
|
7
|
+
game that was a card game a minute ago. You swap content, camera, and
|
|
8
|
+
win-condition — never this architecture.
|
|
9
|
+
|
|
10
|
+
It composes four primitives across three seams. The engine never touches your
|
|
11
|
+
entity store directly; you supply narrow adapters, so the same command loop runs
|
|
12
|
+
over any movement/combat model.
|
|
13
|
+
|
|
14
|
+
## The seams
|
|
15
|
+
|
|
16
|
+
- **Pick → ids.** `createSelectionSet` is an ordered, de-duplicated set of entity
|
|
17
|
+
ids. What *fills* it is up to you — a pointer marquee, a card that names a lane,
|
|
18
|
+
an ability that grabs everything in a radius, an AI planner. Selection is just
|
|
19
|
+
ids; it does not know or care how they were chosen.
|
|
20
|
+
- **Intent → per-entity queue.** Build one shared `createOrderRegistry` and give
|
|
21
|
+
each commandable entity its own `createOrderQueue`. Register verbs as *data*
|
|
22
|
+
(`defineMoveOrder`, `defineAttackMoveOrder`, `defineTargetedOrder`, or your
|
|
23
|
+
own). Every verb composes over two adapters **you** implement against your
|
|
24
|
+
entity store: `OrderMover` (`position` / `moveToward` / `halt`) and
|
|
25
|
+
`OrderTargeting` (`acquire` / `positionOf`). This adapter boundary is what makes
|
|
26
|
+
the loop genre-free.
|
|
27
|
+
- **Resolve → your effect.** In `onTick`, advance each queue. Engagement verbs
|
|
28
|
+
write `{ engaging, inRange }` into `order.state`; you read that flag and resolve
|
|
29
|
+
the actual interaction your way — damage, harvest, repair, heal, dialogue —
|
|
30
|
+
through the combat seam `ctx.scene.entity.effect(...)`. Orders decide *when*;
|
|
31
|
+
the game decides *what*.
|
|
32
|
+
|
|
33
|
+
## Connected wiring
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createSelectionSet } from "@jgengine/core/scene/selection";
|
|
37
|
+
import {
|
|
38
|
+
createOrderRegistry,
|
|
39
|
+
createOrderQueue,
|
|
40
|
+
defineMoveOrder,
|
|
41
|
+
defineAttackMoveOrder,
|
|
42
|
+
defineTargetedOrder,
|
|
43
|
+
} from "@jgengine/core/world";
|
|
44
|
+
|
|
45
|
+
// --- once, at setup -------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
// Adapters bind orders to YOUR entity store — the only game-specific glue.
|
|
48
|
+
const mover = {
|
|
49
|
+
position: (id) => entities.get(id).pos,
|
|
50
|
+
moveToward: (id, to, dt) => stepToward(entities.get(id), to, dt), // your movement
|
|
51
|
+
halt: (id) => (entities.get(id).vel = [0, 0, 0]),
|
|
52
|
+
};
|
|
53
|
+
const targeting = {
|
|
54
|
+
positionOf: (id) => entities.get(id)?.pos,
|
|
55
|
+
acquire: (id) => nearestEnemyOf(id), // your target pick (kd-tree, grid, faction…)
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const registry = createOrderRegistry();
|
|
59
|
+
registry.register(defineMoveOrder({ kind: "move", mover }));
|
|
60
|
+
registry.register(defineAttackMoveOrder({ kind: "attack-move", mover, targeting }));
|
|
61
|
+
registry.register(defineTargetedOrder({ kind: "attack", mover, targeting }));
|
|
62
|
+
|
|
63
|
+
const queues = new Map(); // entityId -> OrderQueue
|
|
64
|
+
const queueOf = (id) => {
|
|
65
|
+
let q = queues.get(id);
|
|
66
|
+
if (!q) queues.set(id, (q = createOrderQueue(registry)));
|
|
67
|
+
return q;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const selection = createSelectionSet();
|
|
71
|
+
|
|
72
|
+
// --- on a command (pointer marquee, a card play, an ability, an AI plan) --
|
|
73
|
+
|
|
74
|
+
function commandSelection(kind, payload) {
|
|
75
|
+
for (const id of selection) {
|
|
76
|
+
queueOf(id).issue({ kind, payload, policy: "replace" }); // shift-queue => "append"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// e.g. commandSelection("attack-move", { point: worldHit });
|
|
80
|
+
|
|
81
|
+
// --- every frame ----------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
function onTick(ctx, dt) {
|
|
84
|
+
for (const [id, q] of queues) {
|
|
85
|
+
q.tick(ctx, dt); // activates/advances/completes orders, drives the mover
|
|
86
|
+
const active = q.active;
|
|
87
|
+
if (active?.state?.inRange) {
|
|
88
|
+
// Engagement verb says a target is in range — resolve it the game's way.
|
|
89
|
+
ctx.scene.entity.effect({ from: id, to: active.state.target, effect: "damage" });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
That is the whole command loop. Everything genre-specific lives in the three
|
|
96
|
+
functions you supplied (`stepToward`, `nearestEnemyOf`, and the `"damage"` effect)
|
|
97
|
+
— never in the order/selection primitives.
|
|
98
|
+
|
|
99
|
+
## Composing it into different games (and hybrids)
|
|
100
|
+
|
|
101
|
+
The wiring above is fixed; four independent knobs turn it into different play:
|
|
102
|
+
|
|
103
|
+
- **Who fills the selection.** Pointer marquee for an RTS; a targeting card for a
|
|
104
|
+
card-driven skirmish; an AoE ability for a spell; an AI director for autobattle.
|
|
105
|
+
Same `selection`, different caller.
|
|
106
|
+
- **Which camera renders it.** `topDown`, `rts`, `shoulder`, or `first` — camera is
|
|
107
|
+
presentation and reads none of this. Swapping the rig never touches the loop.
|
|
108
|
+
- **What the effect does.** `"damage"` for combat; register a `defineTargetedOrder`
|
|
109
|
+
with a `"harvest"` / `"repair"` / `"talk"` effect for non-combat command. The
|
|
110
|
+
engagement seam is generic; combat is one instance.
|
|
111
|
+
- **When intent arrives.** A **mutating** game reuses one set of queues across
|
|
112
|
+
phases: the card-game phase calls `issue()` from card plays; when it turns into
|
|
113
|
+
a topdown-survival phase, the *same* queues now take `move` / `attack-move` from
|
|
114
|
+
pointer input. You changed who calls `issue()`, not the architecture.
|
|
115
|
+
|
|
116
|
+
## Where to go next
|
|
117
|
+
|
|
118
|
+
- Marquee/box-select and right-click-to-command come from the shell pointer seam
|
|
119
|
+
(`PlayableGame.pointer`: `select`, `selectFilter`, `orderCommand`) — that is the
|
|
120
|
+
pointer *picker* that fills `selection` and calls `commandSelection`; see
|
|
121
|
+
`jgengine-ui` for the pointer wiring.
|
|
122
|
+
- Pathing for `moveToward` can use `findPath` (`@jgengine/core/nav/navGrid`) over a
|
|
123
|
+
walkable grid; the mover adapter is where it plugs in.
|
|
124
|
+
- Formations, order preemption policies, save/replication of in-flight orders, and
|
|
125
|
+
the full order-kind contract are in [../reference.md](../reference.md) under
|
|
126
|
+
*Entity orders (command queue)*.
|
|
127
|
+
- Damage numbers, death, and rewards are `jgengine-combat`; the effect call above
|
|
128
|
+
is the seam into it.
|
|
@@ -2,9 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
Reference module for the [`jgengine-world` API](SKILL.md) skill. Load this when you need the renderer-free world surface.
|
|
4
4
|
|
|
5
|
-
## World
|
|
5
|
+
## World = place (the definition of a world)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A world is the place a game happens in — substrate + laws — declared with `world()` from
|
|
8
|
+
`@jgengine/core/world/place`. It is never a coded diorama: sky look, foliage scatter, props, and
|
|
9
|
+
sculpt are scene content authored in the editor (`jgengine-editor`), and the engine renders its
|
|
10
|
+
default sky when the document has none.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { world } from "@jgengine/core/world/place";
|
|
14
|
+
|
|
15
|
+
export const overworld = world({
|
|
16
|
+
id: "overworld", // place identity: saves, seeds, switching
|
|
17
|
+
ground: { mode: "flat", size: { x: Infinity, z: Infinity } },
|
|
18
|
+
physics: { gravity: -24 }, // laws of THIS place
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `ground.mode` discriminates `size`: `flat` takes `{ x, z }` (or `{ x, y, z }`; `Infinity` = unbounded axis), `round` takes `{ radius }` only, `voxel` takes the generator domain `{ x, y, z }` (axes may be `Infinity`), `board` takes `{ x, y }` in cells/layout units (2D surface you look at — grid games, solitaire, tabletop; `stage` is a `board` alias). TS rejects a `radius` on `flat` and `x`/`z` on `round`.
|
|
23
|
+
- `ground.surface` is optional matter/feel law (`{ matter?, friction?, restitution?, traits? }`) systems and audio read — metal vs slime vs felt changes the feel of the *same* rule systems, never forks them. Not a theme kit.
|
|
24
|
+
- `ground.generator` (procedural modes) is algorithm params only — never a seed, never a genre/franchise preset.
|
|
25
|
+
- **No `seed` in a world definition, ever.** The engine derives seeds from world `id` + save/run via `seedForPlace(id, runSeed)` and injects them into generators and scatter.
|
|
26
|
+
- Multiple worlds per game are first-class: one `world()` per place, each with its own ground + physics (`defineGameDefinition` resolves the active place's physics over the game default). Pure UI/rules games omit `world` entirely.
|
|
27
|
+
- Thin defaults: `flat` + infinite axes + default physics for 3D, `board` for 2D, or no world at all. Never scaffold a "meadow with sky preset and seeded grass" — that is editor content.
|
|
28
|
+
|
|
29
|
+
## Legacy world features
|
|
30
|
+
|
|
31
|
+
Descriptors from `@jgengine/core/world/features` — legacy code-declared world shapes kept for
|
|
32
|
+
existing games. Do not start new worlds here; `environment()` is now the consumption target for
|
|
33
|
+
editor/preset-written scene data (`environmentContentFromDocument`), not a world definition:
|
|
8
34
|
|
|
9
35
|
| Feature | Use |
|
|
10
36
|
|---------|-----|
|
|
@@ -25,6 +51,11 @@ Descriptors from `@jgengine/core/world/features` — config data the runner/worl
|
|
|
25
51
|
|
|
26
52
|
`parentSpace` positions are local to that space — convert at seams only.
|
|
27
53
|
|
|
54
|
+
Display-only map consumers use the portable `MarkerView` / `MarkerSource`
|
|
55
|
+
contracts in `@jgengine/core/world/markers`; the React integration and complete
|
|
56
|
+
external-store walkthrough are owned by the
|
|
57
|
+
[portable minimap recipe](../jgengine-ui/recipes/portable-minimap.md).
|
|
58
|
+
|
|
28
59
|
### Query primitives (renderer-free, for gameplay)
|
|
29
60
|
|
|
30
61
|
Pure `@jgengine/core` functions so gameplay reads the same world the shell renders — no three.js needed:
|
|
@@ -84,7 +115,7 @@ Turn data-only placement into the build tooling of Valheim/Enshrouded/The Sims/F
|
|
|
84
115
|
|
|
85
116
|
**Body shape: box or sphere.** `AddBodyOptions` is a discriminated union — `{ shape?: "box", halfExtents }` (box is the default, `shape` omittable) or `{ shape: "sphere", radius }` (the radius fills all three half-extent columns, so broadphase/bounds see the sphere's enclosing AABB). `world.shape[i]` (`SHAPE_BOX` / `SHAPE_SPHERE`) reports a live body's shape for a consumer walking the raw SoA arrays. Sphere-sphere and sphere-box pairs resolve with a proper radial normal (not the axis-aligned box/box path) — balls, projectile bodies, and rolling debris collide correctly against both boxes and each other.
|
|
86
117
|
|
|
87
|
-
**Collision shapes are box/sphere/voxel
|
|
118
|
+
**Collision shapes are box/sphere/voxel — plus opt-in triangle meshes for raycasts and mesh-accurate walk obstruction.** Every collider in the engine — `PhysicsWorld` bodies (box `halfExtents` or sphere `radius`), object/entity picking (`scene/objectQuery` raycasts against unit boxes), and `world/voxelField`/`world/carve` blocks — is an axis-aligned box, a sphere, or a voxel cell. The one exception is opted-in catalog assets: an asset listed in `@jgengine/assets` `COLLISION_MESH_ASSET_IDS` ships a compact quantized triangle mesh in the generated index (`scene/collisionMesh` `CollisionMeshData`, extracted from the GLB at reindex — never from the rendered scene, so a headless host resolves the same shape) **plus a deterministic compound-box decomposition** (`voxelizeToBoxes` → `CollisionMeshData.boxes`, a voxel-rasterize + column-fill + greedy-merge of the model's solid volume). Collider auto-fit then (a) raycasts the actual triangles through a precomputed BVH — shots pass through a torus hole or an archway instead of hitting the fitted box; and (b) exposes the compound boxes on the fitted `mesh` `ColliderShape` (`ColliderShape.mesh.boxes`, scaled/translated onto the placement like the mesh). The `collideObjects` walk controller consumes these in `stepPlayerMovement` (host and client alike, so parity is automatic): it gathers obstacles from resolved blocking physical colliders via a bounded `object.inBox` broadphase (reach ≥ radius + max half-extent, so a wide wall's full span blocks, not a phantom 1 m box), carries each collider's real `halfExtents`/`offset` — or, for a mesh collider, the compound sub-boxes — yaw-rotated onto the obstacle, and slides the capsule against **each** sub-box (`resolveObstacleStep`), so a capsule walks THROUGH an archway opening and is stopped by its pillars/lintel/sill. `PhysicsWorld` and overlap tests still see the conservative fitted AABB; objects with no resolved collider keep today's default box, objects whose colliders don't block stop obstructing, and non-opted assets keep the fitted box everywhere. The seams for custom *walk* collision remain `movement.beforeCommit` and object raycasts.
|
|
88
119
|
|
|
89
120
|
**Ballistic collision sweep (`physics/ballisticSweep`).** `createBallisticSweep(world, { step?, radius? })` → `BallisticSweep`, a `(origin, velocity, gravity, maxTime) => BallisticSweepHit | null` function that marches the closed-form arc (constant gravity, straight lateral) through a `PhysicsWorld` and reports the first sample inside any live body's AABB (sleeping bodies included), refined by one bisection step; `null` means the whole arc is clear. `step` (default 1/60) is the march interval in seconds, `radius` (default 0) inflates every body's AABB before the point test — pass the projectile's own radius. Wire it into `combat/projectiles` via `ProjectileSystemDeps.sweepBallistic`: when set, a ballistic shot settles at the swept impact point instead of the closed-form landing; omitted or `null` falls back to that closed-form arc.
|
|
90
121
|
|
|
@@ -160,6 +191,15 @@ runtime.step({
|
|
|
160
191
|
// types: TriggerEvent · TriggerActionDefinition · AuthoredTrigger · TriggerHandlers · TriggerSourceKind · AuthoredTriggerRuntime
|
|
161
192
|
```
|
|
162
193
|
|
|
194
|
+
**Built-in actions** — every game gets `announce` / `win` / `advance` for free, so "enter this zone → win" is authored entirely in the editor with no bespoke action code. `registerBuiltinTriggerActions()` exposes them in the inspector; `createTriggerOutcome()` returns a headless, serializable `TriggerOutcome` (`{ won, message, tone, objective, revision }`) plus the `handlers` to pass the runtime — the HUD reads it via `useSyncExternalStore`. That pair is the whole zero-code "reach the goal to win" wiring the create scaffold ships (a `goal` marker with `{ on: enter, action: win }`).
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
registerBuiltinTriggerActions(); // announce/win/advance in the editor
|
|
198
|
+
const outcome = createTriggerOutcome(); // { get, subscribe, handlers, reset }
|
|
199
|
+
const runtime = createAuthoredTriggerRuntime({ document: editorLayers, handlers: outcome.handlers });
|
|
200
|
+
// HUD: const status = useSyncExternalStore(outcome.subscribe, outcome.get, outcome.get); // status.won / status.message
|
|
201
|
+
```
|
|
202
|
+
|
|
163
203
|
Multi-triggers: `meta.triggers: [{ on, action, ...params }]`. Editor inspector renders action params from the declared schema when the game has registered actions.
|
|
164
204
|
|
|
165
205
|
### Continuous area effects (source-following membership)
|
|
@@ -219,4 +259,3 @@ Path progress adapter (`nav/pathFollow`): `PathProgress` is `{ kind: "normalized
|
|
|
219
259
|
- **Preemption policy** on each issue: `replace` (preempt active + clear pending), `append` (shift-queue), `front` (jump the queue without dropping the active order), `reject` (refuse while busy). Mark an order `uninterruptible` and a replacing order waits until it finishes; explicit `cancelActive` still forces it.
|
|
220
260
|
- **Order kinds** are `OrderKind<Ctx, Payload>` with `start`/`update`/`finish`, composed over two narrow adapters — `OrderMover` (`position`/`moveToward`/`halt`) and `OrderTargeting` (`acquire`/`positionOf`). Built-ins ship as ordinary compositions with a configurable `kind` string so a game re-skins a verb ("harvest") without engine edits: `defineMoveOrder`, `defineStopOrder`, `defineHoldOrder`, `defineAttackMoveOrder`, `defineTargetedOrder`, `definePatrolOrder`. Engagement verbs write `{ engaging, inRange }` into `order.state`; the game reads it to run the actual attack/effect, keeping combat resolution game-side.
|
|
221
261
|
|
|
222
|
-
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: level-design
|
|
3
|
+
description: Design, audit, author, implement, and validate readable levels, worlds, encounters, navigation, and pacing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Level design
|
|
7
|
+
|
|
8
|
+
## Ownership
|
|
9
|
+
|
|
10
|
+
Own the spatial realization of game design: player metrics, topology, routes, landmarks, gates, encounter geometry, challenge sequencing, exploration, pacing, recovery, and spatial storytelling. Use `game-design` for the player promise and loops, `jgengine-editor` to author space, and `jgengine-world` to consume it.
|
|
11
|
+
|
|
12
|
+
All authorable world content belongs in `editor.scene.json`, changed through the editor GUI or RPC/CLI. Never hand-edit the document or keep geometry, paths, spawns, zones, objectives, terrain, foliage, encounters, or coordinate arrays in code. File an editor `[FEATURE]` issue before any fallback.
|
|
13
|
+
|
|
14
|
+
Read [references/application-playbook.md](references/application-playbook.md) only when the task explicitly calls for spatial design depth — a level/world design brief, an audit or redesign, or "make this fun/better" work; it is not a mandatory step of creating a game. Read [references/live-editor-loop.md](references/live-editor-loop.md) whenever live captures, an editor-agent handoff, or iterative scene improvement is involved. Read [references/field-guide.md](references/field-guide.md) for metric families, topology, pacing, genre lenses, accessibility, and research.
|
|
15
|
+
|
|
16
|
+
## Choose the mode
|
|
17
|
+
|
|
18
|
+
### Greenfield
|
|
19
|
+
|
|
20
|
+
Measure the real controller, camera, interaction, combat, AI, and network constraints in an authored metric gym. Produce a topology graph, critical/optional route model, beat sheet, learning sequence, encounter cards, authoring plan, and falsifiable completion scenario before art production.
|
|
21
|
+
|
|
22
|
+
### Existing game
|
|
23
|
+
|
|
24
|
+
Start with a spatial-truth audit. Inventory every source of coordinates and semantics; report any gameplay placement outside `editor.scene.json` as a blocking ownership defect. Reconstruct the critical path and representative optional path from scene data and live play. Measure travel, decision, encounter, retry, and recovery time. Inspect first-look orientation, reverse navigation, affordances, spawn safety, combat positions, pacing contrast, and visual hierarchy.
|
|
25
|
+
|
|
26
|
+
“Improve with level design” is an authoring and implementation request. Do not stop at a map critique: select the highest-leverage route/zone/encounter slice, author the change through editor operations, connect runtime semantics, and prove it in play.
|
|
27
|
+
|
|
28
|
+
For a live editor handoff, send evidence and an executable authoring contract—not an aesthetic request. Require the editor agent to inspect authority, document revision, ids, layers, assets, runtime state, and tool support before mutation; apply one coherent hypothesis through undoable operations; then return the exact revision/change log for replay and comparison. Stop when the live editor is not connected to the runtime's authoritative scene or the required operation is unsupported.
|
|
29
|
+
|
|
30
|
+
## Required output contract
|
|
31
|
+
|
|
32
|
+
Make every level-design pass yield:
|
|
33
|
+
|
|
34
|
+
1. **Spatial truth audit:** scene counts and semantic kinds plus every code-owned coordinate table, generated placement, or duplicated position that must migrate.
|
|
35
|
+
2. **Metric sheet:** measured movement, camera, interaction, weapon/AI, readability, density, performance, multiplayer, and retry constraints with units.
|
|
36
|
+
3. **Topology and journey:** nodes/edges, critical and optional routes, gates, returns, landmarks, expected traversal times, and first-minute/mission/campaign beats.
|
|
37
|
+
4. **Zone/encounter diagnosis:** purpose, entry knowledge/state, choices, geometry, actors, escalation, resources, exits, failure/reset, reward, and downstream change.
|
|
38
|
+
5. **Bottleneck ranking:** observed symptom, player consequence, root cause, evidence, reach, confidence, effort, dependencies, and ownership.
|
|
39
|
+
6. **Editor change set:** exact semantic ids/kinds and operations to add/move/remove for paths, markers, zones, spawns, objectives, checkpoints, props, and terrain—without putting coordinates in code.
|
|
40
|
+
7. **Acceptance evidence:** document assertions, route/encounter tests, fresh-player play scenario, pacing/wayfinding thresholds, inspected screenshots, and before/after comparison.
|
|
41
|
+
|
|
42
|
+
## Execution rules
|
|
43
|
+
|
|
44
|
+
- Graph before geometry; graybox before art; gameplay and wayfinding before decoration.
|
|
45
|
+
- Present decision-relevant information before commitment. Make alternate routes differ in risk, cost, verbs, information, reward, or experience.
|
|
46
|
+
- Sequence teaching through cue, safe use, feedback, independent test, combination, transformation, and mastery as the audience requires.
|
|
47
|
+
- Align composition, landmark, value/color, motion, audio, affordance, path shape, and optional UI guidance. No marker can repair contradictory space.
|
|
48
|
+
- Design pacing across separate threat, motor, cognitive, navigation, emotional, resource, and social axes. Constant pressure or empty traversal is not pacing.
|
|
49
|
+
- Make every encounter support intended verbs, counterplay, retreat, fair spawns, recovery, and bounded performance.
|
|
50
|
+
- Populate only after the graybox survives play. Preserve silhouettes, safe footing, goals, threats, interactables, teams, and traversal boundaries through art passes.
|
|
51
|
+
|
|
52
|
+
## Completion
|
|
53
|
+
|
|
54
|
+
An audit or prettier screenshot is not completion for a build/improve request. Finish when the scene document owns spatial truth; a fresh target player can orient, choose, act, recover, and complete the scenario without coaching; the route has intentional decisions and pacing; encounter exploits and alternate states are tested; and `jgengine-verify` records document, gameplay, and inspected visual evidence.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Level Design"
|
|
3
|
+
short_description: "Design readable, paced, playable spaces"
|
|
4
|
+
default_prompt: "Use $level-design to observe this world live, brief and drive the editor agent through an authoritative scene change, then replay the same journey and prove the result."
|