@jgengine/core 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/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.js +1 -1
- package/dist/combat.d.ts +1 -0
- package/dist/combat.js +1 -0
- package/dist/devtools/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -0
- package/dist/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -0
- package/dist/economy/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -0
- package/dist/economy/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +48 -3
- package/dist/editor/commands.js +40 -754
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +37 -2
- package/dist/editor/document.js +422 -6
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -0
- package/dist/editor/index.d.ts +26 -1
- package/dist/editor/index.js +8 -1
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +143 -0
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -0
- package/dist/game/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/defineGame.d.ts +16 -9
- package/dist/game/defineGame.js +14 -6
- package/dist/game/defineSystem.d.ts +2 -0
- package/dist/game/defineSystem.js +2 -0
- package/dist/game/events.d.ts +28 -0
- package/dist/game/playableGame.d.ts +34 -435
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +19 -0
- package/dist/game/quest.js +1 -3
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -0
- package/dist/gameplay.d.ts +3 -1
- package/dist/gameplay.js +3 -1
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -0
- package/dist/item/durability.js +1 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +24 -1
- package/dist/movement/movementModel.d.ts +15 -1
- package/dist/movement/movementModel.js +33 -15
- package/dist/movement/playerMovement.js +102 -2
- package/dist/multiplayer/combatSnapshot.js +13 -8
- package/dist/multiplayer/presenceModel.d.ts +2 -0
- package/dist/multiplayer/presenceModel.js +8 -0
- package/dist/physics/drivableVehicle.d.ts +2 -0
- package/dist/physics/drivableVehicle.js +1 -1
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +87 -0
- package/dist/physics/kinematicVehicle.js +193 -11
- package/dist/physics/physicsWorld.d.ts +26 -0
- package/dist/physics/physicsWorld.js +109 -117
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -0
- package/dist/random/rng.d.ts +4 -1
- package/dist/random/rng.js +4 -1
- package/dist/runtime/adapter.d.ts +13 -0
- package/dist/runtime/adapter.js +13 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +130 -0
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/scene.d.ts +59 -0
- package/dist/runtime/context/scene.js +291 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -0
- package/dist/runtime/descriptors/baseline.js +26 -3
- package/dist/runtime/descriptors/features.d.ts +5 -0
- package/dist/runtime/descriptors/features.js +20 -2
- package/dist/runtime/gameContext.d.ts +2 -428
- package/dist/runtime/gameContext.js +88 -501
- package/dist/runtime/gameContextTypes.d.ts +467 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +4 -2
- package/dist/runtime/headlessRunner.js +1 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +3 -1
- package/dist/runtime/hostedGameRunner.js +1 -0
- package/dist/runtime/hostedWorldSession.d.ts +3 -1
- package/dist/runtime/hostedWorldSession.js +1 -0
- package/dist/runtime/worldSnapshot.d.ts +13 -1
- package/dist/runtime/worldSnapshot.js +15 -3
- package/dist/scene/assetCatalog.d.ts +5 -0
- package/dist/scene/assetSpace.js +1 -3
- package/dist/scene/authoredTriggers.d.ts +50 -0
- package/dist/scene/authoredTriggers.js +114 -0
- package/dist/scene/builtinSceneKinds.js +8 -3
- package/dist/scene/colliders.d.ts +58 -0
- package/dist/scene/colliders.js +108 -0
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +7 -9
- package/dist/scene/entityStats.js +10 -14
- package/dist/scene/sceneKinds.d.ts +16 -0
- package/dist/scene/sceneKinds.js +4 -0
- package/dist/scene/sceneRaycast.js +15 -0
- package/dist/stats/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +59 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -0
- package/dist/store/observableKeyedStore.d.ts +2 -0
- package/dist/store/observableKeyedStore.js +1 -0
- package/dist/survival/decayMeter.js +1 -3
- package/dist/survival/regionHealth.js +1 -3
- package/dist/ui/hudLayout.d.ts +5 -0
- package/dist/ui/hudLayout.js +17 -1
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +15 -1
- package/dist/world/authoredObjects.js +13 -1
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -0
- package/dist/world/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/features.d.ts +18 -3
- package/dist/world/features.js +10 -1
- package/dist/world/grassKind.js +2 -2
- package/dist/world/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/pathNetwork.d.ts +159 -0
- package/dist/world/pathNetwork.js +684 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.js +3 -1
- package/dist/world/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.js +9 -2
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/scatter.js +2 -20
- package/dist/world/streets.d.ts +29 -0
- package/dist/world/streets.js +55 -0
- package/dist/world/terraform.js +58 -88
- package/dist/world/terrain.d.ts +45 -0
- package/dist/world/terrain.js +78 -0
- package/dist/world.d.ts +14 -5
- package/dist/world.js +12 -3
- package/package.json +1 -1
- 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-combat/SKILL.md +5 -1
- package/skills/jgengine-combat/api.md +36 -0
- package/skills/jgengine-combat/capabilities.md +44 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/reference.md +12 -2
- package/skills/jgengine-gameplay/api.md +152 -26
- package/skills/jgengine-gameplay/capabilities.md +78 -2
- package/skills/jgengine-gameplay/reference-systems.md +1 -5
- 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/jgengine/reference-cartridge.md +0 -72
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
function clamp01(value) {
|
|
2
|
+
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
3
|
+
}
|
|
4
|
+
function normalizeLevel(level) {
|
|
5
|
+
const floored = Math.floor(level);
|
|
6
|
+
return floored < 1 ? 1 : floored;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Scatter `count` points one at a time across `statIds`, choosing each target uniformly from the
|
|
10
|
+
* injected rng. Mutates `into` in place; bounded by `count`. The rng draws the sole source of
|
|
11
|
+
* randomness, so an identical rng sequence reproduces an identical distribution.
|
|
12
|
+
*/
|
|
13
|
+
function distributePoints(statIds, count, rng, into) {
|
|
14
|
+
const n = statIds.length;
|
|
15
|
+
if (n === 0 || count <= 0)
|
|
16
|
+
return;
|
|
17
|
+
for (let i = 0; i < count; i++) {
|
|
18
|
+
let index = Math.floor(rng() * n);
|
|
19
|
+
if (index >= n)
|
|
20
|
+
index = n - 1;
|
|
21
|
+
if (index < 0)
|
|
22
|
+
index = 0;
|
|
23
|
+
const stat = statIds[index];
|
|
24
|
+
into[stat] = (into[stat] ?? 0) + 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function zeroedPoints(statIds) {
|
|
28
|
+
const points = {};
|
|
29
|
+
for (const stat of statIds)
|
|
30
|
+
points[stat] = 0;
|
|
31
|
+
return points;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Roll a fresh wild creature at `level`: distribute `(level - 1)` points randomly across the
|
|
35
|
+
* species' stats using the injected rng and return a serializable {@link CreatureStatInstance}.
|
|
36
|
+
* Deterministic — the same rng sequence yields the same point spread. Levels below 1 clamp to 1
|
|
37
|
+
* (no points). Use `seededRng` from `../random/rng` for a persisted, reproducible stream.
|
|
38
|
+
*
|
|
39
|
+
* @capability spawn-level-roll distribute a wild creature's spawn level into random per-stat points
|
|
40
|
+
*/
|
|
41
|
+
export function rollSpawnInstance(speciesId, species, level, rng) {
|
|
42
|
+
const statIds = Object.keys(species);
|
|
43
|
+
const points = zeroedPoints(statIds);
|
|
44
|
+
const normalized = normalizeLevel(level);
|
|
45
|
+
distributePoints(statIds, normalized - 1, rng, points);
|
|
46
|
+
return { speciesId, level: normalized, points, domesticLevels: 0 };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve an instance's live stat values from its distributed points:
|
|
50
|
+
* `value = base * (1 + points * growth)` per stat. Stats with zero points return their base.
|
|
51
|
+
*
|
|
52
|
+
* @capability spawn-stat-values resolve a creature's per-stat values from its distributed points
|
|
53
|
+
*/
|
|
54
|
+
export function resolveSpawnStats(species, instance) {
|
|
55
|
+
const out = {};
|
|
56
|
+
for (const stat of Object.keys(species)) {
|
|
57
|
+
const def = species[stat];
|
|
58
|
+
const pts = instance.points[stat] ?? 0;
|
|
59
|
+
out[stat] = def.base * (1 + pts * def.growth);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Bonus levels granted by a capture at the given effectiveness:
|
|
65
|
+
* `floor(level * clamp01(effectiveness) / 2)`. A perfect (1.0) capture grants half the wild
|
|
66
|
+
* level; a botched (0) capture grants none. Effectiveness outside `[0,1]` clamps.
|
|
67
|
+
*
|
|
68
|
+
* @capability capture-bonus-levels convert wild level and capture effectiveness into bonus levels
|
|
69
|
+
*/
|
|
70
|
+
export function captureBonusLevels(level, effectiveness) {
|
|
71
|
+
return Math.floor((normalizeLevel(level) * clamp01(effectiveness)) / 2);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Apply a capture at the given effectiveness: compute bonus levels via {@link captureBonusLevels},
|
|
75
|
+
* distribute those extra points randomly with the injected rng, and return the post-capture
|
|
76
|
+
* instance (level raised to the tamed total) alongside the granted bonus levels and tamed level.
|
|
77
|
+
* The input instance is not mutated.
|
|
78
|
+
*
|
|
79
|
+
* @capability capture-effectiveness-tame apply capture effectiveness to grant and distribute bonus levels
|
|
80
|
+
*/
|
|
81
|
+
export function applyCaptureEffectiveness(species, instance, effectiveness, rng) {
|
|
82
|
+
const bonusLevels = captureBonusLevels(instance.level, effectiveness);
|
|
83
|
+
const statIds = Object.keys(species);
|
|
84
|
+
const points = { ...instance.points };
|
|
85
|
+
distributePoints(statIds, bonusLevels, rng, points);
|
|
86
|
+
const tamedLevel = instance.level + bonusLevels;
|
|
87
|
+
return {
|
|
88
|
+
bonusLevels,
|
|
89
|
+
tamedLevel,
|
|
90
|
+
instance: { ...instance, level: tamedLevel, points },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Spend one earned post-capture level into `stat`, adding a single growth increment (one point)
|
|
95
|
+
* and raising both `level` and `domesticLevels`. When `maxDomesticLevels` is set and already
|
|
96
|
+
* reached, the spend is refused (`spent: false`) and the instance is returned untouched. Pure:
|
|
97
|
+
* returns a new instance rather than mutating the input.
|
|
98
|
+
*
|
|
99
|
+
* @capability domestic-level-spend spend an earned post-capture level into a chosen stat under a cap
|
|
100
|
+
*/
|
|
101
|
+
export function spendDomesticLevel(instance, stat, options) {
|
|
102
|
+
const cap = options?.maxDomesticLevels;
|
|
103
|
+
if (cap !== undefined && instance.domesticLevels >= cap) {
|
|
104
|
+
return { instance, spent: false };
|
|
105
|
+
}
|
|
106
|
+
const points = { ...instance.points };
|
|
107
|
+
points[stat] = (points[stat] ?? 0) + 1;
|
|
108
|
+
return {
|
|
109
|
+
instance: {
|
|
110
|
+
...instance,
|
|
111
|
+
points,
|
|
112
|
+
level: instance.level + 1,
|
|
113
|
+
domesticLevels: instance.domesticLevels + 1,
|
|
114
|
+
},
|
|
115
|
+
spent: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Deep-copy an instance into a detached, serializable record — the transport/save counterpart of
|
|
120
|
+
* {@link restoreSpawnInstance}. Copies the points map so later edits don't leak into the snapshot.
|
|
121
|
+
*
|
|
122
|
+
* @capability spawn-instance-snapshot deep-copy a creature stat instance into a serializable record
|
|
123
|
+
*/
|
|
124
|
+
export function snapshotSpawnInstance(instance) {
|
|
125
|
+
return {
|
|
126
|
+
speciesId: instance.speciesId,
|
|
127
|
+
level: instance.level,
|
|
128
|
+
domesticLevels: instance.domesticLevels,
|
|
129
|
+
points: { ...instance.points },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Rehydrate a creature stat instance from decoded data (e.g. `JSON.parse` output), returning a
|
|
134
|
+
* fresh deep copy the caller owns. Inverse of {@link snapshotSpawnInstance}.
|
|
135
|
+
*
|
|
136
|
+
* @capability spawn-instance-restore rehydrate a creature stat instance from decoded snapshot data
|
|
137
|
+
*/
|
|
138
|
+
export function restoreSpawnInstance(data) {
|
|
139
|
+
return snapshotSpawnInstance(data);
|
|
140
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Serializable state for any bounded named resource. The caller defines what the resource means. */
|
|
2
|
+
export interface StatPool {
|
|
3
|
+
current: number;
|
|
4
|
+
max: number;
|
|
5
|
+
min: number;
|
|
6
|
+
}
|
|
7
|
+
/** Input used to create a normalized pool. `current` defaults to `max`; `min` defaults to zero. */
|
|
8
|
+
export interface StatPoolInput {
|
|
9
|
+
current?: number;
|
|
10
|
+
max: number;
|
|
11
|
+
min?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Partial update applied without mutating the previous pool. */
|
|
14
|
+
export interface StatPoolPatch {
|
|
15
|
+
current?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
min?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Structural bridge to caller-owned resource state. Implement it over an ECS,
|
|
21
|
+
* Redux/Zustand store, plain object, database-backed model, or JGengine's native
|
|
22
|
+
* entity stats without moving ownership into the SDK.
|
|
23
|
+
*/
|
|
24
|
+
export interface StatPoolAccess<TOwnerId extends string = string, TStatId extends string = string> {
|
|
25
|
+
get(ownerId: TOwnerId, statId: TStatId): StatPool | null;
|
|
26
|
+
set(ownerId: TOwnerId, statId: TStatId, next: StatPool): unknown;
|
|
27
|
+
}
|
|
28
|
+
/** Inspectable result of a bounded pool change. */
|
|
29
|
+
export interface StatPoolChange {
|
|
30
|
+
previous: StatPool;
|
|
31
|
+
pool: StatPool;
|
|
32
|
+
/** Actual applied amount after clamping. */
|
|
33
|
+
applied: number;
|
|
34
|
+
hitMin: boolean;
|
|
35
|
+
hitMax: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Outcome of changing a pool through {@link StatPoolAccess}. */
|
|
38
|
+
export type StatPoolAccessResult = ({
|
|
39
|
+
status: "ok";
|
|
40
|
+
} & StatPoolChange) | {
|
|
41
|
+
status: "rejected";
|
|
42
|
+
reason: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Normalize caller data into a complete plain-data pool.
|
|
46
|
+
*
|
|
47
|
+
* @capability stat-pool create a serializable current/min/max pool for any caller-named resource
|
|
48
|
+
*/
|
|
49
|
+
export declare function createStatPool(input: StatPoolInput): StatPool;
|
|
50
|
+
/**
|
|
51
|
+
* Apply new bounds/current to a pool, clamping the result and leaving the input untouched.
|
|
52
|
+
*
|
|
53
|
+
* @capability stat-pool-patch immutably retune a bounded resource's current value or bounds
|
|
54
|
+
*/
|
|
55
|
+
export declare function patchStatPool(pool: StatPool, patch: StatPoolPatch): StatPool;
|
|
56
|
+
/**
|
|
57
|
+
* Pure bounded delta transition with the exact applied amount and boundary flags.
|
|
58
|
+
*
|
|
59
|
+
* @capability stat-pool-change immutably increase or decrease any named bounded resource with clamp evidence
|
|
60
|
+
*/
|
|
61
|
+
export declare function changeStatPool(pool: StatPool, amount: number): StatPoolChange;
|
|
62
|
+
/**
|
|
63
|
+
* Read, transition, and write a caller-owned pool through the structural
|
|
64
|
+
* adapter. Positive amounts increase the resource; negative amounts decrease
|
|
65
|
+
* it. The adapter receives a complete replacement value, never a hidden
|
|
66
|
+
* mutation.
|
|
67
|
+
*
|
|
68
|
+
* @capability portable-stat-pool apply deterministic bounded resource changes through a caller-owned stat adapter
|
|
69
|
+
*/
|
|
70
|
+
export declare function applyStatPoolDelta<TOwnerId extends string, TStatId extends string>(access: StatPoolAccess<TOwnerId, TStatId>, ownerId: TOwnerId, statId: TStatId, amount: number): StatPoolAccessResult;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { clamp } from "../math/scalar.js";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize caller data into a complete plain-data pool.
|
|
4
|
+
*
|
|
5
|
+
* @capability stat-pool create a serializable current/min/max pool for any caller-named resource
|
|
6
|
+
*/
|
|
7
|
+
export function createStatPool(input) {
|
|
8
|
+
const min = input.min ?? 0;
|
|
9
|
+
return {
|
|
10
|
+
current: clamp(input.current ?? input.max, min, input.max),
|
|
11
|
+
max: input.max,
|
|
12
|
+
min,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Apply new bounds/current to a pool, clamping the result and leaving the input untouched.
|
|
17
|
+
*
|
|
18
|
+
* @capability stat-pool-patch immutably retune a bounded resource's current value or bounds
|
|
19
|
+
*/
|
|
20
|
+
export function patchStatPool(pool, patch) {
|
|
21
|
+
const max = patch.max ?? pool.max;
|
|
22
|
+
const min = patch.min ?? pool.min;
|
|
23
|
+
return {
|
|
24
|
+
current: clamp(patch.current ?? pool.current, min, max),
|
|
25
|
+
max,
|
|
26
|
+
min,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pure bounded delta transition with the exact applied amount and boundary flags.
|
|
31
|
+
*
|
|
32
|
+
* @capability stat-pool-change immutably increase or decrease any named bounded resource with clamp evidence
|
|
33
|
+
*/
|
|
34
|
+
export function changeStatPool(pool, amount) {
|
|
35
|
+
const next = patchStatPool(pool, { current: pool.current + amount });
|
|
36
|
+
return {
|
|
37
|
+
previous: pool,
|
|
38
|
+
pool: next,
|
|
39
|
+
applied: next.current - pool.current,
|
|
40
|
+
hitMin: next.current === next.min,
|
|
41
|
+
hitMax: next.current === next.max,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read, transition, and write a caller-owned pool through the structural
|
|
46
|
+
* adapter. Positive amounts increase the resource; negative amounts decrease
|
|
47
|
+
* it. The adapter receives a complete replacement value, never a hidden
|
|
48
|
+
* mutation.
|
|
49
|
+
*
|
|
50
|
+
* @capability portable-stat-pool apply deterministic bounded resource changes through a caller-owned stat adapter
|
|
51
|
+
*/
|
|
52
|
+
export function applyStatPoolDelta(access, ownerId, statId, amount) {
|
|
53
|
+
const pool = access.get(ownerId, statId);
|
|
54
|
+
if (pool === null)
|
|
55
|
+
return { status: "rejected", reason: `unknown stat "${statId}"` };
|
|
56
|
+
const change = changeStatPool(pool, amount);
|
|
57
|
+
access.set(ownerId, statId, change.pool);
|
|
58
|
+
return { status: "ok", ...change };
|
|
59
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export interface ChangeSignal {
|
|
2
3
|
subscribe(listener: () => void): () => void;
|
|
3
4
|
notify(): void;
|
|
4
5
|
version(): number;
|
|
5
6
|
}
|
|
7
|
+
/** @internal */
|
|
6
8
|
export declare function createChangeSignal(): ChangeSignal;
|
|
9
|
+
/** @internal */
|
|
7
10
|
export declare function notifyAfter<T extends object, K extends keyof T>(target: T, methods: readonly K[], notify: () => void): T;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export function createChangeSignal() {
|
|
2
3
|
const listeners = new Set();
|
|
3
4
|
let version = 0;
|
|
@@ -18,6 +19,7 @@ export function createChangeSignal() {
|
|
|
18
19
|
},
|
|
19
20
|
};
|
|
20
21
|
}
|
|
22
|
+
/** @internal */
|
|
21
23
|
export function notifyAfter(target, methods, notify) {
|
|
22
24
|
const wrapped = { ...target };
|
|
23
25
|
for (const method of methods) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export interface ObservableKeyedStore<T> {
|
|
2
3
|
set(key: string, value: T): void;
|
|
3
4
|
delete(key: string): void;
|
|
@@ -16,4 +17,5 @@ export interface ObservableKeyedStore<T> {
|
|
|
16
17
|
/** Replace all entries with `data` (clears keys absent from it), emitting once. */
|
|
17
18
|
hydrate(data: readonly (readonly [string, T])[]): void;
|
|
18
19
|
}
|
|
20
|
+
/** @internal */
|
|
19
21
|
export declare function createObservableKeyedStore<T>(areEqual?: (previous: T, next: T) => boolean): ObservableKeyedStore<T>;
|
package/dist/ui/hudLayout.d.ts
CHANGED
|
@@ -86,5 +86,10 @@ export declare function rectFromPlacement(placement: HudPlacement, size: HudSize
|
|
|
86
86
|
export declare function registerActiveHudLayout(store: HudLayoutStore): () => void;
|
|
87
87
|
/** @internal */
|
|
88
88
|
export declare function listActiveHudLayouts(): HudLayoutStore[];
|
|
89
|
+
/**
|
|
90
|
+
* @internal Notified whenever a `HudCanvas` mounts or unmounts (the active-layout set changes).
|
|
91
|
+
* Lets the editor's HUD-layout surface enable editing on layouts that mount after it opens.
|
|
92
|
+
*/
|
|
93
|
+
export declare function subscribeActiveHudLayouts(listener: () => void): () => void;
|
|
89
94
|
/** @internal */
|
|
90
95
|
export declare function createHudLayout(options?: HudLayoutOptions): HudLayoutStore;
|
package/dist/ui/hudLayout.js
CHANGED
|
@@ -116,17 +116,33 @@ function parseSerialized(raw) {
|
|
|
116
116
|
return out;
|
|
117
117
|
}
|
|
118
118
|
const activeHudLayouts = new Set();
|
|
119
|
+
const activeHudLayoutListeners = new Set();
|
|
119
120
|
/** @internal Agent bridge seam — HudCanvas registers its store while mounted so headless drivers can reach canvas mode. */
|
|
120
121
|
export function registerActiveHudLayout(store) {
|
|
121
122
|
activeHudLayouts.add(store);
|
|
123
|
+
for (const listener of [...activeHudLayoutListeners])
|
|
124
|
+
listener();
|
|
122
125
|
return () => {
|
|
123
|
-
activeHudLayouts.delete(store)
|
|
126
|
+
if (!activeHudLayouts.delete(store))
|
|
127
|
+
return;
|
|
128
|
+
for (const listener of [...activeHudLayoutListeners])
|
|
129
|
+
listener();
|
|
124
130
|
};
|
|
125
131
|
}
|
|
126
132
|
/** @internal */
|
|
127
133
|
export function listActiveHudLayouts() {
|
|
128
134
|
return [...activeHudLayouts];
|
|
129
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* @internal Notified whenever a `HudCanvas` mounts or unmounts (the active-layout set changes).
|
|
138
|
+
* Lets the editor's HUD-layout surface enable editing on layouts that mount after it opens.
|
|
139
|
+
*/
|
|
140
|
+
export function subscribeActiveHudLayouts(listener) {
|
|
141
|
+
activeHudLayoutListeners.add(listener);
|
|
142
|
+
return () => {
|
|
143
|
+
activeHudLayoutListeners.delete(listener);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
130
146
|
function panelToUi(panel) {
|
|
131
147
|
const layout = {
|
|
132
148
|
anchor: panel.placement.anchor,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ENTITY_MARKER_KINDS, type AuthoredObjectMarkerLike } from "./authoredObjects.js";
|
|
2
|
+
/** Marker kinds {@link authoredEntitySpawns} treats as spawnable entities by default. Re-exported from `authoredObjects` as the single source of truth (the same kinds `resolveAuthoredObjects` skips). */
|
|
3
|
+
export { ENTITY_MARKER_KINDS };
|
|
4
|
+
/** The minimal marker shape entity spawns read; any `EditorMarker` satisfies it. */
|
|
5
|
+
export type AuthoredEntityMarkerLike = AuthoredObjectMarkerLike;
|
|
6
|
+
/** The minimal document shape {@link authoredEntitySpawns} walks; any `EditorDocument` satisfies it. */
|
|
7
|
+
export interface AuthoredEntityDocumentLike {
|
|
8
|
+
markers: readonly AuthoredEntityMarkerLike[];
|
|
9
|
+
}
|
|
10
|
+
/** One resolved entity to spawn from the authored scene — its marker id, catalog id, and pose. */
|
|
11
|
+
export interface AuthoredEntitySpawn {
|
|
12
|
+
/** The marker's id — a stable spawn id, so re-spawning the same marker is idempotent. */
|
|
13
|
+
markerId: string;
|
|
14
|
+
/** The entity catalog id to spawn (join key into `content.entityById`). */
|
|
15
|
+
catalogId: string;
|
|
16
|
+
/** World-space spawn position as a spawn-ready `[x, y, z]` tuple. */
|
|
17
|
+
position: [number, number, number];
|
|
18
|
+
/** Facing (yaw radians), 0 when the marker has none. */
|
|
19
|
+
rotationY: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every authored entity marker (`mob`/`boss` by default) that carries a `catalogId`, as a spawn plan
|
|
23
|
+
* the runtime feeds to `ctx.scene.entity.spawn(catalogId, { id, position })`. Reads the same document
|
|
24
|
+
* the editor authors, so placing a mob and setting its catalog id in the editor is all it takes to
|
|
25
|
+
* spawn it — no coordinates or spawn tables copied into game code. Markers without a catalog id are
|
|
26
|
+
* skipped (they are placement-only, e.g. a boss arena marker a game handles specially).
|
|
27
|
+
* @capability authored-entities spawn authored mob/boss markers from the editor document at runtime
|
|
28
|
+
*/
|
|
29
|
+
export declare function authoredEntitySpawns(document: AuthoredEntityDocumentLike, kinds?: readonly string[]): AuthoredEntitySpawn[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ENTITY_MARKER_KINDS, markerCatalogId } from "./authoredObjects.js";
|
|
2
|
+
/** Marker kinds {@link authoredEntitySpawns} treats as spawnable entities by default. Re-exported from `authoredObjects` as the single source of truth (the same kinds `resolveAuthoredObjects` skips). */
|
|
3
|
+
export { ENTITY_MARKER_KINDS };
|
|
4
|
+
/**
|
|
5
|
+
* Every authored entity marker (`mob`/`boss` by default) that carries a `catalogId`, as a spawn plan
|
|
6
|
+
* the runtime feeds to `ctx.scene.entity.spawn(catalogId, { id, position })`. Reads the same document
|
|
7
|
+
* the editor authors, so placing a mob and setting its catalog id in the editor is all it takes to
|
|
8
|
+
* spawn it — no coordinates or spawn tables copied into game code. Markers without a catalog id are
|
|
9
|
+
* skipped (they are placement-only, e.g. a boss arena marker a game handles specially).
|
|
10
|
+
* @capability authored-entities spawn authored mob/boss markers from the editor document at runtime
|
|
11
|
+
*/
|
|
12
|
+
export function authoredEntitySpawns(document, kinds = ENTITY_MARKER_KINDS) {
|
|
13
|
+
const wanted = new Set(kinds);
|
|
14
|
+
const spawns = [];
|
|
15
|
+
for (const marker of document.markers) {
|
|
16
|
+
if (!wanted.has(marker.kind))
|
|
17
|
+
continue;
|
|
18
|
+
const catalogId = markerCatalogId(marker);
|
|
19
|
+
if (catalogId === null)
|
|
20
|
+
continue;
|
|
21
|
+
spawns.push({
|
|
22
|
+
markerId: marker.id,
|
|
23
|
+
catalogId,
|
|
24
|
+
position: [marker.position.x, marker.position.y, marker.position.z],
|
|
25
|
+
rotationY: marker.rotationY ?? 0,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return spawns;
|
|
29
|
+
}
|
|
@@ -42,12 +42,26 @@ export interface PlaceAuthoredObjectsOptions {
|
|
|
42
42
|
* @capability authored-objects place catalog mesh props from an editor document
|
|
43
43
|
*/
|
|
44
44
|
export declare function markerCatalogId(marker: AuthoredObjectMarkerLike): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Marker kinds that are spawnable entities (materialized by `authoredEntitySpawns`), not static
|
|
47
|
+
* catalog props. {@link resolveAuthoredObjects} skips them by default so a placed `mob`/`boss` is
|
|
48
|
+
* spawned once as an entity and never also placed as a static mesh.
|
|
49
|
+
* @capability authored-objects marker kinds resolved as entity spawns, not static props
|
|
50
|
+
*/
|
|
51
|
+
export declare const ENTITY_MARKER_KINDS: readonly string[];
|
|
52
|
+
/** Options for {@link resolveAuthoredObjects}. */
|
|
53
|
+
export interface ResolveAuthoredObjectsOptions {
|
|
54
|
+
/** Marker kinds to skip (they are handled elsewhere). Default {@link ENTITY_MARKER_KINDS}. */
|
|
55
|
+
excludeKinds?: readonly string[];
|
|
56
|
+
}
|
|
45
57
|
/**
|
|
46
58
|
* Every marker carrying a catalog id, as placeable props — pure, no terrain sample. Parallel to
|
|
47
59
|
* {@link resolveScatter}: games and headless tests read the same list `<AuthoredObjects>` places.
|
|
60
|
+
* Entity-spawn kinds (`mob`/`boss`) are skipped by default — they carry a `catalogId` for their
|
|
61
|
+
* entity definition, but are spawned via `authoredEntitySpawns`, not placed as static meshes.
|
|
48
62
|
* @capability authored-objects place catalog mesh props from an editor document
|
|
49
63
|
*/
|
|
50
|
-
export declare function resolveAuthoredObjects(document: AuthoredObjectsDocumentLike): AuthoredObject[];
|
|
64
|
+
export declare function resolveAuthoredObjects(document: AuthoredObjectsDocumentLike, options?: ResolveAuthoredObjectsOptions): AuthoredObject[];
|
|
51
65
|
/**
|
|
52
66
|
* Places resolved authored objects into an object store, grounding each on `sampleHeight(x,z)` plus
|
|
53
67
|
* per-object and options vertical offsets. Returns the instance ids that were placed (or kept).
|
|
@@ -22,14 +22,26 @@ function markerVerticalOffset(marker) {
|
|
|
22
22
|
metaNumber(marker.meta, "yOffset");
|
|
23
23
|
return fromMeta ?? 0;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Marker kinds that are spawnable entities (materialized by `authoredEntitySpawns`), not static
|
|
27
|
+
* catalog props. {@link resolveAuthoredObjects} skips them by default so a placed `mob`/`boss` is
|
|
28
|
+
* spawned once as an entity and never also placed as a static mesh.
|
|
29
|
+
* @capability authored-objects marker kinds resolved as entity spawns, not static props
|
|
30
|
+
*/
|
|
31
|
+
export const ENTITY_MARKER_KINDS = ["mob", "boss"];
|
|
25
32
|
/**
|
|
26
33
|
* Every marker carrying a catalog id, as placeable props — pure, no terrain sample. Parallel to
|
|
27
34
|
* {@link resolveScatter}: games and headless tests read the same list `<AuthoredObjects>` places.
|
|
35
|
+
* Entity-spawn kinds (`mob`/`boss`) are skipped by default — they carry a `catalogId` for their
|
|
36
|
+
* entity definition, but are spawned via `authoredEntitySpawns`, not placed as static meshes.
|
|
28
37
|
* @capability authored-objects place catalog mesh props from an editor document
|
|
29
38
|
*/
|
|
30
|
-
export function resolveAuthoredObjects(document) {
|
|
39
|
+
export function resolveAuthoredObjects(document, options = {}) {
|
|
40
|
+
const excluded = new Set(options.excludeKinds ?? ENTITY_MARKER_KINDS);
|
|
31
41
|
const out = [];
|
|
32
42
|
for (const marker of document.markers) {
|
|
43
|
+
if (excluded.has(marker.kind))
|
|
44
|
+
continue;
|
|
33
45
|
const catalogId = markerCatalogId(marker);
|
|
34
46
|
if (catalogId === null)
|
|
35
47
|
continue;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SceneMarkerLike } from "./sceneShapes.js";
|
|
2
|
+
/** Minimal marker shape spawn queries read; any `EditorMarker` satisfies it. */
|
|
3
|
+
export interface AuthoredSpawnMarkerLike extends SceneMarkerLike {
|
|
4
|
+
rotationY?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Minimal document shape spawn queries walk; any `EditorDocument` satisfies it. */
|
|
7
|
+
export interface AuthoredSpawnDocumentLike {
|
|
8
|
+
markers: readonly AuthoredSpawnMarkerLike[];
|
|
9
|
+
}
|
|
10
|
+
/** Marker kind {@link authoredSpawnPosition} resolves by default. */
|
|
11
|
+
export declare const PLAYER_SPAWN_KIND = "player_spawn";
|
|
12
|
+
/**
|
|
13
|
+
* Every marker of `kind`, in document order — the generic query behind gameplay that references
|
|
14
|
+
* authored markers by kind instead of copying coordinates into code.
|
|
15
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
16
|
+
*/
|
|
17
|
+
export declare function markersOfKind<TMarker extends AuthoredSpawnMarkerLike>(document: {
|
|
18
|
+
markers: readonly TMarker[];
|
|
19
|
+
}, kind: string): TMarker[];
|
|
20
|
+
/** Options for {@link authoredSpawnPosition}: pick a specific marker id or a non-default kind. */
|
|
21
|
+
export interface AuthoredSpawnOptions {
|
|
22
|
+
/** Marker kind to resolve. Default {@link PLAYER_SPAWN_KIND}. */
|
|
23
|
+
kind?: string;
|
|
24
|
+
/** Exact marker id; when set, kind only filters among markers with this id's kind. */
|
|
25
|
+
id?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Position of the authored spawn marker as a spawn-ready `[x, y, z]` tuple, or null when the
|
|
29
|
+
* document has none. Reads the first `player_spawn` marker by default, so dragging the marker in
|
|
30
|
+
* the editor moves where players spawn — no coordinates copied into game code.
|
|
31
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
32
|
+
*/
|
|
33
|
+
export declare function authoredSpawnPosition(document: AuthoredSpawnDocumentLike, options?: AuthoredSpawnOptions): [number, number, number] | null;
|
|
34
|
+
/**
|
|
35
|
+
* Facing (yaw radians) of the authored spawn marker, or 0 when the document has none — pair with
|
|
36
|
+
* {@link authoredSpawnPosition} to spawn players where and how the editor placed them.
|
|
37
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
38
|
+
*/
|
|
39
|
+
export declare function authoredSpawnRotation(document: AuthoredSpawnDocumentLike, options?: AuthoredSpawnOptions): number;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Marker kind {@link authoredSpawnPosition} resolves by default. */
|
|
2
|
+
export const PLAYER_SPAWN_KIND = "player_spawn";
|
|
3
|
+
/**
|
|
4
|
+
* Every marker of `kind`, in document order — the generic query behind gameplay that references
|
|
5
|
+
* authored markers by kind instead of copying coordinates into code.
|
|
6
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
7
|
+
*/
|
|
8
|
+
export function markersOfKind(document, kind) {
|
|
9
|
+
return document.markers.filter((marker) => marker.kind === kind);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Position of the authored spawn marker as a spawn-ready `[x, y, z]` tuple, or null when the
|
|
13
|
+
* document has none. Reads the first `player_spawn` marker by default, so dragging the marker in
|
|
14
|
+
* the editor moves where players spawn — no coordinates copied into game code.
|
|
15
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
16
|
+
*/
|
|
17
|
+
export function authoredSpawnPosition(document, options = {}) {
|
|
18
|
+
const kind = options.kind ?? PLAYER_SPAWN_KIND;
|
|
19
|
+
const marker = options.id === undefined
|
|
20
|
+
? document.markers.find((entry) => entry.kind === kind)
|
|
21
|
+
: document.markers.find((entry) => entry.id === options.id && entry.kind === kind);
|
|
22
|
+
if (marker === undefined)
|
|
23
|
+
return null;
|
|
24
|
+
return [marker.position.x, marker.position.y, marker.position.z];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Facing (yaw radians) of the authored spawn marker, or 0 when the document has none — pair with
|
|
28
|
+
* {@link authoredSpawnPosition} to spawn players where and how the editor placed them.
|
|
29
|
+
* @capability authored-spawn read spawn points and markers from the editor document
|
|
30
|
+
*/
|
|
31
|
+
export function authoredSpawnRotation(document, options = {}) {
|
|
32
|
+
const kind = options.kind ?? PLAYER_SPAWN_KIND;
|
|
33
|
+
const marker = options.id === undefined
|
|
34
|
+
? document.markers.find((entry) => entry.kind === kind)
|
|
35
|
+
: document.markers.find((entry) => entry.id === options.id && entry.kind === kind);
|
|
36
|
+
return marker?.rotationY ?? 0;
|
|
37
|
+
}
|