@jgengine/core 0.25.0 → 0.27.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 +109 -0
- package/dist/ai/rvo.d.ts +106 -0
- package/dist/ai/rvo.js +581 -0
- package/dist/ai/utilityBrain.d.ts +173 -0
- package/dist/ai/utilityBrain.js +277 -0
- package/dist/combat/cover.d.ts +169 -0
- package/dist/combat/cover.js +264 -0
- package/dist/combat/damageRuntime.d.ts +150 -0
- package/dist/combat/damageRuntime.js +208 -0
- package/dist/combat/damageTable.d.ts +116 -0
- package/dist/combat/damageTable.js +165 -0
- package/dist/combat/hitZones.d.ts +70 -0
- package/dist/combat/hitZones.js +77 -0
- package/dist/combat/magazine.d.ts +8 -0
- package/dist/combat/magazine.js +20 -2
- package/dist/combat/squadCombat.d.ts +167 -0
- package/dist/combat/squadCombat.js +557 -0
- package/dist/combat/squadTactics.d.ts +73 -0
- package/dist/combat/squadTactics.js +49 -0
- package/dist/combat/statusRuntime.d.ts +142 -0
- package/dist/combat/statusRuntime.js +236 -0
- package/dist/combat/weaponDefinition.d.ts +106 -0
- package/dist/combat/weaponDefinition.js +108 -0
- package/dist/combat/weaponSystem.d.ts +189 -0
- package/dist/combat/weaponSystem.js +469 -0
- package/dist/combat.d.ts +9 -0
- package/dist/combat.js +9 -0
- package/dist/devtools/devtools.d.ts +4 -2
- package/dist/devtools/devtools.js +2 -1
- package/dist/devtools/discover.d.ts +2 -1
- package/dist/devtools/discover.js +38 -0
- package/dist/devtools/frame.d.ts +0 -2
- package/dist/devtools/frame.js +1 -2
- package/dist/devtools/logBus.d.ts +58 -0
- package/dist/devtools/logBus.js +79 -0
- package/dist/devtools/logs.d.ts +6 -3
- package/dist/devtools/logs.js +17 -13
- package/dist/devtools/report.d.ts +120 -0
- package/dist/devtools/report.js +49 -0
- package/dist/devtools/reportEndpoint.d.ts +17 -0
- package/dist/devtools/reportEndpoint.js +7 -0
- package/dist/devtools/types.d.ts +48 -11
- package/dist/editor/document.js +210 -0
- package/dist/editor/environment.d.ts +48 -1
- package/dist/editor/environment.js +161 -0
- package/dist/editor/index.d.ts +6 -2
- package/dist/editor/index.js +1 -1
- package/dist/editor/types.d.ts +54 -0
- package/dist/game/playableGame.d.ts +19 -2
- package/dist/gameplay.d.ts +9 -0
- package/dist/gameplay.js +9 -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 +92 -1
- package/dist/nav/laneGraph.d.ts +178 -0
- package/dist/nav/laneGraph.js +671 -0
- package/dist/nav/navCrowd.d.ts +138 -0
- package/dist/nav/navCrowd.js +412 -0
- package/dist/nav/navRuntime.d.ts +108 -0
- package/dist/nav/navRuntime.js +256 -0
- package/dist/nav/traffic.d.ts +223 -0
- package/dist/nav/traffic.js +919 -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/render/colorPipeline.d.ts +53 -0
- package/dist/render/colorPipeline.js +79 -0
- package/dist/render/lightProbes.d.ts +97 -0
- package/dist/render/lightProbes.js +221 -0
- package/dist/render/lookPreset.js +4 -4
- package/dist/render/physicalSky.d.ts +39 -0
- package/dist/render/physicalSky.js +89 -0
- package/dist/render/postProcessing.d.ts +40 -8
- package/dist/render/postProcessing.js +1 -1
- package/dist/render/skyOwnedLighting.d.ts +31 -0
- package/dist/render/skyOwnedLighting.js +46 -0
- package/dist/scene/builtinSceneKinds.js +2 -0
- package/dist/sensor/perception.d.ts +66 -5
- package/dist/sensor/perception.js +167 -42
- package/dist/sim/nearAgents.d.ts +68 -0
- package/dist/sim/nearAgents.js +152 -0
- package/dist/sim/needs.d.ts +60 -0
- package/dist/sim/needs.js +108 -0
- package/dist/sim/offScreen.d.ts +181 -0
- package/dist/sim/offScreen.js +450 -0
- package/dist/sim/routine.d.ts +79 -0
- package/dist/sim/routine.js +137 -0
- package/dist/visibility/visibilitySystem.js +21 -0
- package/dist/world/decalKind.d.ts +48 -0
- package/dist/world/decalKind.js +86 -0
- package/dist/world/features.d.ts +11 -1
- 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.d.ts +10 -1
- package/dist/world.js +9 -0
- package/package.json +1 -1
- package/skills/jgengine/api.md +59 -6
- package/skills/jgengine/capabilities.md +4 -0
- package/skills/jgengine-combat/SKILL.md +19 -0
- package/skills/jgengine-combat/api.md +246 -0
- package/skills/jgengine-combat/capabilities.md +112 -0
- package/skills/jgengine-combat/reference.md +32 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +221 -7
- package/skills/jgengine-gameplay/capabilities.md +64 -3
- package/skills/jgengine-gameplay/reference-systems.md +118 -0
- package/skills/jgengine-world/SKILL.md +7 -1
- package/skills/jgengine-world/api.md +222 -8
- package/skills/jgengine-world/capabilities.md +42 -2
- package/skills/jgengine-world/recipes/npc-senses.md +8 -2
- package/skills/jgengine-world/recipes/vehicle-feel.md +5 -0
|
@@ -36,12 +36,15 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
36
36
|
|
|
37
37
|
- `cancelJob` (function) · `import { cancelJob } from "@jgengine/core/gameplay"`
|
|
38
38
|
|
|
39
|
-
## carry-encumbrance —
|
|
39
|
+
## carry-encumbrance — gate a pickup against the carrier's remaining capacity
|
|
40
40
|
|
|
41
|
+
- `canCarry` (function) · `import { canCarry } from "@jgengine/core/inventory/carryLoad"`
|
|
42
|
+
- `characterLoad` (function) · `import { characterLoad } from "@jgengine/core/inventory/carryLoad"`
|
|
41
43
|
- `resolveEncumbrance` (function) · `import { resolveEncumbrance } from "@jgengine/core/inventory/encumbrance"`
|
|
42
44
|
|
|
43
|
-
## carry-load —
|
|
45
|
+
## carry-load — read carried stacks out of one or more inventories for a weight query
|
|
44
46
|
|
|
47
|
+
- `carriedEntries` (function) · `import { carriedEntries } from "@jgengine/core/inventory/carryLoad"`
|
|
45
48
|
- `totalLoad` (function) · `import { totalLoad } from "@jgengine/core/inventory/encumbrance"`
|
|
46
49
|
|
|
47
50
|
## cell-grid — immutable 2D board of cells with row clears, gravity collapse, and match runs
|
|
@@ -148,6 +151,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
148
151
|
|
|
149
152
|
- `enqueue` (function) · `import { enqueue } from "@jgengine/core/gameplay"`
|
|
150
153
|
|
|
154
|
+
## equipment-slot-inventory — wear gear by slot with item-kind rules, two-handed blocking and replace-on-equip
|
|
155
|
+
|
|
156
|
+
- `createEquipment` (function) · `import { createEquipment } from "@jgengine/core/inventory/equipment"`
|
|
157
|
+
|
|
151
158
|
## event-bus — typed publish/subscribe bus for gameplay events
|
|
152
159
|
|
|
153
160
|
- `createGameEvents` (function) · `import { createGameEvents } from "@jgengine/core/gameplay"`
|
|
@@ -188,6 +195,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
188
195
|
|
|
189
196
|
- `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/core/gameplay"`
|
|
190
197
|
|
|
198
|
+
## gear-score — score a loadout from stat weights, level, rarity and slot coverage
|
|
199
|
+
|
|
200
|
+
- `gearScore` (function) · `import { gearScore } from "@jgengine/core/gameplay"`
|
|
201
|
+
|
|
191
202
|
## group-credits — turn a flat list of credited things into ordered credit sections
|
|
192
203
|
|
|
193
204
|
- `groupCredits` (function) · `import { groupCredits } from "@jgengine/core/game/credits"`
|
|
@@ -212,7 +223,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
212
223
|
|
|
213
224
|
- `createEmptyInventory` (function) · `import { createEmptyInventory } from "@jgengine/core/inventory/inventoryModel"`
|
|
214
225
|
|
|
215
|
-
## inventory-split — split a stack into a target or first-empty slot
|
|
226
|
+
## inventory-split — split a stack into a target or first-empty slot, honoring a stack limit
|
|
216
227
|
|
|
217
228
|
- `splitStack` (function) · `import { splitStack } from "@jgengine/core/inventory/inventoryModel"`
|
|
218
229
|
|
|
@@ -287,20 +298,45 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
287
298
|
|
|
288
299
|
- `RarityStyle` (interface) · `import { RarityStyle } from "@jgengine/core/gameplay"`
|
|
289
300
|
|
|
301
|
+
## loot-comparison — compare two rolled items with per-stat deltas and a weighted score
|
|
302
|
+
|
|
303
|
+
- `compareLootItems` (function) · `import { compareLootItems } from "@jgengine/core/gameplay"`
|
|
304
|
+
|
|
290
305
|
## loot-filter — filter and highlight drops by rarity/name rules
|
|
291
306
|
|
|
292
307
|
- `evaluateLootFilter` (function) · `import { evaluateLootFilter } from "@jgengine/core/gameplay"`
|
|
293
308
|
|
|
309
|
+
## loot-generation — roll a named item from bases, parts, affixes, rarity weights, manufacturer personalities and level scaling
|
|
310
|
+
|
|
311
|
+
- `rollLoot` (function) · `import { rollLoot } from "@jgengine/core/gameplay"`
|
|
312
|
+
|
|
294
313
|
## loot-pipeline — compose ordered loot pools with fallbacks, roll modifiers, and provenance
|
|
295
314
|
|
|
296
315
|
- `createLootPipeline` (function) · `import { createLootPipeline } from "@jgengine/core/gameplay"`
|
|
297
316
|
- `defineLootPipeline` (function) · `import { defineLootPipeline } from "@jgengine/core/gameplay"`
|
|
298
317
|
|
|
318
|
+
## loot-roller — roll items from one validated catalog while counting drops per rarity for a debug probe
|
|
319
|
+
|
|
320
|
+
- `createLootRoller` (function) · `import { createLootRoller } from "@jgengine/core/gameplay"`
|
|
321
|
+
|
|
322
|
+
## loot-score — split one stat block into per-stat weighted contributions
|
|
323
|
+
|
|
324
|
+
- `lootStatContributions` (function) · `import { lootStatContributions } from "@jgengine/core/gameplay"`
|
|
325
|
+
- `scoreLootStats` (function) · `import { scoreLootStats } from "@jgengine/core/gameplay"`
|
|
326
|
+
|
|
299
327
|
## loot-table — register loot tables and roll weighted randomized drops
|
|
300
328
|
|
|
301
329
|
- `createLootRegistry` (function) · `import { createLootRegistry } from "@jgengine/core/gameplay"`
|
|
302
330
|
- `lootTable` (function) · `import { lootTable } from "@jgengine/core/gameplay"`
|
|
303
331
|
|
|
332
|
+
## loot-validation — check a rolled item against its catalog and report each defect with its code
|
|
333
|
+
|
|
334
|
+
- `validateLootItem` (function) · `import { validateLootItem } from "@jgengine/core/gameplay"`
|
|
335
|
+
|
|
336
|
+
## loot-weapon — feed a rolled item into a weapon definition so generated gear fires through the weapon system
|
|
337
|
+
|
|
338
|
+
- `lootWeaponDefinition` (function) · `import { lootWeaponDefinition } from "@jgengine/core/gameplay"`
|
|
339
|
+
|
|
304
340
|
## maturation-stage — resolve the baby/juvenile/adolescent/adult stage from elapsed maturation
|
|
305
341
|
|
|
306
342
|
- `maturationStage` (function) · `import { maturationStage } from "@jgengine/core/game/breeding"`
|
|
@@ -329,6 +365,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
329
365
|
|
|
330
366
|
- `freezeNarrativeCanon` (function) · `import { freezeNarrativeCanon } from "@jgengine/core/game/narrativeAuthoring"`
|
|
331
367
|
|
|
368
|
+
## near-agents — nav crowd plus per-agent needs and utility brains for on-screen residents
|
|
369
|
+
|
|
370
|
+
- `createNearAgents` (function) · `import { createNearAgents } from "@jgengine/core/gameplay"`
|
|
371
|
+
|
|
372
|
+
## needs — decaying needs an agent reads by index to decide what it wants next
|
|
373
|
+
|
|
374
|
+
- `createNeedSet` (function) · `import { createNeedSet } from "@jgengine/core/gameplay"`
|
|
375
|
+
|
|
332
376
|
## notice-feed — serializable tone-tagged notice/event feed bounded by count and age (toasts, life events, killfeed)
|
|
333
377
|
|
|
334
378
|
- `appendFeed` (function) · `import { appendFeed } from "@jgengine/core/gameplay"`
|
|
@@ -343,6 +387,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
343
387
|
|
|
344
388
|
- `evaluateObjective` (function) · `import { evaluateObjective } from "@jgengine/core/gameplay"`
|
|
345
389
|
|
|
390
|
+
## offscreen-world — schedules agents near the player as real state and far ones as cheap state
|
|
391
|
+
|
|
392
|
+
- `createOffScreenWorld` (function) · `import { createOffScreenWorld } from "@jgengine/core/gameplay"`
|
|
393
|
+
|
|
346
394
|
## param-layers — register parameter layers by stable id and resolve serialized selections
|
|
347
395
|
|
|
348
396
|
- `createLayerRegistry` (function) · `import { createLayerRegistry } from "@jgengine/core/gameplay"`
|
|
@@ -470,6 +518,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
470
518
|
|
|
471
519
|
- `createVfxInstanceStore` (function) · `import { createVfxInstanceStore } from "@jgengine/core/gameplay"`
|
|
472
520
|
|
|
521
|
+
## routine-timeline — authored daily routines (home, work, meal) resolved against the world clock
|
|
522
|
+
|
|
523
|
+
- `resolveRoutine` (function) · `import { resolveRoutine } from "@jgengine/core/gameplay"`
|
|
524
|
+
|
|
473
525
|
## rule-selection — register rules and bridge a selection to its contributed parameter layers
|
|
474
526
|
|
|
475
527
|
- `createRuleRegistry` (function) · `import { createRuleRegistry } from "@jgengine/core/gameplay"`
|
|
@@ -507,11 +559,16 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
507
559
|
## shaped-placement — find a shaped inventory item's first fit with ordered rotations, row-major placement, and an explicit search budget
|
|
508
560
|
|
|
509
561
|
- `findShapedPlacement` (function) · `import { findShapedPlacement } from "@jgengine/core/inventory/shapedGrid"`
|
|
562
|
+
- `fitShapedItem` (function) · `import { fitShapedItem } from "@jgengine/core/inventory/shapedGrid"`
|
|
510
563
|
|
|
511
564
|
## shop-grid — serializable vendor/shop stock with buy/sell over a caller-owned wallet — finite/unlimited qty, sell-back, restock/setPrice, snapshot/restore
|
|
512
565
|
|
|
513
566
|
- `createShopStock` (function) · `import { createShopStock } from "@jgengine/core/gameplay"`
|
|
514
567
|
|
|
568
|
+
## skill-tree — spend, refuse and refund points over an authored tree with passives, actives and synergies
|
|
569
|
+
|
|
570
|
+
- `createSkillTree` (function) · `import { createSkillTree } from "@jgengine/core/gameplay"`
|
|
571
|
+
|
|
515
572
|
## social-emotes — emotes and social interactions between nearby players
|
|
516
573
|
|
|
517
574
|
- `createSocial` (function) · `import { createSocial } from "@jgengine/core/gameplay"`
|
|
@@ -644,3 +701,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
644
701
|
## work-queued-jobs — list jobs waiting for a slot, in selection order
|
|
645
702
|
|
|
646
703
|
- `queuedJobs` (function) · `import { queuedJobs } from "@jgengine/core/gameplay"`
|
|
704
|
+
|
|
705
|
+
## xp-curve — resolve an authored xp growth rule into per-level thresholds, totals and a level cap
|
|
706
|
+
|
|
707
|
+
- `defineXpCurve` (function) · `import { defineXpCurve } from "@jgengine/core/gameplay"`
|
|
@@ -28,6 +28,124 @@ and level state, then emits every reached level in ascending order. State stays
|
|
|
28
28
|
in the caller's save format and round-trips as ordinary JSON. See the
|
|
29
29
|
[portable XP/leveling recipe](recipes/portable-xp-leveling.md).
|
|
30
30
|
|
|
31
|
+
## Procedural gear
|
|
32
|
+
|
|
33
|
+
`loot/lootGen` rolls a whole named item from a caller-authored `LootCatalog`:
|
|
34
|
+
bases (`baseStats`, affix `pools`, mount `slots`), parts, affix pools, weighted
|
|
35
|
+
`RarityTier`s, `LootManufacturerDef`s (name, flat `bias`, per-stat `scale`,
|
|
36
|
+
favored pools) and a `levelScale` curve of `[level, multiplier]` points. A roll
|
|
37
|
+
picks maker, base, rarity, one part per slot and `affixCount` affixes; stats are
|
|
38
|
+
`base × rarity.statScale × levelScale`, plus part stats/multipliers, maker
|
|
39
|
+
personality, then additive affixes and multiplicative ones. Additive affix
|
|
40
|
+
rolls scale with level; multiplicative ones do not. Names follow one rule:
|
|
41
|
+
maker, rarity word, first part word, first prefix affix, base name, first suffix
|
|
42
|
+
affix. Every random draw uses the injected `rng`, so a seed reproduces the item.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { compareLootItems, createLootRoller, lootWeaponDefinition, seededRng } from "@jgengine/core/gameplay";
|
|
46
|
+
|
|
47
|
+
const roller = createLootRoller(catalog, { devtoolsName: "loot" });
|
|
48
|
+
const item = roller.roll(seededRng(dropSeed), { level: 30 });
|
|
49
|
+
const delta = compareLootItems(item, equipped, { lowerIsBetter: ["reload"] });
|
|
50
|
+
const weapon = lootWeaponDefinition(item, { template, stats: { damage: "power", roundsPerMinute: "cadence" } });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`validateLootItem` returns coded issues (`unknown-base`, `part-category`,
|
|
54
|
+
`missing-slot-part`, `unknown-affix`, `empty-name`…) and the roller's counters
|
|
55
|
+
snapshot/restore as plain data. Register a roll through
|
|
56
|
+
`proceduralLootEntry` into `item/itemInstanceRegistry` and hand its runtime id
|
|
57
|
+
to `game/lootTable`; the weapon projection only maps stats it is told to, so a
|
|
58
|
+
catalog without weapon stats stays usable for non-weapon gear. Check a
|
|
59
|
+
distribution with a fixed seed over 10,000 drops against each tier's 4σ
|
|
60
|
+
binomial bound rather than eyeballing shares.
|
|
61
|
+
|
|
62
|
+
## Progression and gear score
|
|
63
|
+
|
|
64
|
+
`progression/xpCurve` turns one authored growth rule into the level table, the cap and a
|
|
65
|
+
plain-words rule line, so the balance lives in save-visible data:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { createSkillTree, defineXpCurve, gearScore } from "@jgengine/core/gameplay";
|
|
69
|
+
|
|
70
|
+
const xp = defineXpCurve({ growth: "power", step: 100, growthRate: 1.5, maxLevel: 40, round: "floor" });
|
|
71
|
+
// rule: "power: 100 x level^1.5, floor (levels 1-40)"; xpForLevel(2) is 282, xpForLevel(40) is 0
|
|
72
|
+
xp.levelForXp(xp.totalXpFor(7)).level; // 7
|
|
73
|
+
xp.track(); // the same thresholds as a `leveling` track for a stat adapter
|
|
74
|
+
|
|
75
|
+
const tree = createSkillTree({ nodes, synergies, points: 6 }, { devtoolsName: "skills" });
|
|
76
|
+
tree.spend("strike"); // { ok: true, spent: 1, pointsAvailable: 5 }
|
|
77
|
+
tree.canSpend("sight"); // { ok: false, reason: "requires", detail: '...needs rank 1 of "focus"' }
|
|
78
|
+
const { stats, actives, synergies: live } = tree.resolved();
|
|
79
|
+
tree.respec(); // { refunded: 6, pointsAvailable: 6 }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A `SkillNodeDef` is `{ id, branch?, cost?, maxRank?, requires?, requiresPointsInBranch?,
|
|
83
|
+
excludes?, passives?, actives?, tags? }`; a `SkillSynergyDef` lists at least two `requires` and
|
|
84
|
+
adds passives/actives only while they all hold. `excludes` is symmetric, so two nodes named as
|
|
85
|
+
alternatives block each other in both directions. Actives are `{ id, cooldownMs?, resourceCost?,
|
|
86
|
+
castType?, groups? }`, shaped to bind into `createAbilityKit` slots. Snapshot/restore carries
|
|
87
|
+
`{ points, ranks }`; restore refuses unknown nodes, over-ranked nodes and negative points without
|
|
88
|
+
mutating. A tree where breadth beats depth is the point: in the test fixture, four points down
|
|
89
|
+
`strike` give `damage +3`, while the same four points in `strike x2`, `focus`, `sight` give
|
|
90
|
+
`damage +4` plus the `overload` synergy and its action.
|
|
91
|
+
|
|
92
|
+
`gearScore(set, options)` scores `slot -> { stats, level?, rarity? }` as
|
|
93
|
+
`Σ_stat value × weight × (lowerIsBetter ? -1 : 1) + levelWeight × level + rarityScore[rarity]`,
|
|
94
|
+
summed over filled slots, minus `emptySlotPenalty` per expected-but-empty slot; it also returns
|
|
95
|
+
`bySlot`, `byStat`, `average`, `coverage` and `emptySlots`. `compareGearSets` ranks two loadouts.
|
|
96
|
+
The stat term comes from `lootStatContributions`, the same function behind `scoreLootStats` and
|
|
97
|
+
`compareLootItems`, so per-item and whole-loadout scores cannot drift apart.
|
|
98
|
+
|
|
99
|
+
## Inventory, equipment and weight
|
|
100
|
+
|
|
101
|
+
A carried bag and a stash are two declared inventories, so they hold separate plain state from the
|
|
102
|
+
first frame and persist as `InventorySet.snapshot()`:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { createInventorySet } from "@jgengine/core/inventory/inventoryModel";
|
|
106
|
+
import { canCarry, characterLoad } from "@jgengine/core/inventory/carryLoad";
|
|
107
|
+
import { createEquipment } from "@jgengine/core/inventory/equipment";
|
|
108
|
+
|
|
109
|
+
const items = createInventorySet({ bag: { slots: 24 }, stash: { slots: 60 } }, traits);
|
|
110
|
+
items.move("bag", 3, "stash", 0); // across inventories; a rejected move leaves both untouched
|
|
111
|
+
const saved = items.snapshot(); // detached; JSON-safe; restore() refuses a bad save without mutating
|
|
112
|
+
|
|
113
|
+
const gear = createEquipment(
|
|
114
|
+
[{ id: "weapon", blocks: ["offhand"] }, { id: "offhand" }, { id: "chest", accepts: ["armor"] }],
|
|
115
|
+
(itemId) => baseDefs[registry.get(itemId)?.baseId ?? itemId]?.gear, // rolled item -> its base's gear facts
|
|
116
|
+
{ devtoolsName: "gear" },
|
|
117
|
+
);
|
|
118
|
+
gear.equip("chest", "plate"); // { status: "ok", replaced: null, ... }
|
|
119
|
+
gear.equipBest("greatsword"); // first free preferred slot, else the first one it may replace
|
|
120
|
+
|
|
121
|
+
const load = characterLoad({
|
|
122
|
+
carried: [items.state("bag")],
|
|
123
|
+
worn: gear.worn().map((entry) => entry.itemId),
|
|
124
|
+
weightOf: (itemId) => baseDefs[itemId]?.weight ?? 0,
|
|
125
|
+
capacity: 60 + stats.value("carryCapacity"),
|
|
126
|
+
});
|
|
127
|
+
load.moveMultiplier; // 1 unencumbered, decays across the soft band, 0 at capacity
|
|
128
|
+
canCarry(load, 12); // gate a pickup; the overload policy stays the game's
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`EquipSlotDef` owns `accepts` (item kinds) and `blocks` (slots it occupies while filled), and
|
|
132
|
+
`EquipItemDef` owns the `slots` an item may occupy plus its `kind`; a filled slot swaps and returns
|
|
133
|
+
the replaced item, so nothing is silently destroyed, and `state()` / `restore()` carry
|
|
134
|
+
`{ slots: [[slotId, itemId]] }`. The stash is simply left out of `carried`, so its contents add no
|
|
135
|
+
weight, and declaring it `tier: "banked"` keeps it out of `partitionOnDeath`'s lost half.
|
|
136
|
+
|
|
137
|
+
For a footprint bag, `fitShapedItem` (`inventory/shapedGrid`) places the first legal spot or names
|
|
138
|
+
why it does not fit — `too-large` with both sizes, `no-space` for a full board, `budget-exceeded` for
|
|
139
|
+
an exhausted search. Stack rules stay in `ItemTraits`: `putItem` fills partial stacks then opens new
|
|
140
|
+
ones up to the max stack, `moveItem` merges with a remainder, and `splitStack(..., limit)` refuses an
|
|
141
|
+
`over-stack` merge instead of breaking the cap.
|
|
142
|
+
|
|
143
|
+
The rendered side is the engine's: `<InventoryGrid inventoryId="bag" />` (`packages/react`) binds a
|
|
144
|
+
live declared inventory with drag, swap, merge and split through the `inventory.move` /
|
|
145
|
+
`inventory.split` commands, and `<EquipmentSlots items={...} />` paints the worn slots from
|
|
146
|
+
`gear.worn()`. What the grid cannot yet do is drag from one inventory to another (bag to stash), so a
|
|
147
|
+
stash transfer goes through `InventorySet.move` behind a button or command.
|
|
148
|
+
|
|
31
149
|
## Coherent authored content
|
|
32
150
|
|
|
33
151
|
Keep stable ids and canonical facts for characters, factions, places, and world rules in the game's authored data. Record each chapter's prerequisites, consequences, and which facts it may introduce or change. The game's creative pillars, story voice, content palette, and desired player experience come from its author; generated prose, items, art, and assets must serve those constraints. Reuse constrained generation and validation code across games while each game owns its content and visual identity; do not fill catalogs from default genre kits or mass generic content.
|
|
@@ -59,7 +59,13 @@ NPC decisions are a `decisionGraph` behavior: the graph and its blackboard are d
|
|
|
59
59
|
|
|
60
60
|
For a bounded local detour over movement collision geometry, use `planSolidRoute` (`nav/solidRoute`) with an indexed `solidObstaclesNear` or `sourceObstaclesNear` query. Handle `no-path` and `budget` separately; retain caller movement, goals, terrain and retry policy. See [reference.md](reference.md#local-collision-routes) for limits.
|
|
61
61
|
|
|
62
|
-
Route on
|
|
62
|
+
Route on an editor-baked polygon nav mesh through `createNavRuntime` (`nav/navRuntime`): read the bake off the scene document with `navMeshFromBake(document.bakes)`, price terrain and gate doors with `areaCosts`, author doors, ladders and jumps as `AuthoredNavLink` world points, and call `applyMesh(nextBake)` after an edit — polygons that survive keep their identity through the returned `remap`, so only routes crossing changed polygons need re-planning (`createNavCrowd`'s `revalidate`). A bare `createNavMeshQuery` per mesh is still the lower-level seam; keep `maxNodes` bounded so a long request returns a `partial` route instead of stalling a frame.
|
|
63
|
+
|
|
64
|
+
Move many agents across that mesh with `createNavCrowd` (`nav/navCrowd`): it plans corridors, follows the funnel-smoothed waypoints, keeps them apart with `createRvoCrowd` (`ai/rvo`) reciprocal velocity obstacles, bounds path searches to a per-tick time and count budget, and serializes through `snapshot`/`restore`. Give a crowd converging on one spot a `spread` radius so agents fan out (`ai/crowd`'s stable per-id offset) instead of stacking. Avoidance keeps agents inside the mesh's outer bounds; interior walls remain the game's collision layer.
|
|
65
|
+
|
|
66
|
+
### Street traffic
|
|
67
|
+
|
|
68
|
+
Vehicles on roads do not path-find: they follow authored lane spans. `createLaneGraph` (`nav/laneGraph`) resolves those spans — `traffic_lane` paths, or `road` paths from the editor's street bake, split where streets cross — into junctions, crossings with a signal program or first-come right-of-way, and parking spots; `laneGraphFromDocument` reads that straight off the scene document, so editing a road moves its traffic. `createTraffic` (`nav/traffic`) then drives cars on it: IDM car following and braking, one car at a time through each intersection box (only once the exit lane has room, which is what keeps a blocked crossing from wedging the network), signals with phases, lane changes across authored `left`/`right` neighbours, parking that reserves a spot, and `setDemand(count)` for fleet size — the time-of-day curve is the caller's, not the engine's. Cars are posed, not driven: when a player or a chase needs real handling, feed intent into `physics/kinematicVehicle` and let this seam keep the ambient city.
|
|
63
69
|
|
|
64
70
|
### Fallback-seam diagnostics
|
|
65
71
|
|