@jgengine/core 0.26.0 → 0.28.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 +174 -0
- package/dist/ai/policeResponse.d.ts +253 -0
- package/dist/ai/policeResponse.js +421 -0
- package/dist/anim/actionLayer.d.ts +47 -0
- package/dist/anim/actionLayer.js +63 -0
- package/dist/anim/ikSolver.d.ts +14 -0
- package/dist/anim/ikSolver.js +37 -0
- package/dist/anim/locomotionGraph.d.ts +58 -3
- package/dist/anim/locomotionGraph.js +142 -15
- package/dist/anim/ragdollRig.d.ts +53 -0
- package/dist/anim/ragdollRig.js +132 -0
- package/dist/anim/skeletonStandard.d.ts +140 -0
- package/dist/anim/skeletonStandard.js +326 -0
- package/dist/combat/damageRuntime.d.ts +14 -1
- package/dist/combat/damageRuntime.js +62 -4
- package/dist/combat/death.js +7 -3
- package/dist/combat/deathReason.d.ts +7 -1
- package/dist/combat/effects.d.ts +25 -0
- package/dist/combat/effects.js +16 -0
- package/dist/combat/statusRuntime.d.ts +2 -0
- package/dist/combat/statusRuntime.js +1 -0
- package/dist/economy/loanBook.d.ts +182 -0
- package/dist/economy/loanBook.js +303 -0
- package/dist/economy/market.d.ts +161 -0
- package/dist/economy/market.js +248 -0
- package/dist/economy/transactionLedger.d.ts +162 -0
- package/dist/economy/transactionLedger.js +301 -0
- package/dist/editor/creatorStorage.js +3 -1
- package/dist/editor/simulation.d.ts +5 -2
- package/dist/editor/simulation.js +29 -19
- package/dist/game/cameraConfig.d.ts +14 -0
- package/dist/game/clipRoles.d.ts +5 -4
- package/dist/game/clipRoles.js +34 -4
- package/dist/game/events.d.ts +27 -0
- package/dist/game/gameSave.d.ts +142 -0
- package/dist/game/gameSave.js +174 -0
- package/dist/game/playableGame.d.ts +74 -6
- package/dist/game/saveSlotIndex.d.ts +54 -0
- package/dist/game/saveSlotIndex.js +71 -0
- package/dist/game/saveSlots.d.ts +2 -0
- package/dist/game/saveSlots.js +1 -1
- package/dist/game/saveStore.d.ts +8 -0
- package/dist/game/saveStore.js +16 -0
- package/dist/gameplay.d.ts +17 -2
- package/dist/gameplay.js +15 -0
- package/dist/inventory/carryLoad.d.ts +48 -0
- package/dist/inventory/carryLoad.js +44 -0
- package/dist/inventory/equipment.d.ts +95 -0
- package/dist/inventory/equipment.js +169 -0
- package/dist/inventory/inventoryModel.d.ts +15 -6
- package/dist/inventory/inventoryModel.js +54 -5
- package/dist/inventory/shapedGrid.d.ts +26 -0
- package/dist/inventory/shapedGrid.js +47 -0
- package/dist/loot/lootGen.d.ts +186 -0
- package/dist/loot/lootGen.js +437 -0
- package/dist/loot/lootWeapon.d.ts +35 -0
- package/dist/loot/lootWeapon.js +70 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +156 -1
- package/dist/mission/compositions.d.ts +85 -0
- package/dist/mission/compositions.js +130 -0
- package/dist/mission/mission.d.ts +435 -0
- package/dist/mission/mission.js +770 -0
- package/dist/mission/triggers.d.ts +61 -0
- package/dist/mission/triggers.js +108 -0
- package/dist/nav/laneGraph.d.ts +178 -0
- package/dist/nav/laneGraph.js +671 -0
- package/dist/nav/traffic.d.ts +222 -0
- package/dist/nav/traffic.js +918 -0
- package/dist/physics/kinematicVehicle.d.ts +36 -0
- package/dist/physics/kinematicVehicle.js +15 -1
- package/dist/physics/vehicleDamage.d.ts +136 -0
- package/dist/physics/vehicleDamage.js +214 -0
- package/dist/physics/vehicleParts.d.ts +62 -0
- package/dist/physics/vehicleParts.js +65 -0
- package/dist/progression/gearScore.d.ts +56 -0
- package/dist/progression/gearScore.js +59 -0
- package/dist/progression/skillTree.d.ts +129 -0
- package/dist/progression/skillTree.js +265 -0
- package/dist/progression/xpCurve.d.ts +60 -0
- package/dist/progression/xpCurve.js +141 -0
- package/dist/runtime/context/combat.js +11 -0
- package/dist/scene/builtinSceneKinds.js +2 -0
- package/dist/settings/accessibilitySettings.d.ts +81 -0
- package/dist/settings/accessibilitySettings.js +152 -0
- package/dist/settings/graphicsProfile.d.ts +3 -0
- package/dist/settings/graphicsProfile.js +4 -0
- package/dist/settings/settingsModel.d.ts +1 -1
- package/dist/settings/settingsModel.js +1 -0
- package/dist/sim/nearAgents.d.ts +8 -1
- package/dist/sim/nearAgents.js +34 -17
- package/dist/sim/offScreen.d.ts +2 -1
- package/dist/sim/offScreen.js +14 -3
- package/dist/sim/population.d.ts +147 -0
- package/dist/sim/population.js +182 -0
- package/dist/sim/queue.d.ts +90 -0
- package/dist/sim/queue.js +171 -0
- package/dist/sim/rides.d.ts +287 -0
- package/dist/sim/rides.js +471 -0
- package/dist/sim/routine.js +19 -11
- package/dist/ui/loadingTips.d.ts +41 -0
- package/dist/ui/loadingTips.js +64 -0
- package/dist/ui/menuNav.d.ts +79 -0
- package/dist/ui/menuNav.js +147 -0
- package/dist/ui/menuTransition.d.ts +50 -0
- package/dist/ui/menuTransition.js +67 -0
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +2 -0
- package/dist/vfx/effectLibrary.d.ts +98 -0
- package/dist/vfx/effectLibrary.js +181 -0
- package/dist/vfx/particleBudget.d.ts +40 -0
- package/dist/vfx/particleBudget.js +39 -0
- package/dist/vfx/particleDirector.d.ts +16 -0
- package/dist/vfx/particleDirector.js +18 -0
- package/dist/world/authoredSimulation.js +7 -1
- package/dist/world/captureCamera.d.ts +30 -0
- package/dist/world/captureCamera.js +19 -0
- package/dist/world/decalKind.d.ts +48 -0
- package/dist/world/decalKind.js +86 -0
- package/dist/world/features.d.ts +18 -5
- package/dist/world/features.js +1 -0
- package/dist/world/grassKind.d.ts +5 -0
- package/dist/world/grassKind.js +3 -0
- package/dist/world/terraform.d.ts +8 -0
- package/dist/world/terrain.d.ts +1 -0
- package/dist/world/terrain.js +2 -0
- package/dist/world.d.ts +7 -2
- package/dist/world.js +5 -0
- package/package.json +1 -1
- package/skills/jgengine/api.md +2 -2
- package/skills/jgengine-combat/SKILL.md +9 -1
- package/skills/jgengine-combat/api.md +5 -4
- package/skills/jgengine-combat/reference.md +4 -0
- package/skills/jgengine-gameplay/SKILL.md +86 -0
- package/skills/jgengine-gameplay/api.md +485 -16
- package/skills/jgengine-gameplay/capabilities.md +131 -4
- package/skills/jgengine-gameplay/reference-systems.md +118 -0
- package/skills/jgengine-world/SKILL.md +8 -0
- package/skills/jgengine-world/api.md +243 -11
- package/skills/jgengine-world/capabilities.md +61 -1
- package/skills/jgengine-world/recipes/character-animation.md +25 -2
- package/skills/jgengine-world/recipes/vehicle-feel.md +5 -0
- package/skills/jgengine-world/reference.md +1 -1
|
@@ -24,7 +24,9 @@ spread, damage — hand the definition to `createWeaponSystem`
|
|
|
24
24
|
(`combat/weaponSystem`): it runs single/auto/burst, hitscan or ballistic
|
|
25
25
|
projectiles, interrupted reloads and a readable ammo economy, and resolves both
|
|
26
26
|
deliveries through `createDamageRuntime`. Generated guns (rolled parts) are the
|
|
27
|
-
same `WeaponDefinition
|
|
27
|
+
same `WeaponDefinition`: `loot/lootGen` rolls the item and `loot/lootWeapon`'s
|
|
28
|
+
`lootWeaponDefinition` maps its stats onto one, so no second weapon type exists;
|
|
29
|
+
see
|
|
28
30
|
[reference.md](reference.md#weapons-as-data).
|
|
29
31
|
For scene-backed `createProjectileSystem`, inject authoritative `rng`; spread
|
|
30
32
|
samples independent pellet rays and prediction stays on the center ray. The
|
|
@@ -42,6 +44,12 @@ compose through `createDamageRuntime` (`combat/damageRuntime`): a readable table
|
|
|
42
44
|
time-to-kill quotes, and status ticks that follow the declared stack rule. It
|
|
43
45
|
snapshots/restores as plain data; see
|
|
44
46
|
[reference.md](reference.md#damage-runtime-armor-crits-and-statuses).
|
|
47
|
+
Both damage paths raise `combat.damage` on `ctx.game.events`, one event per target
|
|
48
|
+
application: the runtime with the table `type`, and `ctx.scene.entity.effect` with
|
|
49
|
+
the effect id as `type` and `remaining` read from the last pool in the receive
|
|
50
|
+
order. Use the runtime for new damage that needs types, zones or armor; keep the
|
|
51
|
+
effect path for games that already drain `receive` pools. Neither delegates to
|
|
52
|
+
the other, so a hit never raises the event twice.
|
|
45
53
|
|
|
46
54
|
Closure-backed magazines expose snapshot/restore and `retune` for capacity/reload
|
|
47
55
|
changes without reconstructing ammo state; capacity overflow is explicit.
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
- `DamageTableDef` (interface): interface DamageTableDef — A serializable damage table: damage types scored against resistance classes, plus the armor curve those rows are read with. It is data a generated gun (B3.1) and a weapon runtime (B2.3) pass in; the engine holds no built-in elemental chart.
|
|
70
70
|
- `DamageTableRow` (interface): interface DamageTableRow — One readable row: a type against a resistance class at a given armor value.
|
|
71
71
|
- `DamageTypeDef` (interface): interface DamageTypeDef — One damage type's row in a damage table: a caller-owned id (`"kinetic"`, `"fire"`) and how it fares against each resistance class a target can present (`"flesh"`, `"shield"`, `"armor"`). Values below `1` are resisted, `1` is neutral, above `1` is a weakness, and `0` is immune.
|
|
72
|
-
- `DeathReason` (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; via?: { item?: string } } | { kind: "environment"; source: string } | { kind: "self"; source: string } — Why an entity died — who or what gets credit, for drop/command rules and the `entity.died` event.
|
|
72
|
+
- `DeathReason` (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; source?: string; via?: { item?: string } } | { kind: "environment"; source: string; attacker?: string } | { kind: "self"; source: string } — Why an entity died — who or what gets credit, for drop/command rules and the `entity.died` event. `source` names what dealt it: a player kill's killer instance id, or the hazard/item of an environment death; `attacker` carries the non-player entity's instance id when one dealt it.
|
|
73
73
|
- `DefenseResolution` (interface): interface DefenseResolution — Result of resolving an incoming hit against a defensive window: `outcome` is the graded defense (`"parry"`/`"block"`/`"iframe"`/`"hit"`) and `perfect` is true when the input landed inside the tight just-frame at the window's start rather than its wider active span.
|
|
74
74
|
- `DuplicateDamageTypeError` (class): class DuplicateDamageTypeError extends Error — Raised when a table lists the same damage type twice, which would make the winner depend on order.
|
|
75
75
|
- `EncounterConfig` (interface): interface EncounterConfig<TData = unknown> — Non-serializable configuration for an encounter: the authored phase tree plus predicate and spawn-provider registries. Re-supplied alongside a loaded {@link EncounterState}, the way `advanceSpawnDirector` pairs config with state, so the state itself stays pure data.
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
- `createCoverField` (function): function createCoverField(options: CoverFieldOptions): CoverField — Cover over authored boxes: derive the stand points once, re-derive them after an edit, and answer bounded nearest-cover queries. `walkable` is injected, so the seam checks the game's own ground test (`nav.findPolygon(point) !== null`) instead of assuming a flat floor.
|
|
244
244
|
- `createDamageClamp` (function): function createDamageClamp(config: DamageClampConfig): DamageInterceptor — A stateless interceptor that caps any single application at `maxPerHit`.
|
|
245
245
|
- `createDamagePipeline` (function): function createDamagePipeline(initial?: readonly DamageInterceptor[]): DamagePipeline — Create a mutable damage interception chain that state transitions install into and remove from — e.g. add an invulnerability interceptor on phase enter and drop it on phase exit. Evaluation order is install order.
|
|
246
|
-
- `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet.
|
|
246
|
+
- `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet. Every application — a hit or a status tick — is announced as `combat.damage` carrying the target, source, type, amount, absorbed, remaining health and a lethal flag, so a HUD names the blow instead of guessing.
|
|
247
247
|
- `createDeathSystem` (function): function createDeathSystem(deps: DeathSystemDeps): DeathSystem — Resolve entity death and the on-death consequences — drops, respawn eligibility, kill credit.
|
|
248
248
|
- `createDefensiveWindow` (function): function createDefensiveWindow(config: DefensiveWindowConfig): DefensiveWindow — Open a timed defensive window — block, parry, or i-frames — and test incoming hits against it.
|
|
249
249
|
- `createDotField` (function): function createDotField(): DotField — Builds an empty {@link DotField}; `apply` DoTs onto it and drain damage each frame with `tick`.
|
|
@@ -462,7 +462,7 @@
|
|
|
462
462
|
- `TargetProfile` (interface): interface TargetProfile — What a target presents to the table: one resistance class and its armor points.
|
|
463
463
|
- `TimeToKill` (interface): interface TimeToKill — A quoted time to kill for one fixed hit against one health pool.
|
|
464
464
|
- `UnknownTargetProfileError` (class): class UnknownTargetProfileError extends Error — Raised when a hit names a target that was never registered — a wiring error, not a 0-damage hit.
|
|
465
|
-
- `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet.
|
|
465
|
+
- `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet. Every application — a hit or a status tick — is announced as `combat.damage` carrying the target, source, type, amount, absorbed, remaining health and a lethal flag, so a HUD names the blow instead of guessing.
|
|
466
466
|
|
|
467
467
|
## @jgengine/core/combat/damageTable
|
|
468
468
|
|
|
@@ -501,7 +501,7 @@
|
|
|
501
501
|
|
|
502
502
|
## @jgengine/core/combat/deathReason
|
|
503
503
|
|
|
504
|
-
- `DeathReason` (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; via?: { item?: string } } | { kind: "environment"; source: string } | { kind: "self"; source: string } — Why an entity died — who or what gets credit, for drop/command rules and the `entity.died` event.
|
|
504
|
+
- `DeathReason` (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; source?: string; via?: { item?: string } } | { kind: "environment"; source: string; attacker?: string } | { kind: "self"; source: string } — Why an entity died — who or what gets credit, for drop/command rules and the `entity.died` event. `source` names what dealt it: a player kill's killer instance id, or the hazard/item of an environment death; `attacker` carries the non-player entity's instance id when one dealt it.
|
|
505
505
|
|
|
506
506
|
## @jgengine/core/combat/defensiveWindow
|
|
507
507
|
|
|
@@ -541,6 +541,7 @@
|
|
|
541
541
|
- `AreaTarget` (interface): interface AreaTarget { instanceId: string; scale: number } — ⚠ undocumented
|
|
542
542
|
- `AreaTargetInput` (interface): interface AreaTargetInput { at: EntityPosition; radius: number; falloff?: "linear" | "none"; los?: boolean } — ⚠ undocumented
|
|
543
543
|
- `CombatSpatialDeps` (interface): interface CombatSpatialDeps { inRadius(center: EntityPosition, radius: number): string[]; hasLineOfSight(from: EntityPosition | string, to: string): boolean; positionOf(instanceId: string): EntityPosition | undefined } — ⚠ undocumented
|
|
544
|
+
- `EffectDamageApplication` (interface): interface EffectDamageApplication — One effect application that drained a target's pools, as an announcer needs it. The effect path has no damage table, so the caller names the channel (`effect`) and reads health its own way.
|
|
544
545
|
- `EffectInput` (type): type EffectInput = SingleTargetEffectInput | AreaEffectInput — ⚠ undocumented
|
|
545
546
|
- `EffectResult` (interface): interface EffectResult { instanceId: string; effect: string; applied: AppliedPoolDelta[]; lethal: boolean; slain?: SlainIdentity } — ⚠ undocumented
|
|
546
547
|
- `EffectSystem` (interface): interface EffectSystem { canReceive(instanceId: string, effect: string, magnitude?: number): string | null; preview(input: SingleTargetEffectInput): number; applyEffect(input: EffectInput): EffectResult[] } — ⚠ undocumented · used by `createEffectSystem`: Resolve direct or area resource effects through caller-owned stat pools, including ordered absorption, restoration, modifiers, and lethal co…
|
|
@@ -49,6 +49,8 @@ AoE: `inRadius(at, radius)` → LoS filter (default on) → `canReceive` per tar
|
|
|
49
49
|
|
|
50
50
|
Each per-target `EffectResult` carries `{ instanceId, effect, applied, lethal }`. On a **lethal** hit it additionally carries `slain: { catalogId, name?, userId? }` — the victim's identity captured **before** the death system despawns it, so kill credit / XP reads the slain kind straight off the call result without a spawn-time id→kind registry (a non-lethal hit omits `slain`). This is the synchronous complement to the `entity.died` event: use `slain` when the code awarding the kill is the same code that fired the effect; use `entity.died` for decoupled listeners (quests, feeds, leaderboards).
|
|
51
51
|
|
|
52
|
+
Every draining application also raises one `combat.damage` (`DamageAppliedEvent`) on `ctx.game.events` per target it lands on: `source` is the effect's `from`, `type` is the effect id, `amount` is the total drained, `absorbed` is the incoming magnitude the receive modifiers and depleted pools stopped, and `remaining`/`lethal` read the **last** stat in the receive order — the pool whose exhaustion counts as death. A restorative application (`via: { amount: -n }`) raises no damage event.
|
|
53
|
+
|
|
52
54
|
`canReceive`'s optional signed `magnitude` makes the check direction-aware: positive (or omitted) keeps the standard `"pools-depleted"` check; negative checks the opposite direction and returns `"pools-full"` only when every stat in the receive order is already at max — so a heal (`via: { amount: -n }`) correctly reaches a fully-depleted target and is rejected only when there's nothing left to restore.
|
|
53
55
|
|
|
54
56
|
**Projectiles** (aim-based — no target ids):
|
|
@@ -145,6 +147,8 @@ Genre systems layered over the same effects/projectiles/targeting/loot primitive
|
|
|
145
147
|
|
|
146
148
|
`createDamageRuntime` (`combat/damageRuntime`) is the stateful in-process composition of the table, the zones, the armor curve, and carried statuses. A number reads as `base × zone multiplier × type multiplier × (1 − armor mitigation)`; `hit()` returns every stage, `tick()` ticks statuses through the same table and armor, `timeToKill()` quotes a fight, and `table()` reads the whole chart. `snapshot()`/`restore()` carry registered targets and carried statuses; `retune()` swaps table, zones, or definitions without dropping either. For multiplayer authority, keep the pure stages (`resolveMatchup`, `resolveReceivedDamage`, `resolveStatusApplication`) and replicate their results instead.
|
|
147
149
|
|
|
150
|
+
With `events: ctx.game.events` and `healthOf`, every hit and status tick raises one `combat.damage` carrying the table `type`, the final `amount`, what resistance stopped (`absorbed`), and `remaining`/`lethal`. The runtime and the effect path each announce their own application; neither calls the other, so a game that uses both gets one event per blow, not two. Prefer the runtime for damage that needs types, zones, armor or time-to-kill; the effect path stays for pools drained directly through the entity catalog's `receive` rules and now announces the same event with the effect id as `type`.
|
|
151
|
+
|
|
148
152
|
- **Type × resistance table** (`combat/damageTable`) — `{ types: [{ id, vs: { armor: 2, flesh: 0.5 }, bypassArmor? }], armor: { halfValue, maxMitigation }, defaultMultiplier? }`. `typeMultiplier` reads one pair; `resolveTableDamage` folds type, armor, and base into a row; `readDamageTable` returns every row at given armor values and `formatDamageTable` prints them. Armor uses `armor / (armor + halfValue)` — armor equal to `halfValue` halves the hit — so a designer reads the number rather than guessing a percentage per point. Unknown types throw; unknown resistance classes use the default.
|
|
149
153
|
- **Hit zones and crits** (`combat/hitZones`) — `{ zones: [{ id, multiplier?, critChance?, critMultiplier? }], defaultZone, critChance?, critMultiplier? }`. `resolveHitZone(table, zone, rng)` rolls once and returns **one** multiplier: a crit replaces the zone's normal multiplier (head 1.5× normal, 2.5× crit → 2.5×, never 3.75×). `hitZoneMultiplier(table, zone, crit)` quotes the same number without a roll, which is what `timeToKill({ crit: true })` uses.
|
|
150
154
|
- **Statuses** (`combat/statusRuntime`) — `createStatusRuntime({ statuses, rng?, applyDamage? })`; each `StatusDef` declares `{ id, stack, durationMs, intervalMs?, magnitude?, effect?, chance? }`. `stack` is the existing `StatusStackPolicy` (`refresh` / `stack` to `max` / `replace` / `ignore`), and it is the only thing that changes stacks: a tick deals `magnitude × stacks`, so two burns at 4 per tick deal 8, never 16. `effect` is `{ kind: "damage", channel }` (routed through `applyDamage`, so resistances and armor apply) or `{ kind: "stat", stat }` (read with `statDelta`, e.g. negative `moveSpeed` for slow or negative `armor` for corrode). Reapplication restarts the tick clock instead of banking a burst; ticks per call are bounded and the remainder carries. `dotField` stays the cheap no-stacking scheduler.
|
|
@@ -38,10 +38,60 @@ Pins consume no choice RNG and must satisfy the same pool and constraints;
|
|
|
38
38
|
incompatible pins backtrack earlier unpinned choices or return `unsatisfiable`
|
|
39
39
|
within the configured attempt and pick budgets.
|
|
40
40
|
|
|
41
|
+
Roll whole named gear from an authored catalog with `rollLoot`
|
|
42
|
+
(`loot/lootGen`): bases with mount slots, parts, affix pools, weighted rarity
|
|
43
|
+
tiers, manufacturer stat personalities and a level curve, all caller data.
|
|
44
|
+
`validateLootItem` names each defect, `compareLootItems` returns per-stat deltas
|
|
45
|
+
and a weighted score, and `lootWeaponDefinition` (`loot/lootWeapon`) projects a
|
|
46
|
+
rolled item onto a `WeaponDefinition` for `createWeaponSystem`. `createLootRoller`
|
|
47
|
+
counts drops per rarity for a debug probe and snapshots its counters. Sample
|
|
48
|
+
catalog, distribution check and generated-weapon proof are in
|
|
49
|
+
[reference-systems.md](reference-systems.md#procedural-gear).
|
|
50
|
+
|
|
51
|
+
Resolve an authored XP curve with `defineXpCurve` (`progression/xpCurve`): one growth rule
|
|
52
|
+
(`flat`, `linear`, `power`, `geometric`, `table`), a `maxLevel` cap and a stated `rule` string;
|
|
53
|
+
`table()` and `totalXpFor()` read the same data, and `track()` hands those thresholds to
|
|
54
|
+
`leveling`. Spend a character's points in `createSkillTree` (`progression/skillTree`): nodes carry
|
|
55
|
+
a point `cost`, `requires`, `excludes`, `passives` and `actives`, `synergies` pay off two or more
|
|
56
|
+
ranked nodes together, `respec()` refunds exactly the points spent, and `resolved()` returns the
|
|
57
|
+
`StatModifierSet` plus ability-slot-shaped actives to feed `createStats` / `createAbilityKit`.
|
|
58
|
+
Score a whole loadout with `gearScore` (`progression/gearScore`) on the same weight policy as
|
|
59
|
+
`compareLootItems`. [reference-systems.md](reference-systems.md#progression-and-gear-score) shows
|
|
60
|
+
the data shapes and a two-build comparison.
|
|
61
|
+
|
|
62
|
+
Wear gear with `createEquipment` (`inventory/equipment`): slot defs own accepted kinds and the slots
|
|
63
|
+
they block, item defs own the slots an item may occupy, a filled slot swaps and hands back the
|
|
64
|
+
replaced item, and `state()` / `restore()` round-trip the worn set. Weigh a character with
|
|
65
|
+
`characterLoad` (`inventory/carryLoad`), which turns carried inventories plus worn gear into the
|
|
66
|
+
shared encumbrance bands (`moveMultiplier`, sprint/jump gates, `remaining`), so `canCarry` gates a
|
|
67
|
+
pickup. Declare a stash as its own inventory (`tier: "banked"`) and move items with
|
|
68
|
+
`InventorySet.move`; `snapshot()` / `restore()` save both as detached data. The bag and stash render
|
|
69
|
+
through `<InventoryGrid>` with `<EquipmentSlots>` above it. See
|
|
70
|
+
[inventory, equipment and weight](reference-systems.md#inventory-equipment-and-weight).
|
|
71
|
+
|
|
41
72
|
Validate generated quest catalogs with `validateQuestCatalog` (`game/questCatalog`).
|
|
42
73
|
Follow the [quest authoring recipe](recipes/quest-authoring.md) for bounded batches,
|
|
43
74
|
canonical facts, external unlock declarations, and precise repair diagnostics.
|
|
44
75
|
|
|
76
|
+
Run a mission with `createMissionRuntime` (`mission/mission`): a mission is data — a graph of
|
|
77
|
+
objectives the machine walks, with named fail states, per-objective timers, conditional branches and
|
|
78
|
+
rewards. `escortMission`, `chaseMission` and `defendMission` (`mission/compositions`) are compositions
|
|
79
|
+
of the same `reach`/`interact`/`survive`/`pursue`/`kill`/`collect` kinds, not archetypes: an escort is
|
|
80
|
+
a `reach` with an `actor-died` guard, a defend is a `survive` with a survivor floor, a chase is a
|
|
81
|
+
`pursue` with an escape guard. Start, advance and fail missions from authored scene markers with
|
|
82
|
+
`registerMissionTriggerActions` and `createMissionTriggerBridge` (`mission/triggers`); positional
|
|
83
|
+
objectives read `missionMarkers(document)`. Rewards route through the quest appliers and the loot
|
|
84
|
+
registry (`applyMissionRewards`). `validateMissionCatalog` is the content gate and it fails a mission
|
|
85
|
+
whose success path is counters alone — `createMissionRuntime` runs it at construction, so a
|
|
86
|
+
counter-only mission throws instead of shipping.
|
|
87
|
+
|
|
88
|
+
Report what happened instead of polling your own counters: `tick(dt, { events })` takes
|
|
89
|
+
`MissionWorldEvent`s (`kill`/`collect`/`deliver` with a target, actor and tags), an objective's `match`
|
|
90
|
+
filter credits only matching ones - a decoy, a wrong weapon or a rival's kill does not advance it - and
|
|
91
|
+
a `failOn` of kind `event` or `left-area` ends the run on a matching event or on an actor leaving an
|
|
92
|
+
authored marker. `view()` is everything a HUD needs: label, `remaining`, `counted`/`count`,
|
|
93
|
+
`step`/`steps`, `outcome` and `reason`.
|
|
94
|
+
|
|
45
95
|
For cross-catalog sources and skill gates, compose `validateContentReferences` and
|
|
46
96
|
`validateContentProgression` (`game/contentValidation`) with `validateRecipeCatalog`
|
|
47
97
|
(`crafting/recipeCatalog`). Follow [content validation](reference-content-validation.md).
|
|
@@ -71,6 +121,10 @@ Prefer `defineSystem` and `defineGame({ systems })` for scheduled capabilities.
|
|
|
71
121
|
|
|
72
122
|
`createNearAgents` (`sim/nearAgents`) is the default near half: a nav crowd with a need set and a utility brain per agent, counting its decisions for tests and probes. Give it the same need definitions the off-screen world decays, or the two halves disagree at the boundary.
|
|
73
123
|
|
|
124
|
+
`createVisitorPopulation` (`sim/population`) is the crowd a scene reports, and it is the bodies: a visitor joins only when your `VisitorWorld.admit` has made its body, and only while `VisitorWorld.has` holds it, so `onSite()` is the member count a HUD reads in O(1) and `arrivalsToday()` is the sum of the admissions the world accepted rather than a tally kept beside them. Arrival rate, capacity (a number or a function of the clock), the leaving rule and the `inside` bounds check are game policy; a refused body or one placed outside the bounds is released and never counted. `rollDay()` closes the day without emptying the park, `adopt` takes over a body a save or a seed made, and a restore only brings back members the world still holds.
|
|
125
|
+
|
|
126
|
+
Rides and their lines are `createRideSystem` and `createWaitQueue` (`sim/rides`, `sim/queue`): a ride cycles load → ride → unload at its own capacity, wears against its own breakdown curve, and reports boards, alights, breakdowns and repair costs through a sink; the wait a guest faces comes from the queue's own measured boarding throughput, not a configured constant. Guest choice stays on the utility brain — `rideChoiceOptions` compiles a guest's `RideTaste` (intensity comfort, nausea comfort, queue tolerance) and the park's ratings into options, `rideChoiceCandidates` costs each one by its own line's wait, and `createRideChooser` wires both to one guest. Ratings, cycle times, breakdown curves and costs are caller data, and `retune` changes them in play.
|
|
127
|
+
|
|
74
128
|
For staffed production, use `stationOutputRate` (`work/staffedStation`) to resolve
|
|
75
129
|
caller-owned worker stats and efficiency, then feed the rate to `tickProduction`.
|
|
76
130
|
Keep staffing and production progress in the same serializable store; the
|
|
@@ -120,6 +174,38 @@ A character kit-bashed from primitives/`ModelPart`s (no skeleton, no clips) anim
|
|
|
120
174
|
- `ctx.game.chat.send(userId, text)` targets global chat; `recent({ limit })` returns at most 100 messages. The default is 240 characters and one message per author per channel every two seconds. Rejected messages need visible UI feedback.
|
|
121
175
|
- Hosts reuse `validateChatMessage` and `decideRateWindow`, storing the rate window transactionally with each accepted operation. Preserve chat rate windows when saving/restoring chat state.
|
|
122
176
|
|
|
177
|
+
Price goods with `createMarket` (`economy/market`): `price = clamp(basePrice × (coverPeriods × demand / max(stock, 1))^elasticity)`
|
|
178
|
+
between `basePrice × minFactor` and `basePrice × maxFactor`, over finite integer
|
|
179
|
+
stock. `sell` refuses a sale it cannot cover and names the shortfall, `restock`
|
|
180
|
+
lowers the price, `recordDemand(unitsSold, unitsUnmet)` moves demand from what
|
|
181
|
+
buyers actually took, and `priceCurve`/`retune` change the policy. Amounts are
|
|
182
|
+
integer minor units, so a long simulation never drifts.
|
|
183
|
+
|
|
184
|
+
Keep the books with `createTransactionLedger` (`economy/transactionLedger`):
|
|
185
|
+
per-account balances in integer minor units plus a journal recording every
|
|
186
|
+
movement, the balance it produced and a caller-owned `reason`. `post` is the one
|
|
187
|
+
money path, and `totals()` keeps a per-reason count and net past the `maxEntries`
|
|
188
|
+
journal cap. Register upkeep, rent or a subscription with `addCharge` and settle
|
|
189
|
+
it on its cadence with `chargeDue` — bounded per charge and per advance, and a
|
|
190
|
+
cycle with no funds is counted as missed rather than overdrawing.
|
|
191
|
+
|
|
192
|
+
Lend and borrow with `buildLoanSchedule` / `createLoanBook` (`economy/loanBook`):
|
|
193
|
+
a schedule in integer minor units (`"amortized"` or `"flat"`), the principal
|
|
194
|
+
posted on the draw, interest and principal posted as separate reason-tagged
|
|
195
|
+
entries, and arrears that count once per advance until the loan defaults at
|
|
196
|
+
`maxMissed`. `repay` pays principal early; a save stores terms and progress and
|
|
197
|
+
the schedule is rebuilt on restore.
|
|
198
|
+
|
|
199
|
+
One document saves the whole world. `createGameSave` (`game/gameSave`) holds one versioned
|
|
200
|
+
`GameSaveDocument` with a section per registered system, named slots, caller-policy autosave and a
|
|
201
|
+
`migrate` path, over the same injected `SaveBackend` `createSaveStore` uses. Register each stateful
|
|
202
|
+
system once by `name` with its `snapshot()`/`restore()`; `capture()` builds the document,
|
|
203
|
+
`save()`/`touch()` persist it, `load()`/`switchSlot()` restore every section, and
|
|
204
|
+
`digest()`/`digests()` hash the state for a save/reload determinism test. Pass `rng: { seed, stream }`
|
|
205
|
+
so the world's shared seed and cursor survive the save. A snapshot field that means "never" must
|
|
206
|
+
serialize as `null` and be mapped back on restore - `Infinity` becomes `null` through JSON, which
|
|
207
|
+
silently changes a restored run.
|
|
208
|
+
|
|
123
209
|
For timed away missions, compose the pure transitions in `work/expedition` with
|
|
124
210
|
your existing `defineStore` and clock. The [expedition recipe](recipes/expeditions.md)
|
|
125
211
|
keeps bounded offline catch-up, finite supplies, return/death, capacity and RNG
|