@jgengine/core 0.19.0 → 0.22.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 +364 -0
- package/dist/ai/decisionGraph.d.ts +2 -0
- package/dist/ai/decisionGraph.js +6 -0
- package/dist/ai/flock.d.ts +1 -1
- package/dist/ai/flock.js +7 -4
- package/dist/anim/animGraph.d.ts +33 -2
- package/dist/anim/animGraph.js +200 -12
- package/dist/anim/locomotionGraph.d.ts +11 -3
- package/dist/anim/locomotionGraph.js +25 -15
- package/dist/audio/audioFalloff.d.ts +4 -0
- package/dist/audio/voiceAllocator.d.ts +46 -0
- package/dist/audio/voiceAllocator.js +76 -0
- package/dist/combat/abilityKit.d.ts +19 -0
- package/dist/combat/abilityKit.js +40 -4
- package/dist/combat/death.d.ts +4 -3
- package/dist/combat/death.js +3 -3
- package/dist/combat/magazine.d.ts +9 -0
- package/dist/combat/magazine.js +24 -2
- package/dist/combat/projectiles.d.ts +94 -2
- package/dist/combat/projectiles.js +282 -61
- package/dist/combat/regenShield.d.ts +14 -0
- package/dist/combat/regenShield.js +23 -7
- package/dist/crafting/recipeCatalog.d.ts +16 -0
- package/dist/crafting/recipeCatalog.js +55 -0
- package/dist/economy/auctionBook.d.ts +1 -1
- package/dist/economy/auctionBook.js +11 -1
- package/dist/economy/listingBook.d.ts +1 -1
- package/dist/economy/listingBook.js +11 -1
- package/dist/economy/resourceLedger.js +3 -3
- package/dist/economy/shopStock.js +7 -6
- package/dist/economy/techTree.js +7 -6
- package/dist/economy/unlockPoints.js +3 -6
- package/dist/editor/commandApply.js +89 -16
- package/dist/editor/commands.d.ts +38 -1
- package/dist/editor/commands.js +320 -3
- package/dist/editor/creatorStorage.d.ts +59 -0
- package/dist/editor/creatorStorage.js +164 -0
- package/dist/editor/document.d.ts +4 -0
- package/dist/editor/document.js +116 -17
- package/dist/editor/index.d.ts +3 -0
- package/dist/editor/index.js +1 -0
- package/dist/editor/liveSync.d.ts +4 -1
- package/dist/editor/liveSync.js +26 -10
- package/dist/editor/materialAuthoring.d.ts +14 -0
- package/dist/editor/materialAuthoring.js +38 -0
- package/dist/editor/modelNodeVisibility.d.ts +11 -0
- package/dist/editor/modelNodeVisibility.js +29 -0
- package/dist/editor/movementCatalog.d.ts +90 -0
- package/dist/editor/movementCatalog.js +144 -0
- package/dist/editor/simulation.d.ts +60 -0
- package/dist/editor/simulation.js +202 -0
- package/dist/editor/staticPrefab.d.ts +17 -0
- package/dist/editor/staticPrefab.js +51 -0
- package/dist/editor/terrainMaterials.d.ts +10 -0
- package/dist/editor/terrainMaterials.js +13 -0
- package/dist/editor/types.d.ts +11 -0
- package/dist/game/cameraConfig.d.ts +3 -1
- package/dist/game/clipRoles.d.ts +2 -1
- package/dist/game/clipRoles.js +7 -3
- package/dist/game/contentValidation.d.ts +69 -0
- package/dist/game/contentValidation.js +118 -0
- package/dist/game/controlGate.d.ts +8 -0
- package/dist/game/controlGate.js +43 -6
- package/dist/game/defineGame.d.ts +32 -3
- package/dist/game/dialogueGraph.d.ts +20 -0
- package/dist/game/dialogueGraph.js +107 -0
- package/dist/game/events.d.ts +5 -0
- package/dist/game/keyValueStore.d.ts +17 -4
- package/dist/game/keyValueStore.js +35 -23
- package/dist/game/lootModifiers.d.ts +17 -0
- package/dist/game/lootModifiers.js +26 -0
- package/dist/game/narrativeAuthoring.d.ts +67 -0
- package/dist/game/narrativeAuthoring.js +122 -0
- package/dist/game/playableGame.d.ts +52 -20
- package/dist/game/quest.d.ts +15 -0
- package/dist/game/quest.js +209 -63
- package/dist/game/questCatalog.d.ts +50 -0
- package/dist/game/questCatalog.js +205 -0
- package/dist/game/saveStore.d.ts +6 -5
- package/dist/game/saveStore.js +57 -57
- package/dist/game/systemRuntime.d.ts +2 -2
- package/dist/game/systemRuntime.js +84 -33
- package/dist/gameplay.d.ts +3 -0
- package/dist/gameplay.js +3 -0
- package/dist/interaction/promptRegistry.d.ts +5 -4
- package/dist/interaction/promptRegistry.js +8 -2
- package/dist/inventory/inventoryModel.js +9 -6
- package/dist/inventory/shapedGrid.d.ts +48 -9
- package/dist/inventory/shapedGrid.js +69 -10
- package/dist/item/durability.js +4 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -1
- package/dist/item/itemInstanceRegistry.js +111 -7
- package/dist/item/itemgen.d.ts +1 -1
- package/dist/item/itemgen.js +1 -1
- package/dist/item/modularItem.js +7 -1
- package/dist/material/appearanceSignals.d.ts +53 -0
- package/dist/material/appearanceSignals.js +47 -0
- package/dist/material/hairCards.d.ts +47 -0
- package/dist/material/hairCards.js +136 -0
- package/dist/material/materialAsset.d.ts +162 -0
- package/dist/material/materialAsset.js +382 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +348 -1
- package/dist/movement/characterController.d.ts +5 -2
- package/dist/movement/characterController.js +50 -23
- package/dist/movement/freeFlight.d.ts +2 -2
- package/dist/movement/freeFlight.js +3 -3
- package/dist/movement/movementModel.d.ts +24 -1
- package/dist/movement/movementModel.js +74 -7
- package/dist/movement/playerMovement.d.ts +28 -1
- package/dist/movement/playerMovement.js +425 -102
- package/dist/movement/poseState.d.ts +2 -0
- package/dist/movement/poseState.js +29 -1
- package/dist/movement/solidObstacles.d.ts +3 -1
- package/dist/movement/solidObstacles.js +8 -5
- package/dist/movement/terrainGrade.d.ts +13 -0
- package/dist/movement/terrainGrade.js +33 -0
- package/dist/movement/voxelController.d.ts +2 -2
- package/dist/movement/voxelController.js +3 -3
- package/dist/nav/navGrid.d.ts +19 -1
- package/dist/nav/navGrid.js +26 -10
- package/dist/nav/solidRoute.d.ts +50 -0
- package/dist/nav/solidRoute.js +144 -0
- package/dist/physics/ballisticSweep.d.ts +10 -3
- package/dist/physics/ballisticSweep.js +53 -29
- package/dist/physics/forceVolume.d.ts +41 -0
- package/dist/physics/forceVolume.js +88 -0
- package/dist/physics/kinematicVehicle.d.ts +8 -3
- package/dist/physics/kinematicVehicle.js +2 -2
- package/dist/physics/physicsWorldBackend.js +19 -3
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +8 -9
- package/dist/physics/vehicleDynamics.d.ts +3 -2
- package/dist/physics/vehicleDynamics.js +2 -2
- package/dist/physics/vehicleObstacles.d.ts +3 -2
- package/dist/physics/vehicleObstacles.js +29 -5
- package/dist/runtime/cameraDirector.d.ts +39 -2
- package/dist/runtime/cameraDirector.js +162 -0
- package/dist/runtime/commandDispatch.d.ts +14 -0
- package/dist/runtime/commandDispatch.js +19 -0
- package/dist/runtime/context/combat.d.ts +11 -4
- package/dist/runtime/context/combat.js +178 -11
- package/dist/runtime/context/deathLoot.d.ts +9 -8
- package/dist/runtime/context/deathLoot.js +16 -16
- package/dist/runtime/context/scene.d.ts +2 -0
- package/dist/runtime/context/scene.js +13 -21
- package/dist/runtime/descriptors/baseline.js +34 -3
- package/dist/runtime/descriptors/features.d.ts +2 -1
- package/dist/runtime/descriptors/features.js +18 -0
- package/dist/runtime/gameContext.js +79 -13
- package/dist/runtime/gameContextTypes.d.ts +14 -0
- package/dist/runtime/headlessRunner.d.ts +3 -1
- package/dist/runtime/headlessRunner.js +11 -11
- package/dist/runtime/hostedGameRunner.d.ts +24 -1
- package/dist/runtime/hostedGameRunner.js +156 -10
- package/dist/runtime/hostedWorldSession.d.ts +17 -5
- package/dist/runtime/hostedWorldSession.js +154 -16
- package/dist/runtime/hostedWorldStore.d.ts +2 -2
- package/dist/runtime/inputRecorder.d.ts +1 -1
- package/dist/runtime/inputRecorder.js +21 -1
- package/dist/runtime/inputSnapshot.d.ts +16 -4
- package/dist/runtime/inputSnapshot.js +31 -3
- package/dist/runtime/runtimeSave.d.ts +10 -6
- package/dist/runtime/runtimeSave.js +11 -5
- package/dist/runtime/simContext.d.ts +8 -3
- package/dist/runtime/simContext.js +7 -3
- package/dist/runtime/simSnapshot.js +2 -0
- package/dist/runtime/worldReplication.d.ts +2 -0
- package/dist/runtime/worldReplication.js +1 -1
- package/dist/scene/assetCatalog.d.ts +2 -0
- package/dist/scene/behaviorRuntime.d.ts +26 -5
- package/dist/scene/behaviorRuntime.js +263 -16
- package/dist/scene/behaviors.d.ts +30 -1
- package/dist/scene/behaviors.js +29 -0
- package/dist/scene/entityStore.d.ts +14 -3
- package/dist/scene/entityStore.js +45 -9
- package/dist/scene/mount.d.ts +9 -0
- package/dist/scene/mount.js +35 -0
- package/dist/scene/possession.d.ts +2 -0
- package/dist/scene/possession.js +21 -1
- package/dist/scene/sceneRaycast.d.ts +7 -0
- package/dist/scene/sceneRaycast.js +55 -0
- package/dist/scene/spatial.d.ts +20 -1
- package/dist/scene/spatial.js +94 -2
- package/dist/scene/vehicleSeat.d.ts +36 -2
- package/dist/scene/vehicleSeat.js +89 -2
- package/dist/stats/accumulatorMeter.d.ts +10 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +9 -2
- package/dist/stats/eventMeter.js +5 -2
- package/dist/store/defineStore.d.ts +3 -1
- package/dist/store/defineStore.js +3 -1
- package/dist/store/observableKeyedStore.d.ts +1 -1
- package/dist/store/observableKeyedStore.js +8 -6
- package/dist/time/simClock.d.ts +6 -2
- package/dist/time/simClock.js +5 -0
- package/dist/vfx/particleDirector.d.ts +58 -6
- package/dist/vfx/particleDirector.js +113 -22
- package/dist/vfx/particles.d.ts +68 -5
- package/dist/vfx/particles.js +284 -32
- package/dist/visibility/assetStreaming.d.ts +21 -3
- package/dist/visibility/assetStreaming.js +108 -24
- package/dist/visibility/settings.d.ts +8 -2
- package/dist/visibility/settings.js +3 -0
- package/dist/visibility/visibilitySystem.js +1 -0
- package/dist/work/expedition.d.ts +110 -0
- package/dist/work/expedition.js +195 -0
- package/dist/work/staffedStation.d.ts +29 -0
- package/dist/work/staffedStation.js +27 -0
- package/dist/world/authoredAnimation.d.ts +38 -0
- package/dist/world/authoredAnimation.js +99 -0
- package/dist/world/authoredObjects.d.ts +24 -4
- package/dist/world/authoredObjects.js +43 -16
- package/dist/world/authoredSimulation.d.ts +74 -0
- package/dist/world/authoredSimulation.js +302 -0
- package/dist/world/authoredWeather.d.ts +120 -0
- package/dist/world/authoredWeather.js +314 -0
- package/dist/world/buildingKit.d.ts +8 -3
- package/dist/world/buildingKit.js +21 -8
- package/dist/world/environmentForces.d.ts +21 -0
- package/dist/world/environmentForces.js +42 -0
- package/dist/world/features.d.ts +3 -3
- package/dist/world/footprintGrid.d.ts +55 -0
- package/dist/world/footprintGrid.js +201 -0
- package/dist/world/grassKind.d.ts +10 -0
- package/dist/world/grassKind.js +22 -0
- package/dist/world/pathTerrain.d.ts +1 -1
- package/dist/world/terraform.d.ts +2 -2
- package/dist/world/terraform.js +46 -18
- package/dist/world/weather.d.ts +17 -0
- package/dist/world/weather.js +116 -20
- package/dist/world/worldSolids.d.ts +11 -0
- package/dist/world/worldSolids.js +112 -1
- package/dist/world.d.ts +5 -5
- package/dist/world.js +4 -4
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/game-design/SKILL.md +2 -0
- package/skills/jgengine/SKILL.md +23 -2
- package/skills/jgengine/api.md +28 -17
- package/skills/jgengine/recipes/minimal-game.md +7 -2
- package/skills/jgengine-combat/SKILL.md +8 -2
- package/skills/jgengine-combat/api.md +22 -10
- package/skills/jgengine-combat/recipes/portable-runtime-state.md +60 -6
- package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +37 -3
- package/skills/jgengine-combat/reference.md +24 -2
- package/skills/jgengine-gameplay/SKILL.md +63 -0
- package/skills/jgengine-gameplay/api.md +131 -37
- package/skills/jgengine-gameplay/capabilities.md +68 -0
- package/skills/jgengine-gameplay/recipes/device-save-status.md +46 -0
- package/skills/jgengine-gameplay/recipes/expeditions.md +108 -0
- package/skills/jgengine-gameplay/recipes/narrative-authoring.md +76 -0
- package/skills/jgengine-gameplay/recipes/paged-item-storage.md +101 -0
- package/skills/jgengine-gameplay/recipes/quest-authoring.md +63 -0
- package/skills/jgengine-gameplay/recipes/shaped-inventory.md +50 -0
- package/skills/jgengine-gameplay/reference-content-validation.md +74 -0
- package/skills/jgengine-gameplay/reference-systems.md +119 -0
- package/skills/jgengine-world/SKILL.md +12 -3
- package/skills/jgengine-world/api.md +201 -58
- package/skills/jgengine-world/capabilities.md +116 -3
- package/skills/jgengine-world/recipes/character-animation.md +31 -8
- package/skills/jgengine-world/recipes/controls.md +4 -0
- package/skills/jgengine-world/recipes/npc-senses.md +12 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +3 -1
- package/skills/jgengine-world/reference.md +167 -5
- package/skills/jgengine-world/visibility.md +28 -1
|
@@ -13,6 +13,15 @@ export interface WorldSolid {
|
|
|
13
13
|
export interface WorldSolidsState {
|
|
14
14
|
layers: Record<string, readonly WorldSolid[]>;
|
|
15
15
|
}
|
|
16
|
+
/** Caller-owned work counters, reset by each world-solid ray candidate query. */
|
|
17
|
+
export interface WorldSolidRayWork {
|
|
18
|
+
/** Hash-grid cell lookups, including empty cells. */
|
|
19
|
+
cells: number;
|
|
20
|
+
/** Indexed entry visits, including duplicates removed before bounds tests. */
|
|
21
|
+
entries: number;
|
|
22
|
+
/** Cached AABB ray tests after deduplication. */
|
|
23
|
+
bounds: number;
|
|
24
|
+
}
|
|
16
25
|
/**
|
|
17
26
|
* Static collision for world geometry that is not a scene object: generated buildings, studio
|
|
18
27
|
* volumes like `city`, wall runs. Solids live in named layers so a feature can replace or drop its
|
|
@@ -30,6 +39,8 @@ export interface WorldSolids {
|
|
|
30
39
|
layers(): readonly string[];
|
|
31
40
|
/** Every solid whose world AABB overlaps `min`..`max`. Bounded by a uniform XZ hash grid. */
|
|
32
41
|
inBox(min: readonly [number, number, number], max: readonly [number, number, number]): WorldSolid[];
|
|
42
|
+
/** Optional accelerated ray candidates; finite coordinates/direction and nonnegative finite distance, normalized internally. */
|
|
43
|
+
inRay?(origin: readonly [number, number, number], direction: readonly [number, number, number], maxDistance: number, work?: WorldSolidRayWork): WorldSolid[];
|
|
33
44
|
/** Every solid in every layer. */
|
|
34
45
|
all(): WorldSolid[];
|
|
35
46
|
count(): number;
|
|
@@ -31,6 +31,8 @@ export function worldSolidFootprint(solid) {
|
|
|
31
31
|
*/
|
|
32
32
|
export function createWorldSolids(options = {}) {
|
|
33
33
|
const cellSize = options.cellSize ?? DEFAULT_CELL_SIZE;
|
|
34
|
+
if (!Number.isFinite(cellSize) || cellSize <= 0)
|
|
35
|
+
throw new RangeError("World solid cellSize must be finite and positive");
|
|
34
36
|
const layerMap = new Map();
|
|
35
37
|
const grid = new Map();
|
|
36
38
|
const oversize = [];
|
|
@@ -47,7 +49,7 @@ export function createWorldSolids(options = {}) {
|
|
|
47
49
|
const x1 = cellOf(entry.maxX);
|
|
48
50
|
const z0 = cellOf(entry.minZ);
|
|
49
51
|
const z1 = cellOf(entry.maxZ);
|
|
50
|
-
if ((x1 - x0 + 1) * (z1 - z0 + 1) > MAX_CELLS_PER_SOLID) {
|
|
52
|
+
if (![x0, x1, z0, z1].every(Number.isSafeInteger) || (x1 - x0 + 1) * (z1 - z0 + 1) > MAX_CELLS_PER_SOLID) {
|
|
51
53
|
oversize.push(entry);
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
@@ -95,6 +97,28 @@ export function createWorldSolids(options = {}) {
|
|
|
95
97
|
entry.maxZ >= min[2] &&
|
|
96
98
|
entry.minZ <= max[2]);
|
|
97
99
|
}
|
|
100
|
+
function rayOverlaps(entry, origin, direction, maxDistance) {
|
|
101
|
+
let near = 0;
|
|
102
|
+
let far = maxDistance;
|
|
103
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
104
|
+
const min = axis === 0 ? entry.minX : axis === 1 ? entry.minY : entry.minZ;
|
|
105
|
+
const max = axis === 0 ? entry.maxX : axis === 1 ? entry.maxY : entry.maxZ;
|
|
106
|
+
const from = origin[axis];
|
|
107
|
+
const delta = direction[axis];
|
|
108
|
+
if (delta === 0) {
|
|
109
|
+
if (from < min || from > max)
|
|
110
|
+
return false;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const a = (min - from) / delta;
|
|
114
|
+
const b = (max - from) / delta;
|
|
115
|
+
near = Math.max(near, Math.min(a, b));
|
|
116
|
+
far = Math.min(far, Math.max(a, b));
|
|
117
|
+
if (near > far)
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
98
122
|
return {
|
|
99
123
|
set(layer, solids) {
|
|
100
124
|
if (solids.length === 0) {
|
|
@@ -159,6 +183,93 @@ export function createWorldSolids(options = {}) {
|
|
|
159
183
|
out.push(entry.solid);
|
|
160
184
|
return out;
|
|
161
185
|
},
|
|
186
|
+
inRay(origin, direction, maxDistance, work) {
|
|
187
|
+
if (work !== undefined) {
|
|
188
|
+
work.cells = 0;
|
|
189
|
+
work.entries = 0;
|
|
190
|
+
work.bounds = 0;
|
|
191
|
+
}
|
|
192
|
+
if (!origin.every(Number.isFinite) || !direction.every(Number.isFinite) || !Number.isFinite(maxDistance) || maxDistance < 0) {
|
|
193
|
+
throw new RangeError("World solid rays require finite coordinates, direction and nonnegative distance");
|
|
194
|
+
}
|
|
195
|
+
const length = Math.hypot(...direction);
|
|
196
|
+
if (!Number.isFinite(length))
|
|
197
|
+
throw new RangeError("World solid ray direction length must be finite");
|
|
198
|
+
const out = [];
|
|
199
|
+
if (length === 0 || total === 0)
|
|
200
|
+
return out;
|
|
201
|
+
const normalized = [direction[0] / length, direction[1] / length, direction[2] / length];
|
|
202
|
+
stamp++;
|
|
203
|
+
const consider = (entry) => {
|
|
204
|
+
if (work !== undefined)
|
|
205
|
+
work.entries++;
|
|
206
|
+
if (entry.stamp === stamp)
|
|
207
|
+
return;
|
|
208
|
+
entry.stamp = stamp;
|
|
209
|
+
if (work !== undefined)
|
|
210
|
+
work.bounds++;
|
|
211
|
+
if (rayOverlaps(entry, origin, normalized, maxDistance))
|
|
212
|
+
out.push(entry.solid);
|
|
213
|
+
};
|
|
214
|
+
const scan = () => {
|
|
215
|
+
for (const entries of layerMap.values())
|
|
216
|
+
for (const entry of entries)
|
|
217
|
+
consider(entry);
|
|
218
|
+
};
|
|
219
|
+
const visit = (x, z) => {
|
|
220
|
+
if (work !== undefined)
|
|
221
|
+
work.cells++;
|
|
222
|
+
const bucket = grid.get(keyOf(x, z));
|
|
223
|
+
if (bucket !== undefined)
|
|
224
|
+
for (const entry of bucket)
|
|
225
|
+
consider(entry);
|
|
226
|
+
};
|
|
227
|
+
let x = cellOf(origin[0]);
|
|
228
|
+
let z = cellOf(origin[2]);
|
|
229
|
+
const stepX = Math.sign(normalized[0]);
|
|
230
|
+
const stepZ = Math.sign(normalized[2]);
|
|
231
|
+
const deltaX = stepX === 0 ? Infinity : cellSize / Math.abs(normalized[0]);
|
|
232
|
+
const deltaZ = stepZ === 0 ? Infinity : cellSize / Math.abs(normalized[2]);
|
|
233
|
+
let nextX = stepX === 0 ? Infinity : ((x + (stepX > 0 ? 1 : 0)) * cellSize - origin[0]) / normalized[0];
|
|
234
|
+
let nextZ = stepZ === 0 ? Infinity : ((z + (stepZ > 0 ? 1 : 0)) * cellSize - origin[2]) / normalized[2];
|
|
235
|
+
const estimatedCells = 3 * (Math.ceil(maxDistance * Math.abs(normalized[0]) / cellSize) + Math.ceil(maxDistance * Math.abs(normalized[2]) / cellSize) + 1);
|
|
236
|
+
if (!Number.isSafeInteger(x) || !Number.isSafeInteger(z) || estimatedCells > total) {
|
|
237
|
+
scan();
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
if (grid.size > 0) {
|
|
241
|
+
visit(x, z);
|
|
242
|
+
while (Math.min(nextX, nextZ) <= maxDistance) {
|
|
243
|
+
const crossX = nextX <= nextZ;
|
|
244
|
+
const crossZ = nextZ <= nextX;
|
|
245
|
+
if (crossX && crossZ) {
|
|
246
|
+
visit(x + stepX, z);
|
|
247
|
+
visit(x, z + stepZ);
|
|
248
|
+
}
|
|
249
|
+
const oldX = x;
|
|
250
|
+
const oldZ = z;
|
|
251
|
+
const oldNextX = nextX;
|
|
252
|
+
const oldNextZ = nextZ;
|
|
253
|
+
if (crossX) {
|
|
254
|
+
x += stepX;
|
|
255
|
+
nextX += deltaX;
|
|
256
|
+
}
|
|
257
|
+
if (crossZ) {
|
|
258
|
+
z += stepZ;
|
|
259
|
+
nextZ += deltaZ;
|
|
260
|
+
}
|
|
261
|
+
if (!Number.isSafeInteger(x) || !Number.isSafeInteger(z) ||
|
|
262
|
+
(crossX && (x === oldX || nextX <= oldNextX)) || (crossZ && (z === oldZ || nextZ <= oldNextZ))) {
|
|
263
|
+
scan();
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
visit(x, z);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const entry of oversize)
|
|
270
|
+
consider(entry);
|
|
271
|
+
return out;
|
|
272
|
+
},
|
|
162
273
|
all: () => [...layerMap.values()].flatMap((entries) => entries.map((entry) => entry.solid)),
|
|
163
274
|
count: () => total,
|
|
164
275
|
version: () => revision,
|
package/dist/world.d.ts
CHANGED
|
@@ -58,12 +58,12 @@ export { partsBounds, registerAssetGenerator, type GeneratedAsset, type Generate
|
|
|
58
58
|
export { applyRotationPolicy, headingToRotationY, normalizeDegrees, parseAssetSpace, parseRotationPolicy, resolveAnchorOffset, resolveFacingRotationY, rotatedFootprint, rotationYToHeading, snapHeading, toEngineUnits, validateAssetSpace, type AssetAnchor, type AssetSpace, type AssetSpaceIssue, type PlacementRotationPolicy, } from "./scene/assetSpace.js";
|
|
59
59
|
export { collectAuthoredTriggers, createAuthoredTriggerRuntime, getTriggerAction, listTriggerActions, pointInVolume, registerTriggerAction, type AuthoredTrigger, type AuthoredTriggerRuntime, type TriggerActionDefinition, type TriggerDispatchEvent, type TriggerEvent, type TriggerHandlers, type TriggerSourceKind, } from "./scene/authoredTriggers.js";
|
|
60
60
|
export { selectAutoTarget, type AutoTargetPolicy } from "./scene/autoTarget.js";
|
|
61
|
-
export { advanceBehaviors, behaviorControl, registerBehaviorActions, type BehaviorAction, type BehaviorActionContext, type BehaviorActionOptions, type BehaviorControl, type BehaviorInspection, type BehaviorResumePolicy, type BehaviorSnapshot, type BehaviorStatus, } from "./scene/behaviorRuntime.js";
|
|
62
|
-
export { patrol, player, talkable, wander, type BehaviorDescriptor, type DecisionGraphBehavior, } from "./scene/behaviors.js";
|
|
61
|
+
export { advanceBehaviors, behaviorControl, registerBehaviorActions, type BehaviorAction, type BehaviorActionContext, type BehaviorActionOptions, type BehaviorControl, type BehaviorInspection, type BehaviorResumePolicy, type BehaviorSnapshot, type BehaviorStatus, type PursueBehaviorSnapshot, } from "./scene/behaviorRuntime.js";
|
|
62
|
+
export { patrol, player, pursue, talkable, wander, type BehaviorDescriptor, type DecisionGraphBehavior, type PursueBehavior, } from "./scene/behaviors.js";
|
|
63
63
|
export { createBodyBind } from "./scene/bodyBind.js";
|
|
64
64
|
export { type ColliderPurpose, type EntityColliderSet, type ResolvedCollider } from "./scene/colliders.js";
|
|
65
65
|
export { type StatCatalog, type StatValue } from "./scene/entityStats.js";
|
|
66
|
-
export { entityMetaOf, groundSpeed, type EntityPosition, type SceneEntity } from "./scene/entityStore.js";
|
|
66
|
+
export { entityMetaOf, groundSpeed, type EntityPosition, type EntityStore, type SceneEntity, } from "./scene/entityStore.js";
|
|
67
67
|
export { DEFAULT_FORWARD } from "./scene/facing.js";
|
|
68
68
|
export { readNamedSockets, type ModelNode } from "./scene/modelSockets.js";
|
|
69
69
|
export { MountController, createMountController } from "./scene/mount.js";
|
|
@@ -77,7 +77,7 @@ export { createSelectionBookmarks, recallSelectionBookmark, type BookmarkRecallM
|
|
|
77
77
|
export { createSequenceDirector, type CueListener, type EmittedCue, type SequenceCue, type SequenceDirector, type SequenceDirectorOptions, type SequenceSnapshot, type SequenceState, } from "./scene/sequenceDirector.js";
|
|
78
78
|
export { type Aim } from "./scene/spatial.js";
|
|
79
79
|
export { createStationClaim, type Station } from "./scene/stationClaim.js";
|
|
80
|
-
export { VehicleSeats, createVehicleSeats } from "./scene/vehicleSeat.js";
|
|
80
|
+
export { VehicleSeats, boardVehicle, createVehicleSeats, leaveVehicle, type BoardVehicleOptions, type LeaveVehicleOptions, } from "./scene/vehicleSeat.js";
|
|
81
81
|
export { type ConcealmentSensor } from "./sensor/concealment.js";
|
|
82
82
|
export { type FreezeMonitor, type FreezeViolation } from "./sensor/freezeMonitor.js";
|
|
83
83
|
export { type FramingConfig, type FrustumProjection, type FrustumSample, type FrustumSensor, type FrustumTarget, } from "./sensor/frustumSensor.js";
|
|
@@ -114,7 +114,7 @@ export { resolveStructureBuildings, summarizeEnvironment } from "./world/environ
|
|
|
114
114
|
export { createFastTravelNetwork, type FastTravelNetwork, type FastTravelOptions, type FastTravelSnapshot, type TravelPointDef, type TravelPointView, } from "./world/fastTravel.js";
|
|
115
115
|
export { biomes, building, environment, flat, grass, ocean, pad, plots, rain, road, sky, snow, terrain, tilemap, voxel, type BiomeBand, type BuildingEnvironmentDescriptor, type EnvironmentWorldFeature, type GrassEnvironmentDescriptor, type OceanEnvironmentDescriptor, type PadEnvironmentDescriptor, type PadSize, type RainEnvironmentDescriptor, type RoadEnvironmentDescriptor, type SkyEnvironmentDescriptor, type SnowEnvironmentDescriptor, type TerrainCircleRegion, type TerrainDetailConfig, type TerrainDetailMaterialConfig, type TerrainEnvironmentDescriptor, type TerrainFlattenMask, type TerrainMaterialRegion, type TerrainPolylineRegion, type TerrainRectRegion, type TerrainRegionStyle, type WeatherEnvironmentDescriptor, type WorldFeature, type WorldGridCell, type WorldGridConfig, } from "./world/features.js";
|
|
116
116
|
export { createFogField, type FogCells, type FogField } from "./world/fog.js";
|
|
117
|
-
export { boundaryNeighbors, createFootprintGrid, footprintObstacles, hasValidAdjacency } from "./world/footprintGrid.js";
|
|
117
|
+
export { boundaryNeighbors, canMerge, createFootprintGrid, footprintObstacles, hasValidAdjacency, mergeFootprints, splitRegion, type FootprintRegion, type MergedRegion, type RegionContribution, type RegionFootprint, type RegionSplitIds, type RegionWorkBudget, } from "./world/footprintGrid.js";
|
|
118
118
|
export { type Aabb, type AvoidZone } from "./world/geometry.js";
|
|
119
119
|
export { GRASS_SCHEMA } from "./world/grassKind.js";
|
|
120
120
|
export { resolveGridInstances } from "./world/gridInstances.js";
|
package/dist/world.js
CHANGED
|
@@ -59,11 +59,11 @@ export { applyRotationPolicy, headingToRotationY, normalizeDegrees, parseAssetSp
|
|
|
59
59
|
export { collectAuthoredTriggers, createAuthoredTriggerRuntime, getTriggerAction, listTriggerActions, pointInVolume, registerTriggerAction, } from "./scene/authoredTriggers.js";
|
|
60
60
|
export { selectAutoTarget } from "./scene/autoTarget.js";
|
|
61
61
|
export { advanceBehaviors, behaviorControl, registerBehaviorActions, } from "./scene/behaviorRuntime.js";
|
|
62
|
-
export { patrol, player, talkable, wander, } from "./scene/behaviors.js";
|
|
62
|
+
export { patrol, player, pursue, talkable, wander, } from "./scene/behaviors.js";
|
|
63
63
|
export { createBodyBind } from "./scene/bodyBind.js";
|
|
64
64
|
export {} from "./scene/colliders.js";
|
|
65
65
|
export {} from "./scene/entityStats.js";
|
|
66
|
-
export { entityMetaOf, groundSpeed } from "./scene/entityStore.js";
|
|
66
|
+
export { entityMetaOf, groundSpeed, } from "./scene/entityStore.js";
|
|
67
67
|
export { DEFAULT_FORWARD } from "./scene/facing.js";
|
|
68
68
|
export { readNamedSockets } from "./scene/modelSockets.js";
|
|
69
69
|
export { MountController, createMountController } from "./scene/mount.js";
|
|
@@ -77,7 +77,7 @@ export { createSelectionBookmarks, recallSelectionBookmark, } from "./scene/sele
|
|
|
77
77
|
export { createSequenceDirector, } from "./scene/sequenceDirector.js";
|
|
78
78
|
export {} from "./scene/spatial.js";
|
|
79
79
|
export { createStationClaim } from "./scene/stationClaim.js";
|
|
80
|
-
export { VehicleSeats, createVehicleSeats } from "./scene/vehicleSeat.js";
|
|
80
|
+
export { VehicleSeats, boardVehicle, createVehicleSeats, leaveVehicle, } from "./scene/vehicleSeat.js";
|
|
81
81
|
export {} from "./sensor/concealment.js";
|
|
82
82
|
export {} from "./sensor/freezeMonitor.js";
|
|
83
83
|
export {} from "./sensor/frustumSensor.js";
|
|
@@ -114,7 +114,7 @@ export { resolveStructureBuildings, summarizeEnvironment } from "./world/environ
|
|
|
114
114
|
export { createFastTravelNetwork, } from "./world/fastTravel.js";
|
|
115
115
|
export { biomes, building, environment, flat, grass, ocean, pad, plots, rain, road, sky, snow, terrain, tilemap, voxel, } from "./world/features.js";
|
|
116
116
|
export { createFogField } from "./world/fog.js";
|
|
117
|
-
export { boundaryNeighbors, createFootprintGrid, footprintObstacles, hasValidAdjacency } from "./world/footprintGrid.js";
|
|
117
|
+
export { boundaryNeighbors, canMerge, createFootprintGrid, footprintObstacles, hasValidAdjacency, mergeFootprints, splitRegion, } from "./world/footprintGrid.js";
|
|
118
118
|
export {} from "./world/geometry.js";
|
|
119
119
|
export { GRASS_SCHEMA } from "./world/grassKind.js";
|
|
120
120
|
export { resolveGridInstances } from "./world/gridInstances.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "jgengine core — pure-TypeScript game framework SDK (npm @jgengine/core). Entity stores, commands, combat, inventory, multiplayer seams. Zero dependencies. Not an ECS. Agent skills ship in the tarball. https://jgengine.com",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jgengine",
|
|
@@ -25,7 +25,7 @@ Create one immutable handoff at the destination the user requested, or use the m
|
|
|
25
25
|
### Build the handoff
|
|
26
26
|
|
|
27
27
|
1. Distill the current objective and the user's latest intent. If a focus was supplied, make it the `resume_focus`.
|
|
28
|
-
2. Inspect only the workspace state needed to explain what exists now. Use JGengine project instructions already in context (`AGENTS.md
|
|
28
|
+
2. Inspect only the workspace state needed to explain what exists now. Use JGengine project instructions already in context (`AGENTS.md`, `workflow`, domain skills).
|
|
29
29
|
3. Point to plans, issues, commits, diffs, documentation, and relevant files instead of reproducing their contents. Prefer repository-relative paths (`docs/plans/…`, `packages/…`, `Games/…`).
|
|
30
30
|
4. Redact secrets, credentials, tokens, and unrelated personal information. Preserve operational paths only when the next agent needs them.
|
|
31
31
|
5. Write or publish with existing tools. If the user named another path, folder, format, or publication destination, honor it. Do not also create a managed-store copy unless asked.
|
|
@@ -11,6 +11,8 @@ Own the intended player experience and the rules that produce it: fantasy, pilla
|
|
|
11
11
|
|
|
12
12
|
Use `level-design` for spatial realization; route implementation to `jgengine-gameplay`, `jgengine-world`, `jgengine-combat`, `jgengine-ui`, `jgengine-multiplayer` once intent is coherent.
|
|
13
13
|
|
|
14
|
+
For a substantial existing-game quality pass spanning concept, depth, world and presentation, use `game-improvement` to sequence these owners and keep a per-game implementation plan and acceptance evidence.
|
|
15
|
+
|
|
14
16
|
For a greenfield build, read [references/application-playbook.md](references/application-playbook.md) and produce its greenfield contract (promise, pillars, anti-pillars, core decision, and falsifier) before writing code. Audits may read only the playbook's audit sections. Read [references/field-guide.md](references/field-guide.md) when the task needs deeper loop, economy, progression, genre, accessibility, or research guidance.
|
|
15
17
|
|
|
16
18
|
## Choose the mode
|
package/skills/jgengine/SKILL.md
CHANGED
|
@@ -9,6 +9,22 @@ This skill decides what to load; it is not an engine manual. Package map, comman
|
|
|
9
9
|
|
|
10
10
|
A created project installs only `jgengine`, `jgengine-editor`, `jgengine-verify`, `game-design`, and `jgengine-ui`. Run `npx jgengine skills -p --all` before loading any other domain named below. Those projects do not ship `api.md`; read exact signatures from the `.d.ts` files under `node_modules/@jgengine/<pkg>/dist/`.
|
|
11
11
|
|
|
12
|
+
Created projects pin their scaffolding CLI as a local devDependency, separately from SDK versions. Run `npx jgengine doctor` after SDK upgrades to check CLI compatibility. Skill installation preserves newer or differing unversioned copies; update the project CLI first, and use `skills -p --force` only when replacement is intended. Each packaged skill install records its CLI and SDK versions in `.jgengine-version.json`.
|
|
13
|
+
|
|
14
|
+
## Framework-first decisions
|
|
15
|
+
|
|
16
|
+
For every requested interaction, feature, fix or visual change, follow the boundary in
|
|
17
|
+
[AGENTS.md](../../../AGENTS.md): discover the installed
|
|
18
|
+
capability, adopt it, or improve its shared owner before adding reusable game-local code.
|
|
19
|
+
Games keep unique rules, content, assets and presentation. Custom markup is not a reason
|
|
20
|
+
to copy focus handling, input tracking, storage or model lifecycle.
|
|
21
|
+
|
|
22
|
+
Check installed versions before recreating an upstream fix. A published upgrade may
|
|
23
|
+
replace a workaround; a beta merge does not update consumers. In the games workspace,
|
|
24
|
+
the root catalog owns SDK versions. After adoption, remove superseded local machinery
|
|
25
|
+
and verify the actual game. Outside these repositories, report missing SDK contracts
|
|
26
|
+
upstream rather than treating a local engine implementation as the finished solution.
|
|
27
|
+
|
|
12
28
|
## 1. Establish the target
|
|
13
29
|
|
|
14
30
|
For greenfield builds, write `src/art-direction.md` before any code.
|
|
@@ -26,6 +42,10 @@ Pick the dimension first: `create` defaults to a 3D world (terrain, sky, player
|
|
|
26
42
|
|
|
27
43
|
For an existing game asked to pick up a new engine release ("jgengine published a new version, grab it"), run `npx jgengine upgrade` in the project first: it diffs installed `@jgengine/*` versions against the latest release and prints every Migrate step and Adopt-worthy addition in between (`--json` for structured output). Bump, migrate oldest-first, then work the Adopt lists instead of reconstructing the changelog by hand.
|
|
28
44
|
|
|
45
|
+
`doctor` and `upgrade` resolve Bun `catalog:` and `catalog:<name>` declarations from the nearest workspace's `workspaces.catalog` or `workspaces.catalogs`, and follow hoisted SDK installs. Missing or malformed catalog entries fail diagnostics. `upgrade` is read-only by default. For catalog-owned versions, run `upgrade --plan --to <published-version>` at the workspace root or in a game, review the whole root catalog plan and migrations, then run `upgrade --apply --to <published-version>`. Apply preserves per-game `catalog:` declarations, unrelated text, CLI/github versions, and a held editor override; it verifies published SDK targets and refuses files changed while planning. Apply changes declarations only: run `bun install` at the root, then `jgengine doctor --workspace --json` once there to check installed SDK identities across declared workspace owners. Rebuild, migrate, and verify game behavior afterward. `--json` includes exact before/after text; an apply prints its plan before writing and reports write status on stderr. A report without installed packages labels its resolved pins as a declared baseline.
|
|
46
|
+
|
|
47
|
+
When a linked consumer renders differently despite matching root SDK versions, `doctor` checks direct consumers and SDK dependency/peer paths, including private copies with the same version. Review the reported importer and real paths, align the shared catalog and linked-consumer declarations, then reinstall at the root and rerun `doctor --workspace`; a root install may leave private linked-package installs intact. Restart running games after reinstall. Workspace mode checks installed identities only, reports coverage, and refuses unsupported patterns, escaped symlinks or exhausted traversal budgets. The default project audit stays scoped; neither mode covers custom bundler aliases or indirect non-SDK chains.
|
|
48
|
+
|
|
29
49
|
## 2. Select domains
|
|
30
50
|
|
|
31
51
|
Load only rows the target needs. Do not preload every domain.
|
|
@@ -33,6 +53,7 @@ Load only rows the target needs. Do not preload every domain.
|
|
|
33
53
|
| Need | Skill | Owner |
|
|
34
54
|
| --- | --- | --- |
|
|
35
55
|
| player promise, pillars, verbs, loops, economy, progression, balance | `game-design` | experience and rule intent |
|
|
56
|
+
| substantial existing-game improvement across depth, world and presentation | `game-improvement` | ordered improvement plan and per-game completion |
|
|
36
57
|
| metrics, topology, encounters, navigation, spatial pacing | `level-design` | playable space intent |
|
|
37
58
|
| scene placement, terrain painting, paths, asset authoring | `jgengine-editor` | editor document and authoring tools |
|
|
38
59
|
| runtime world, movement, input, interaction, AI, navigation, audio | `jgengine-world` | spatial simulation and world runtime |
|
|
@@ -49,12 +70,12 @@ World content routes editor-first: any request that adds, moves, restyles, or re
|
|
|
49
70
|
Within each selected domain:
|
|
50
71
|
|
|
51
72
|
0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code) (`combat-loop`, `loot`, `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear`, `enter-vehicle` and more); `npx jgengine recipe` lists them. Copy that instead of reconning per-file.
|
|
52
|
-
1. Search capabilities by intent; do not read `capabilities.md` top to bottom. `npx jgengine find <intent>` searches every domain's index and every recipe at once, installed or not, and prints the primitive and its import (or the recipe to open);
|
|
73
|
+
1. Search capabilities by intent; do not read `capabilities.md` top to bottom. `npx jgengine find <intent>` searches every domain's index and every recipe at once, installed or not, and prints the primitive and its import (or the recipe to open); agents can use `--json --limit 5` for ranked imports and recipes with total, partial-match, and truncation fields. In the monorepo, `rg -i "<intent>" .claude/skills/*/capabilities.md` does the same search. Search the moment you are tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. A `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob almost certainly has a shipped drop-in (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
|
|
53
74
|
2. Open `api.md` (monorepo) or the package `.d.ts` only when exact signatures are needed; grep it, never read it whole.
|
|
54
75
|
3. Open the linked reference only for a deeper recipe or trap.
|
|
55
76
|
4. For how several primitives wire into a running loop, read the domain's `recipes/` — connected, genre-free walkthroughs organized by composition seam; this skill's own [recipes/minimal-game.md](recipes/minimal-game.md) is the default whole-game walkthrough. Prefer a recipe over reading a game's source; the games under `Games/*` are not templates or references, and a hybrid game is just a different composition of the same primitives.
|
|
56
77
|
|
|
57
|
-
If no capability fits, identify the upstream package seam before writing game-local code. In this repository, implement a reusable primitive with its first adopter;
|
|
78
|
+
If no capability fits, identify the upstream package seam before writing game-local code. In this repository, implement a reusable primitive with its first adopter and a reviewed consumer cleanup; coordinate a differently composed second consumer when applicable. Recipes wire SDK APIs, not copied implementations. If editor-owned content cannot be expressed, file the editor gap first; track any fallback and its removal path.
|
|
58
79
|
|
|
59
80
|
## 4. Build and finish
|
|
60
81
|
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.22.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
|
|
233
233
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
234
234
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
235
|
-
- `VERSION` (const): const VERSION: "0.
|
|
235
|
+
- `VERSION` (const): const VERSION: "0.22.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
236
236
|
|
|
237
237
|
## @jgengine/core/runtime/adapter
|
|
238
238
|
|
|
@@ -260,10 +260,20 @@
|
|
|
260
260
|
## @jgengine/core/runtime/cameraDirector
|
|
261
261
|
|
|
262
262
|
- `CHASE_VIEWS` (const): const CHASE_VIEWS: readonly ChaseView[] — Default order {@link nextChaseView} cycles through.
|
|
263
|
-
- `CameraDirector` (interface): interface CameraDirector { follow(entityId: string | null): void; followedEntityId(): string | null | undefined;
|
|
264
|
-
- `
|
|
263
|
+
- `CameraDirector` (interface): interface CameraDirector { follow(entityId: string | null): void; followedEntityId(): string | null | undefined; setRig(kind: CameraRigKind | null, config?: CameraRigConfig): void; rig(): CameraRigOverride | null; setCinematic(config: CinematicCameraConfig | null):… — ⚠ undocumented
|
|
264
|
+
- `CameraDirectorSnapshot` (type): type CameraDirectorSnapshot = CameraSerialized<CameraDirectorState> — JSON-safe camera policy; documented instant chase responses use an explicit Infinity marker.
|
|
265
|
+
- `CameraDirectorState` (interface): interface CameraDirectorState — Camera policy state before the JSON-safe snapshot number encoding.
|
|
266
|
+
- `CameraRigConfig` (type): type CameraRigConfig = Omit<GameCameraConfig, "rig" | "projection" | "pixelPerfect" | "frustum" | "playerFov" | "weapon" | "onCameraFollow"> — Data overlay for an existing rig; callbacks and Canvas projection/preferences remain configured by the game.
|
|
267
|
+
- `CameraRigOverride` (interface): interface CameraRigOverride — Runtime rig selection and detached data overlay over the configured camera.
|
|
268
|
+
- `ChaseCameraTuning` (type): type ChaseCameraTuning = Partial< Pick< ChaseCameraConfig, | "distance" | "height" | "lookHeight" | "springDamping" | "fov" | "lead" | "bank" | "shakePerSpeed" | "velocityYaw" | "yawResponse" | "headingSource" | "distanceBySpeed" | "pitchFollow" | "fovKick" | "lookBackAction" | "collision" | "view" … — Runtime patch over the static `camera.chase` config — distance/height/fov retuning from gameplay events (#286.11), a whole driving-feel overlay applied only while a vehicle is piloted (#1299), or a `view` switch between chase and seat cameras.
|
|
265
269
|
- `nextChaseView` (function): function nextChaseView(current: ChaseView, views: readonly ChaseView[] = CHASE_VIEWS): ChaseView — The view after `current` in `views`, wrapping at the end; a view missing from the list starts the cycle over. Pair with `setChaseTuning` to bind a "change camera" key: `ctx.camera.setChaseTuning({ ...ctx.camera.chaseTuning(), view: nextChaseView(view) })`.
|
|
266
270
|
|
|
271
|
+
## @jgengine/core/runtime/commandDispatch
|
|
272
|
+
|
|
273
|
+
- `DispatchedCommandResult` (type): type DispatchedCommandResult = CommandResult<GameContext> | Promise<CommandResult<GameContext>> — A UI command completes locally or after its authoritative host acknowledges it.
|
|
274
|
+
- `bindCommandTransport` (function): function bindCommandTransport(ctx: GameContext, route: Route): () => void — Bind UI command dispatch to the host for this context. A missing join rejects; it never mutates the local replica.
|
|
275
|
+
- `dispatchCommand` (function): function dispatchCommand(ctx: GameContext, name: string, input: unknown): DispatchedCommandResult — Dispatch a player's command through the bound host, or locally for an unbound offline context. Await the result before showing success.
|
|
276
|
+
|
|
267
277
|
## @jgengine/core/runtime/commandInput
|
|
268
278
|
|
|
269
279
|
- `QuantityResult` (type): type QuantityResult = { ok: true; quantity: number } | { ok: false; reason: "invalid-quantity" } — Validated integer quantity or a stable input rejection.
|
|
@@ -318,7 +328,7 @@
|
|
|
318
328
|
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
319
329
|
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
320
330
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
321
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean;
|
|
331
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setVelocity: EntityStore["setVelocity"]; setPoseConstraint: … — ⚠ undocumented
|
|
322
332
|
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
323
333
|
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
324
334
|
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
@@ -349,7 +359,7 @@
|
|
|
349
359
|
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
350
360
|
- `HitReactionInput` (interface): interface HitReactionInput { from: string; to: string; config: HitReactionConfig | ImpactPresetName; power?: number } — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
351
361
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
352
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean;
|
|
362
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setVelocity: EntityStore["setVelocity"]; setPoseConstraint: … — ⚠ undocumented
|
|
353
363
|
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
354
364
|
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
355
365
|
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
@@ -420,7 +430,7 @@
|
|
|
420
430
|
- `HostedGameRunner` (interface): interface HostedGameRunner — The GameContext-loop equivalent of the pure-reducer `createGameHost`: one authoritative `createGameContext` per world, driven server-side. `onInit` runs once at construction; `onNewPlayer`/`onPlayerLeave` fire per join/leave; `tick` advances `onTick` then commits a revision. Clients pull a full {@link WorldSnapshot} baseline once, then per-tick {@link WorldDiff}s from their last revision. Games ship only normal GameContext code — the runner adds no per-game surface.
|
|
421
431
|
- `HostedGameRunnerOptions` (interface): interface HostedGameRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedGameRunner}: the game definition, its content lookup, and an optional host identity.
|
|
422
432
|
- `INPUT_COMMAND` (const): const INPUT_COMMAND: "engine.input" — Reserved command name the authoritative host intercepts on the existing `runCommand` transport to route a client's {@link InputFrame} to `session.input`, so per-tick input needs no separate wire.
|
|
423
|
-
- `InputFrame` (interface): interface InputFrame — One client's
|
|
433
|
+
- `InputFrame` (interface): interface InputFrame — One client's serializable held state and discrete edges for a simulation tick.
|
|
424
434
|
|
|
425
435
|
## @jgengine/core/runtime/hostedWorldSession
|
|
426
436
|
|
|
@@ -429,7 +439,7 @@
|
|
|
429
439
|
- `HostedWorldSessionOptions` (interface): interface HostedWorldSessionOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedWorldSession}: the game, its persistence store, and the auto-save cadence.
|
|
430
440
|
- `HostedWorldStore` (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
431
441
|
- `HostedWorldSync` (type): type HostedWorldSync = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; diff: WorldDiff } — A client replication pull: a full baseline (first sync / fell behind) or a diff since the client's cursor.
|
|
432
|
-
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore —
|
|
442
|
+
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore — Already-loaded store adapter: synchronous reads, with synchronous or asynchronous writes.
|
|
433
443
|
- `asyncMemoryWorldStore` (function): function asyncMemoryWorldStore(seed?: HostedWorldRecord): HostedWorldStore — Async in-process store for callers exercising the production persistence contract.
|
|
434
444
|
- `createHostedWorldSessionAsync` (function): function createHostedWorldSessionAsync<TAssetRef extends ModelAssetRef, TMultiplayer>(options: Omit<HostedWorldSessionOptions<TAssetRef, TMultiplayer>, "store"> & { store: HostedWorldStore }): Promise<HostedWorldSession> — Build a hosted session from an asynchronous persistence backend.
|
|
435
445
|
|
|
@@ -437,7 +447,7 @@
|
|
|
437
447
|
|
|
438
448
|
- `HostedWorldRecord` (interface): interface HostedWorldRecord — One hosted world's persisted authoritative state — the unit a {@link HostedWorldStore} loads and saves.
|
|
439
449
|
- `HostedWorldStore` (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
440
|
-
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore —
|
|
450
|
+
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore — Already-loaded store adapter: synchronous reads, with synchronous or asynchronous writes.
|
|
441
451
|
|
|
442
452
|
## @jgengine/core/runtime/inputRecorder
|
|
443
453
|
|
|
@@ -448,8 +458,9 @@
|
|
|
448
458
|
|
|
449
459
|
## @jgengine/core/runtime/inputSnapshot
|
|
450
460
|
|
|
451
|
-
- `InputFrame` (interface): interface InputFrame — One client's
|
|
452
|
-
- `
|
|
461
|
+
- `InputFrame` (interface): interface InputFrame — One client's serializable held state and discrete edges for a simulation tick.
|
|
462
|
+
- `InputPress` (interface): interface InputPress — One discrete press, identified across retransmitted live input frames.
|
|
463
|
+
- `InputSnapshot` (interface): interface InputSnapshot { rumble(userId: string, options: {strong: number; weak: number; ms: number}): Promise<boolean>; haptics(userId: string): HapticChannels; publish(held: readonly string[], edges?: {pressed?: readonly string[]; reset?: boolean}): void; beginS… — ⚠ undocumented · used by `GamepadSource` (@jgengine/shell/input/gamepadSource): Poll browser gamepads and feed semantic actions into the shell tracker.
|
|
453
464
|
- `createInputSnapshot` (function): function createInputSnapshot(): InputSnapshot — ⚠ undocumented
|
|
454
465
|
|
|
455
466
|
## @jgengine/core/runtime/localPlayers
|
|
@@ -509,11 +520,11 @@
|
|
|
509
520
|
|
|
510
521
|
## @jgengine/core/runtime/runtimeSave
|
|
511
522
|
|
|
512
|
-
- `RuntimeSave` (interface): interface RuntimeSave — Whole-world save/load bound to a live world and a pluggable backend. `save()` captures `target.
|
|
523
|
+
- `RuntimeSave` (interface): interface RuntimeSave — Whole-world save/load bound to a live world and a pluggable backend. `save()` captures `target.state()` and writes it; `load()` reads it back and calls `target.restore()`. Older targets use `snapshot()`/`hydrate()`. In `autosave` mode it also writes on a trailing timer while the world keeps changing. Named slots, versioned migration, and offline↔cloud (backend swap) all come for free from the underlying save store.
|
|
513
524
|
- `RuntimeSaveConfig` (interface): interface RuntimeSaveConfig — How {@link createRuntimeSave} is wired — the live world `target`, the `backend` it persists through, and optional mode/slot/versioning/cadence knobs.
|
|
514
525
|
- `RuntimeSaveMode` (type): type RuntimeSaveMode = "autosave" | "manual" — `"autosave"` writes a fresh capture on a trailing timer while the world changes (at most once per `autosaveMs`, so a never-idle world still persists); `"manual"` writes only on an explicit `save()`/`checkpoint()` — the save-point / quest-trigger model.
|
|
515
526
|
- `RuntimeSaveOptions` (type): type RuntimeSaveOptions = Omit<RuntimeSaveConfig, "target"> — {@link RuntimeSaveConfig} without `target` — what a host (`createGameContext`) accepts to build `ctx.game.save` and bind it to the context itself.
|
|
516
|
-
- `RuntimeSaveTarget` (interface): interface RuntimeSaveTarget — The narrow slice of a live `GameContext` a runtime save reads and writes — a `GameContext` satisfies it directly (`ctx.
|
|
527
|
+
- `RuntimeSaveTarget` (interface): interface RuntimeSaveTarget — The narrow slice of a live `GameContext` a runtime save reads and writes — a `GameContext` satisfies it directly (`ctx.state`/`ctx.restore`/`ctx.subscribe`). Depending on this instead of the full context keeps the save controller a deep, decoupled module: it captures and restores the *whole* opted-in world without knowing any subsystem.
|
|
517
528
|
- `createRuntimeSave` (function): function createRuntimeSave(config: RuntimeSaveConfig): RuntimeSave — Bridge a live world to a pluggable save backend: whole-state capture/restore with autosave, save points, named slots, and versioned migration — the same call for offline (localStorage) and cloud (Convex), only the `backend` differs. The game keeps control of *when* it saves (the `mode` plus explicit `checkpoint()` calls from quests/areas) and *when* it restores (`load()` on boot), so any save mechanic — continuous autosave, manual save points, checkpoint triggers — is expressible without new engine code.
|
|
518
529
|
|
|
519
530
|
## @jgengine/core/runtime/save
|
|
@@ -527,7 +538,7 @@
|
|
|
527
538
|
- `SimContextState` (interface): interface SimContextState — Serializable `ctx.sim` state: loop position plus the interpolation pose history.
|
|
528
539
|
- `SimStage` (interface): interface SimStage — Extra work a game or engine seam (a physics backend, a netcode buffer) runs inside every simulation step.
|
|
529
540
|
- `SimStagePhase` (type): type SimStagePhase = "beforeMovement" | "afterMovement" | "afterTick" — Where a registered stage runs inside one simulation step.
|
|
530
|
-
- `createSimContext` (function): function createSimContext(options: { config?: SimulationConfig; entities: EntityStore }): SimContext — Build `ctx.sim` for a context; `createGameContext` calls this from `definition.simulation`.
|
|
541
|
+
- `createSimContext` (function): function createSimContext(options: { config?: SimulationConfig; entities: EntityStore; time?: Pick<SimClock, "advance"> }): SimContext — Build `ctx.sim` for a context; `createGameContext` calls this from `definition.simulation`.
|
|
531
542
|
|
|
532
543
|
## @jgengine/core/runtime/simLoop
|
|
533
544
|
|
|
@@ -640,7 +651,7 @@
|
|
|
640
651
|
## @jgengine/shell/GameHost
|
|
641
652
|
|
|
642
653
|
- `EditorSummonModule` (interface): interface EditorSummonModule — Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it.
|
|
643
|
-
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
654
|
+
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor, creator, creatorOpen = false, onCreatorOpenChange }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
644
655
|
- `GameHostProps` (interface): interface GameHostProps — Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon.
|
|
645
656
|
|
|
646
657
|
## @jgengine/shell/defineGame
|
|
@@ -659,7 +670,7 @@
|
|
|
659
670
|
- `GameCameraConfig` (interface): interface GameCameraConfig — Camera tuning for the shell's rig stack: pick the rig via `rig`, then tune it through its matching config block. All fields optional — the default is the third-person orbit rig.
|
|
660
671
|
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
661
672
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
662
|
-
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
673
|
+
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor, creator, creatorOpen = false, onCreatorOpenChange }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
663
674
|
- `GameHostProps` (interface): interface GameHostProps — Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon.
|
|
664
675
|
- `GameLoop` (interface): interface GameLoop<TContext = unknown> — Lifecycle hooks a game implements to drive init, per-tick simulation, and player join/leave.
|
|
665
676
|
- `Hotbar` (function): function Hotbar({ inventoryId, activeSlot, keys, slotSize, itemIcon, style, className, }: { inventoryId: string; activeSlot?: number; keys?: readonly string[]; slotSize?: number; /** Caller-supplied item id → icon registry; return null/undefined to fall back to the default glyph. */ itemIcon?: (item… — A numbered hotbar bound to an inventory — painted iconed slots (a `GameIcon` glyph over a school-keyed gradient with a count badge, #1035), an active-slot highlight, and a keycap per slot. Place it and pass the `inventoryId`; `activeSlot` highlights the equipped one. Supply `itemIcon` to map your item ids to your own glyphs — the default resolves a `GameIcon` from the item id.
|
|
@@ -673,7 +684,7 @@
|
|
|
673
684
|
- `collectAuthoredTriggers` (function): function collectAuthoredTriggers(document: SceneDocumentLike): AuthoredTrigger[] — Collect every authored trigger on a document's markers and volumes. Pure — no runtime state. Action params use the live {@link registerTriggerAction} registry when present.
|
|
674
685
|
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef>(config: GameConfig<TAssetRef>): PlayableGame — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
675
686
|
- `defineKeyedStore` (function): function defineKeyedStore<T>(keyFor: (id: string) => string, initial: T | (() => T)): KeyedStoreHandle<T> — Define a typed per-owner keyed family on the game store: game code expresses `read(ctx, userId)`/`write(ctx, userId, v)`/`update(...)` with the value type it means, and both the `` `prefix:${id}` `` key composition and the `unknown → T` cast live once, here, behind the boundary. Reach for this over N separate `defineStore` calls whenever the owning id varies at runtime (per-user class, per-instance auras) — `defineStore` stays the right call for a single fixed slot.
|
|
676
|
-
- `defineStore` (function): function defineStore<T>(key: string, initial: T | (() => T)): StoreHandle<T> — Define a typed slot on the game store: game code expresses `read(ctx)`/`write(ctx, v)`/`update(...)` with the value type it means, and the `unknown → T` cast lives once, here, behind the boundary. Pass a factory for `initial` when the fallback is a fresh mutable object; the factory runs at most once and its result is reused, so an unwritten slot keeps a stable identity across reads (no per-read churn for a React selector, no allocation on a hot path).
|
|
687
|
+
- `defineStore` (function): function defineStore<T>(key: string, initial: T | (() => T)): StoreHandle<T> — Define a typed slot on the game store: game code expresses `read(ctx)`/`write(ctx, v)`/`update(...)` with the value type it means, and the `unknown → T` cast lives once, here, behind the boundary. Pass a factory for `initial` when the fallback is a fresh mutable object; the factory runs at most once and its result is reused, so an unwritten slot keeps a stable identity across reads (no per-read churn for a React selector, no allocation on a hot path). A value that carries functions (a closure-based run store, an input handle) is runtime-only: snapshots, saves and replication skip it and hydration keeps it in place. Hold such objects with `perContext` when they need no store notifications.
|
|
677
688
|
- `defineSystem` (function): function defineSystem(definition: SystemDefinition): SystemDefinition — Declare a composable game system. Pure data + hooks — the engine compiles the schedule and installs lifecycle when the game boots.
|
|
678
689
|
- `environment` (function): function environment(config?: EnvironmentWorldConfig): EnvironmentWorldFeature — Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
679
690
|
- `environmentContentFromDocument` (function): function environmentContentFromDocument(doc: EditorDocument, options?: EnvironmentContentOptions): EnvironmentContent — Derives the coordinate/placement content of an `environment()` world from its scene document: terrain footprint (via {@link terrainBoundsFromDocument}), ground clearings under authored spawns/POIs (via `clearanceZonesFrom`), and the document's sculpt snapshot. This is the seam that lets a game author its world footprint and flatten regions in the editor instead of hardcoding them — the `world.ts` that consumes it carries only engine tuning.
|
|
@@ -6,6 +6,11 @@ authored scene edited in the browser, one system, one store slot, an empty-canva
|
|
|
6
6
|
and a win condition. Every new game starts here; add combat, multiplayer, or deeper
|
|
7
7
|
world features only after this loop plays.
|
|
8
8
|
|
|
9
|
+
Every extension follows the [framework-first decision](../SKILL.md#framework-first-decisions):
|
|
10
|
+
compose an installed SDK capability, improve its upstream contract if needed, then
|
|
11
|
+
remove superseded game glue after published adoption. The game owns its rules and art;
|
|
12
|
+
it should not recreate input, model lifecycle, storage or UI accessibility machinery.
|
|
13
|
+
|
|
9
14
|
## 1. Scaffold
|
|
10
15
|
|
|
11
16
|
`npx jgengine create <name>` (inside the engine monorepo: `bun run new:game "<id-or-title>"`).
|
|
@@ -80,8 +85,8 @@ direction. Compose the shipped building blocks the moment you need them (`StatBa
|
|
|
80
85
|
`Coins`, `InventoryGrid`, `CharacterSheet`, window shells from `jgengine-ui`), then reskin via
|
|
81
86
|
`HudTheme` tokens. Reaching for those is using the engine correctly, not incomplete work. What
|
|
82
87
|
the engine does not ship is a whole finished game *face* or genre theme preset — do not leave an
|
|
83
|
-
unarranged, unskinned generic dashboard as the product.
|
|
84
|
-
|
|
88
|
+
unarranged, unskinned generic dashboard as the product. Custom markup can use headless
|
|
89
|
+
SDK behavior; a missing reusable behavior is an upstream gap (see `jgengine-ui`).
|
|
85
90
|
|
|
86
91
|
## 3. Author, play, win
|
|
87
92
|
|
|
@@ -19,6 +19,9 @@ a parallel entity store. To bolt firing onto an existing game, compose
|
|
|
19
19
|
`resolveDamageHit`) per the
|
|
20
20
|
[portable weapon-plumbing recipe](recipes/portable-weapon-plumbing.md); never add
|
|
21
21
|
a default gun or FPS kit.
|
|
22
|
+
For scene-backed `createProjectileSystem`, inject authoritative `rng`; spread
|
|
23
|
+
samples independent pellet rays and prediction stays on the center ray. The
|
|
24
|
+
weapon-plumbing recipe covers the bounded cone and cover contracts.
|
|
22
25
|
Tune how a weapon feels (recoil, bloom, ADS, shake and rumble) with the
|
|
23
26
|
[weapon-feel recipe](recipes/weapon-feel.md): feel target → `measureWeapon`
|
|
24
27
|
numbers → handling knobs, and `createWeaponFeedbackSignals` into the feedback mixer.
|
|
@@ -28,8 +31,11 @@ receiver, interceptor, and status stages remain pure. Follow the
|
|
|
28
31
|
[portable damage/effects recipe](recipes/portable-damage-effects.md) to compose
|
|
29
32
|
them over an existing store, clock, RNG, spatial index, and death flow.
|
|
30
33
|
|
|
31
|
-
Closure-backed magazines
|
|
32
|
-
|
|
34
|
+
Closure-backed magazines expose snapshot/restore and `retune` for capacity/reload
|
|
35
|
+
changes without reconstructing ammo state; capacity overflow is explicit.
|
|
36
|
+
Stat modifiers expose snapshot/restore; ability
|
|
37
|
+
kits, shields, and meters expose state/restore for caller-owned saves,
|
|
38
|
+
rollback, replay, and workers. Follow the
|
|
33
39
|
[portable runtime-state recipe](recipes/portable-runtime-state.md); the caller
|
|
34
40
|
keeps its clock, reserve state, schema, and authority.
|
|
35
41
|
|