@jgengine/core 0.29.0 → 0.31.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 +53 -0
- package/dist/agents/agent.d.ts +134 -0
- package/dist/agents/agent.js +224 -0
- package/dist/agents/mood.d.ts +74 -0
- package/dist/agents/mood.js +97 -0
- package/dist/agents/relationships.d.ts +92 -0
- package/dist/agents/relationships.js +132 -0
- package/dist/agents/schedule.d.ts +58 -0
- package/dist/agents/schedule.js +72 -0
- package/dist/ai/director/boss.d.ts +63 -0
- package/dist/ai/director/boss.js +87 -0
- package/dist/ai/director/encounter.d.ts +146 -0
- package/dist/ai/director/encounter.js +195 -0
- package/dist/ai/director/pressure.d.ts +59 -0
- package/dist/ai/director/pressure.js +71 -0
- package/dist/ai/director/roles.d.ts +61 -0
- package/dist/ai/director/roles.js +118 -0
- package/dist/ai/director/sound.d.ts +79 -0
- package/dist/ai/director/sound.js +113 -0
- package/dist/game/gameSave.d.ts +6 -0
- package/dist/game/gameSave.js +6 -0
- package/dist/game/playableGame.d.ts +2 -2
- package/dist/game/saveStore.d.ts +7 -0
- package/dist/game/saveStore.js +35 -0
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/item/affix.d.ts +2 -0
- package/dist/loot/drop.d.ts +47 -0
- package/dist/loot/drop.js +57 -0
- package/dist/loot/haul.d.ts +78 -0
- package/dist/loot/haul.js +126 -0
- package/dist/loot/lootGen.d.ts +7 -1
- package/dist/loot/lootGen.js +24 -5
- package/dist/loot/salvage.d.ts +74 -0
- package/dist/loot/salvage.js +68 -0
- package/dist/loot/vendor.d.ts +76 -0
- package/dist/loot/vendor.js +72 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +47 -1
- package/dist/mission/mission.d.ts +24 -0
- package/dist/mission/mission.js +28 -3
- package/dist/progression/careerBank.d.ts +63 -0
- package/dist/progression/careerBank.js +83 -0
- package/dist/progression/gates.d.ts +66 -0
- package/dist/progression/gates.js +88 -0
- package/dist/progression/standing.d.ts +77 -0
- package/dist/progression/standing.js +151 -0
- package/dist/progression/upgrades.d.ts +70 -0
- package/dist/progression/upgrades.js +128 -0
- package/dist/ui/accessibility.d.ts +8 -0
- package/dist/ui/accessibility.js +14 -0
- package/package.json +1 -1
- package/skills/jgengine/api.md +3 -3
- package/skills/jgengine-gameplay/SKILL.md +12 -1
- package/skills/jgengine-gameplay/api.md +141 -3
- package/skills/jgengine-gameplay/capabilities.md +74 -0
- package/skills/jgengine-gameplay/recipes/agent-lives.md +76 -0
- package/skills/jgengine-world/api.md +52 -0
- package/skills/jgengine-world/capabilities.md +32 -0
|
@@ -29,6 +29,14 @@ export declare function reducedMotionDuration(state: AccessibilityState, ms: num
|
|
|
29
29
|
* maps to `null` (no filter). Consumed by the React `ColorblindFilters` defs.
|
|
30
30
|
*/
|
|
31
31
|
export declare const COLORBLIND_MATRICES: Record<ColorblindMode, string | null>;
|
|
32
|
+
/**
|
|
33
|
+
* The CSS filter that applies `mode` to one rendered frame — a `url(#jg-cb-<mode>)` reference to the
|
|
34
|
+
* `feColorMatrix` defs {@link COLORBLIND_MATRICES} drives. `"none"` maps to `null` (no filter), so a
|
|
35
|
+
* caller can spread it onto the canvas alone and leave HUD and menu text at its own contrast.
|
|
36
|
+
*
|
|
37
|
+
* @capability colorblind-frame-filter resolve a colorblind mode to the CSS filter for the 3D frame alone
|
|
38
|
+
*/
|
|
39
|
+
export declare function colorblindFrameFilter(mode: ColorblindMode): string | null;
|
|
32
40
|
/** Observable, serializable accessibility-preferences store. */
|
|
33
41
|
export interface AccessibilityStore {
|
|
34
42
|
get(): AccessibilityState;
|
package/dist/ui/accessibility.js
CHANGED
|
@@ -32,6 +32,20 @@ export const COLORBLIND_MATRICES = {
|
|
|
32
32
|
tritanopia: "0.95 0.05 0 0 0 0 0.433 0.567 0 0 0 0.475 0.525 0 0 0 0 0 1 0",
|
|
33
33
|
grayscale: "0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0",
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* The CSS filter that applies `mode` to one rendered frame — a `url(#jg-cb-<mode>)` reference to the
|
|
37
|
+
* `feColorMatrix` defs {@link COLORBLIND_MATRICES} drives. `"none"` maps to `null` (no filter), so a
|
|
38
|
+
* caller can spread it onto the canvas alone and leave HUD and menu text at its own contrast.
|
|
39
|
+
*
|
|
40
|
+
* @capability colorblind-frame-filter resolve a colorblind mode to the CSS filter for the 3D frame alone
|
|
41
|
+
*/
|
|
42
|
+
export function colorblindFrameFilter(mode) {
|
|
43
|
+
const matrix = COLORBLIND_MATRICES[mode];
|
|
44
|
+
// An unknown id at runtime would reference a filter that does not exist, and a broken `url()` filter hides the element.
|
|
45
|
+
if (matrix === null || matrix === undefined)
|
|
46
|
+
return null;
|
|
47
|
+
return `url(#jg-cb-${mode})`;
|
|
48
|
+
}
|
|
35
49
|
/**
|
|
36
50
|
* Serializable, observable store of accessibility preferences (reduced motion,
|
|
37
51
|
* high contrast, text scale, colorblind mode, captions). Genre-agnostic plumbing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "jgengine core — pure-TypeScript game framework SDK (npm @jgengine/core). Entity stores, commands, combat, inventory, multiplayer seams. Zero dependencies. Not an ECS. Agent skills ship in the tarball. https://jgengine.com",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jgengine",
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.31.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
|
|
286
286
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
287
287
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
288
|
-
- `VERSION` (const): const VERSION: "0.
|
|
288
|
+
- `VERSION` (const): const VERSION: "0.31.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
289
289
|
|
|
290
290
|
## @jgengine/core/runtime/adapter
|
|
291
291
|
|
|
@@ -728,7 +728,7 @@
|
|
|
728
728
|
- `GameLoop` (interface): interface GameLoop<TContext = unknown> — Lifecycle hooks a game implements to drive init, per-tick simulation, and player join/leave.
|
|
729
729
|
- `Hotbar` (function): function Hotbar({ inventoryId, activeSlot, keys, slotSize = 46, itemIcon, style, className, }: { inventoryId: string; activeSlot?: number; keys?: readonly string[]; slotSize?: number; /** Caller-supplied item id → icon registry; return null/undefined to fall back to the default glyph. */ itemIcon?: … — A numbered hotbar bound to an inventory — painted iconed slots (a `GameIcon` glyph over a school-keyed gradient with a count badge, #1035), an active-slot highlight, and a keycap per slot. Place it and pass the `inventoryId`; `activeSlot` highlights the equipped one. Supply `itemIcon` to map your item ids to your own glyphs — the default resolves a `GameIcon` from the item id.
|
|
730
730
|
- `HudCanvas` (function): function HudCanvas({ layout, editChord, compactScale, showDuring, className, style, children, }: { layout: HudLayoutStore; editChord?: HudEditChord | false; /** Zoom applied to the whole HUD on compact displays. Default 0.85. */ compactScale?: number; /** Opt-in play-phase gate: render the HUD only … — Full-viewport HUD surface. Panels declared with `HudPanel` flow into nine anchor regions and stack automatically with a gap — no per-panel pixel offsets, no manual clearance for sibling panels, the touch-control dock (`--jg-hud-dock-clearance`), or device safe areas. On compact displays the whole surface scales down and each panel applies its `compact` behavior.
|
|
731
|
-
- `HudPanel` (function): function HudPanel({ id, anchor = "top-left", order, compact: compactMode = "keep", chip, interactive, inset, locked, showDuring, priority, mobileBehavior, allowOverlapWith, collisionGroup, region = true, width, height, type, className, style, children, }: { id: string; anchor?: HudAnchor; /**
|
|
731
|
+
- `HudPanel` (function): function HudPanel({ id, anchor = "top-left", lane, order, compact: compactMode = "keep", chip, interactive, inset, locked, showDuring, priority, mobileBehavior, allowOverlapWith, collisionGroup, region = true, width, height, type, className, style, children, }: { id: string; anchor?: HudAnchor; /** … — A HUD block that lives in one of the nine anchor regions. Panels sharing a region stack outward from the screen edge in ascending `order`. On fine pointers panels stay draggable through the edit chord; a dragged panel leaves the flow and keeps its custom placement. On compact displays custom placements are ignored and the `compact` behavior applies.
|
|
732
732
|
- `PlayableGame` (type): type PlayableGame = EnginePlayableGame< ComponentType, ComponentType, RenderEntity, RenderObject, ComponentType<ViewmodelProps>, ComponentType<WorldOverlayProps> > & { /** HUD theme the shell applies at the `GameUI` root. Unset uses `defaultHudTheme`; `false` opts out of the default theme and the en… — The concrete React-bound playable a shell `defineGame` returns and `GameHost` mounts — the one PlayableGame games handle.
|
|
733
733
|
- `StatBar` (function): function StatBar({ statId = "health", entityId, tone = "health", label, showValue = true, width = 200, icon, style, className, }: { statId?: string; entityId?: string; tone?: StatTone; label?: string; showValue?: boolean; width?: number; icon?: ReactNode; style?: CSSProperties; className?: string; }… — A polished stat bar (health/mana/stamina/shield/xp) — a rounded, glassy meter with a tone-colored fill and an optional value readout. Reads `statId` off `entityId` (defaults to the local player). Renders nothing until the stat exists.
|
|
734
734
|
- `StoreHandle` (interface): interface StoreHandle<T> — A typed, cast-free handle onto one slot of the reactive game store (`ctx.game.store`). The single type parameter is fixed at definition; `read`/`write`/`update` never widen to `unknown`, so no call site repeats `store.get(key) as T`. Writes flow through the engine store, so they bump `ctx.version()`, serialize into a {@link WorldSnapshot}, and replay under host authority — the same guarantees a hand-rolled `store.get`/`store.set` pair gives up the moment it forks run state into a module-level singleton.
|
|
@@ -44,7 +44,16 @@ tiers, manufacturer stat personalities and a level curve, all caller data.
|
|
|
44
44
|
`validateLootItem` names each defect, `compareLootItems` returns per-stat deltas
|
|
45
45
|
and a weighted score, and `lootWeaponDefinition` (`loot/lootWeapon`) projects a
|
|
46
46
|
rolled item onto a `WeaponDefinition` for `createWeaponSystem`. `createLootRoller`
|
|
47
|
-
counts drops per rarity for a debug probe and snapshots its counters.
|
|
47
|
+
counts drops per rarity for a debug probe and snapshots its counters.
|
|
48
|
+
|
|
49
|
+
Roll a campaign drop with `rollDrop` (`loot/drop`) and show `compareToEquipped`
|
|
50
|
+
against worn gear; carry and bank it with `createRunHaul` (`loot/haul`), which
|
|
51
|
+
weighs the hold through the encumbrance rules and drops the hold on `fail`. Break
|
|
52
|
+
an item down once with `salvageItem` and `createItemLedger` (`loot/salvage`), and
|
|
53
|
+
price a vendor shelf through the wallet with `stockVendor` (`loot/vendor`).
|
|
54
|
+
Bases and affixes take `minLevel`; bases take `mass`, which a roll carries forward.
|
|
55
|
+
|
|
56
|
+
Sample
|
|
48
57
|
catalog, distribution check and generated-weapon proof are in
|
|
49
58
|
[reference-systems.md](reference-systems.md#procedural-gear).
|
|
50
59
|
|
|
@@ -121,6 +130,8 @@ Prefer `defineSystem` and `defineGame({ systems })` for scheduled capabilities.
|
|
|
121
130
|
|
|
122
131
|
`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.
|
|
123
132
|
|
|
133
|
+
`createAgent` (`agents/agent`) is one resident's whole life without the nav: needs decay through `sim/needs`, the utility brain picks among the targets the world offers, `agents/mood` reads a feeling from those needs plus named sources a queue or a litter patch raises, and an authored day plan (`agents/schedule`, which resolves `sim/routine`) puts the agent at work, at a meal or at home - including a work beat, where a stop names the places it patrols. A target that disappears falls to the agent's declared fallback. `createRelationshipBook` (`agents/relationships`) keeps familiarity and opinion per pair: a social action moves both, friends prefer each other's company, and a bonded pair refuses to split. Every part snapshots and restores. [Agent lives](recipes/agent-lives.md) wires both a park guest and a household being from the same factory.
|
|
134
|
+
|
|
124
135
|
`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
136
|
|
|
126
137
|
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.
|
|
@@ -2,6 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
# jgengine-gameplay — exported API surface
|
|
4
4
|
|
|
5
|
+
## @jgengine/core/agents/agent
|
|
6
|
+
|
|
7
|
+
- `Agent` (interface): interface Agent — Needs, mood, schedule and a chosen action, in one object a game ticks.
|
|
8
|
+
- `AgentAction` (interface): interface AgentAction — What the agent is doing.
|
|
9
|
+
- `AgentActionReason` (type): type AgentActionReason = UtilitySwitchReason | "fallback" | "schedule" | "cleared" — Why the action last changed.
|
|
10
|
+
- `AgentConfig` (interface): interface AgentConfig — Everything an agent is made of. Policy is data; nothing about a game is compiled in.
|
|
11
|
+
- `AgentFallback` (interface): interface AgentFallback — Where an agent goes when the thing it wanted is gone: a named option and a named place.
|
|
12
|
+
- `AgentFrame` (interface): interface AgentFrame — What one update produced: the action, the feeling, and where the day plan puts the agent.
|
|
13
|
+
- `AgentSnapshot` (interface): interface AgentSnapshot — Serializable whole agent: needs, feeling, held action, day-plan cursor and the far flag.
|
|
14
|
+
- `AgentTarget` (interface): interface AgentTarget — One reachable thing the agent could do this tick, as the game sees its own world.
|
|
15
|
+
- `AgentUpdate` (interface): interface AgentUpdate — One call per tick: the clock, and the targets the game's world currently offers.
|
|
16
|
+
- `createAgent` (function): function createAgent(config: AgentConfig): Agent — Create one agent. The same factory serves a park guest, a household being, a patrolling janitor and a soldier: `options` is what it can choose between, `schedule` is where its day puts it.
|
|
17
|
+
|
|
18
|
+
## @jgengine/core/agents/mood
|
|
19
|
+
|
|
20
|
+
- `DEFAULT_MOOD_TIERS` (const): const DEFAULT_MOOD_TIERS: readonly MoodTier[] — Five bands a park guest, a household being or a soldier can share. A game overrides them when its own tone needs different words; the numeric bands are what HUD counters and tests read.
|
|
21
|
+
- `Mood` (interface): interface Mood — What the agent feels right now, on the caller's own tier labels.
|
|
22
|
+
- `MoodReader` (interface): interface MoodReader — A mood reader that keeps the game-raised biases, so a queue or a litter patch moves mood too.
|
|
23
|
+
- `MoodSnapshot` (interface): interface MoodSnapshot — Serializable mood state: the game-raised biases that sit on top of need satisfaction.
|
|
24
|
+
- `MoodTier` (interface): interface MoodTier — One labelled band of the 0-100 mood score.
|
|
25
|
+
- `MoodWeight` (interface): interface MoodWeight — One need's say in the mood score; weights are relative, so they need no fixed total.
|
|
26
|
+
- `createMoodReader` (function): function createMoodReader(config: { readonly needs: Pick<NeedSet, "ids" | "fraction">; readonly weights?: readonly MoodWeight[]; readonly tiers?: readonly MoodTier[]; }): MoodReader — Create the mood an agent carries. Needs are the agent's own (shared with its utility brain), and every bias is named data the game can remove again - there is no decaying hidden penalty.
|
|
27
|
+
- `moodTierOf` (function): function moodTierOf(score: number, tiers: readonly MoodTier[] = DEFAULT_MOOD_TIERS): MoodTier — The band a score reads as, from tiers sorted by `at` descending. Unknown or unsorted tiers fall back to the lowest declared band rather than throwing, because a HUD reads this every frame.
|
|
28
|
+
- `readMood` (function): function readMood(needs: Pick<NeedSet, "ids" | "fraction">, weights: readonly MoodWeight[], tiers: readonly MoodTier[] = DEFAULT_MOOD_TIERS, sources?: Readonly<Record<string, number>>): Mood — A mood read from a need set: each declared need contributes its `0..1` satisfaction times its weight, normalized by the total weight, so an agent with four needs and one with six read on the same 0-100 scale. Pure and allocation-light - the per-frame read a HUD and a thought bubble share.
|
|
29
|
+
|
|
30
|
+
## @jgengine/core/agents/relationships
|
|
31
|
+
|
|
32
|
+
- `DEFAULT_RELATIONSHIP_MILESTONES` (const): const DEFAULT_RELATIONSHIP_MILESTONES: readonly RelationshipMilestone[] — The pair levels every game starts from; a game overrides the words and the cut points.
|
|
33
|
+
- `DEFAULT_SOCIAL_CONTACT` (const): const DEFAULT_SOCIAL_CONTACT: Required<SocialContact> — What a plain social action adds: a shared moment nudges both dials, never maxes either.
|
|
34
|
+
- `Relationship` (interface): interface Relationship — How one pair stands: how much time they have shared, and what they think of each other.
|
|
35
|
+
- `RelationshipBook` (interface): interface RelationshipBook — Everyone's view of everyone else: familiarity and opinion per pair, changed by social actions, with the pair levels that make a friendship a preference and a bond a refusal to split.
|
|
36
|
+
- `RelationshipBookSnapshot` (interface): interface RelationshipBookSnapshot — Serializable relationship state: every known pair plus the dials in play.
|
|
37
|
+
- `RelationshipMilestone` (interface): interface RelationshipMilestone — One labelled bond level on the opinion scale.
|
|
38
|
+
- `RelationshipPolicy` (interface): interface RelationshipPolicy — Tunables a game owns, every one reachable through {@link RelationshipBook.retune}.
|
|
39
|
+
- `SocialContact` (interface): interface SocialContact — Opinion and familiarity one interaction adds; a game passes its own for a hug, a row or a shift.
|
|
40
|
+
- `SocialOutcome` (interface): interface SocialOutcome — What one social action did to a pair.
|
|
41
|
+
- `createRelationshipBook` (function): function createRelationshipBook(policy: RelationshipPolicy = {}): RelationshipBook — Create the shared book of pair state. Keys are undirected and delimiter-safe through `createPairKeyCodec`, and milestone crossings come from `crossThresholds`, so a notice feed and a journal both read the same event.
|
|
42
|
+
|
|
43
|
+
## @jgengine/core/agents/schedule
|
|
44
|
+
|
|
45
|
+
- `AgentSchedule` (interface): interface AgentSchedule — A day plan resolved against the world clock. No state to tick: a far agent reads the same day.
|
|
46
|
+
- `AgentScheduleConfig` (interface): interface AgentScheduleConfig — An agent's whole day as data: what it does, where, and when.
|
|
47
|
+
- `AgentSchedulePhase` (interface): interface AgentSchedulePhase — Where a schedule puts an agent at one instant.
|
|
48
|
+
- `AgentScheduleStop` (interface): interface AgentScheduleStop extends RoutineStopSpec — One authored stop in an agent's day. The shape is the routine owner's (`sim/routine`) plus a beat: the places a working stop walks in order, so a janitor on patrol is an agent with a route rather than a special case.
|
|
49
|
+
- `createAgentSchedule` (function): function createAgentSchedule(config: AgentScheduleConfig): AgentSchedule — Resolve an authored day plan into a clock-driven schedule. Timing, travel and arrival slips come from `sim/routine`; this adds the work beat and the refills a stop carries, so an off-hours stop is just another authored stop and a patrol is not a second scheduler.
|
|
50
|
+
|
|
5
51
|
## @jgengine/core/behaviour/behaviour
|
|
6
52
|
|
|
7
53
|
- `Behaviour` (class): class Behaviour — Subclass and override the lifecycle hooks. A behaviour only joins the per-frame update dispatch if it actually overrides `onUpdate` (prototype-identity check at each activation), so hook-only behaviours cost nothing per frame.
|
|
@@ -1251,6 +1297,7 @@
|
|
|
1251
1297
|
- `DEFAULT_ESCAPE_MULTIPLE` (const): const DEFAULT_ESCAPE_MULTIPLE: 3 — A pursuit escapes at this multiple of its keep-close distance when none is declared.
|
|
1252
1298
|
- `DEFAULT_FIXED_STAGES` (const): const DEFAULT_FIXED_STAGES: readonly ["input", "movement", "combat", "ai", "activities", "cleanup"] — Default fixed-sim stage order — systems pick a stage; most need only this.
|
|
1253
1299
|
- `DEFAULT_FRAME_STAGES` (const): const DEFAULT_FRAME_STAGES: readonly ["input", "movement", "combat", "ai", "activities", "cleanup", "animation", "camera", "effects"] — Default frame stage order. Gameplay stages mirror the fixed table so once-per-frame systems (`type: "frame"`) can pick `combat`/`ai`/… without falling into the unknown-stage bucket; presentation stages follow.
|
|
1300
|
+
- `DEFAULT_MARKER_TOLERANCE` (const): const DEFAULT_MARKER_TOLERANCE: 8 — Default vertical allowance (m) for a horizontal marker proximity test.
|
|
1254
1301
|
- `DEFAULT_PICKUP_RADIUS` (const): const DEFAULT_PICKUP_RADIUS: 2 — ⚠ undocumented
|
|
1255
1302
|
- `DEFAULT_PING_CATEGORIES` (const): const DEFAULT_PING_CATEGORIES: Record<PingCategory, PingCategoryDef> — Content-agnostic default ping wheel: enemy / loot / location / danger.
|
|
1256
1303
|
- `DEFAULT_PURSUE_DISTANCE` (const): const DEFAULT_PURSUE_DISTANCE: 3 — Default keep-close distance (m) for a `pursue` objective.
|
|
@@ -1399,7 +1446,7 @@
|
|
|
1399
1446
|
- `LootDropProvenance` (interface): interface LootDropProvenance — Why one drop is in the result: the stage/table/entry that produced it, its weights, and the modifiers that shaped it.
|
|
1400
1447
|
- `LootFilterRule` (interface): interface LootFilterRule { id: string; when: LootFilterCondition; hide?: boolean; color?: string; beam?: boolean; label?: string } — ⚠ undocumented · used by `evaluateLootFilter`: First matching rule wins (PoE/Last Epoch block semantics) — later rules never override an earlier match.
|
|
1401
1448
|
- `LootInstalledPart` (interface): interface LootInstalledPart — One part occupying one slot of a rolled item.
|
|
1402
|
-
- `LootIssueCode` (type): type LootIssueCode = | "invalid-level" | "unknown-base" | "unknown-manufacturer" | "manufacturer-base" | "unknown-rarity" | "duplicate-slot" | "unknown-slot" | "missing-slot-part" | "unknown-part" | "part-category" | "unknown-affix" | "duplicate-affix" | "invalid-affix" | "non-
|
|
1449
|
+
- `LootIssueCode` (type): type LootIssueCode = | "invalid-level" | "unknown-base" | "level-gated-base" | "unknown-manufacturer" | "manufacturer-base" | "unknown-rarity" | "duplicate-slot" | "unknown-slot" | "missing-slot-part" | "unknown-part" | "part-category" | "unknown-affix" | "duplicate-affix" | "invalid-affix" | "non-f… — A defect found by {@link validateLootItem}.
|
|
1403
1450
|
- `LootItem` (interface): interface LootItem — One rolled item: the base, maker, rarity, level, composed name, chosen parts, rolled affixes and resolved stats. Plain JSON, so a save stores it and the instance registry can key it.
|
|
1404
1451
|
- `LootManufacturerDef` (interface): interface LootManufacturerDef — A maker with a stat personality: its mark goes in the name, `bias` shifts stats additively, `scale` multiplies them, and favored affix pools weigh more when this maker rolls the item.
|
|
1405
1452
|
- `LootModifier` (interface): interface LootModifier<TCtx = unknown> — A registered, id-tagged loot policy applied to one stage. `plan` transforms eligibility, weights, and roll counts before rolling (luck, difficulty, gating); `drops` post-processes the rolled drops (quantity multipliers, dedupe, caps). Neither hook mutates table definitions, and the id is recorded in provenance so a resolved drop can be traced back to the policies that shaped it.
|
|
@@ -1447,6 +1494,8 @@
|
|
|
1447
1494
|
- `MissionFailCondition` (interface): interface MissionFailCondition — A condition that fails the mission while the objective is active. `to` names a {@link MissionFailDef}; with no `to` the mission fails with the generic `"failed"` state.
|
|
1448
1495
|
- `MissionFailDef` (interface): interface MissionFailDef — A named fail state an objective, timer or branch can land in.
|
|
1449
1496
|
- `MissionIssue` (interface): interface MissionIssue — A located mission-catalog problem; `path` is a JSON Pointer into the supplied catalog.
|
|
1497
|
+
- `MissionMarkerPlane` (type): type MissionMarkerPlane = "horizontal" | "spatial" — Which plane a marker proximity test compares in.
|
|
1498
|
+
- `MissionMarkerProximity` (interface): interface MissionMarkerProximity — How a positional objective compares an actor to its authored marker.
|
|
1450
1499
|
- `MissionObjectiveDef` (interface): interface MissionObjectiveDef — One objective. Every field is content; the machine reads only these.
|
|
1451
1500
|
- `MissionObjectiveKind` (type): type MissionObjectiveKind = "reach" | "interact" | "survive" | "pursue" | "kill" | "collect" — The primitive goals an objective can have. Escort, chase and defend are not kinds — they are compositions of these plus fail conditions and timers (see `compositions.ts`).
|
|
1452
1501
|
- `MissionRewardAppliers` (interface): interface MissionRewardAppliers — Appliers a mission reward routes through — the same shape quests use, plus a loot-table hook.
|
|
@@ -2132,7 +2181,7 @@
|
|
|
2132
2181
|
|
|
2133
2182
|
## @jgengine/core/item/affix
|
|
2134
2183
|
|
|
2135
|
-
- `AffixDef` (interface): interface AffixDef { id: string; stat: string; op?: AffixOp; roll: number | [number, number]; weight: number; namePart?: {position: "prefix" | "suffix"; text: string} } — ⚠ undocumented
|
|
2184
|
+
- `AffixDef` (interface): interface AffixDef { id: string; stat: string; op?: AffixOp; roll: number | [number, number]; weight: number; minLevel?: number; namePart?: {position: "prefix" | "suffix"; text: string} } — ⚠ undocumented
|
|
2136
2185
|
- `AffixOp` (type): type AffixOp = "add" | "mul" — ⚠ undocumented
|
|
2137
2186
|
- `AffixPool` (interface): interface AffixPool { id: string; affixes: readonly AffixDef[] } — ⚠ undocumented
|
|
2138
2187
|
- `AffixRoller` (interface): interface AffixRoller { rollRarity(rng: () => number): RarityTier; roll(base: ItemBaseDef, rarityId: string, rng: () => number): RolledItem; rollRandom(base: ItemBaseDef, rng: () => number): RolledItem } — ⚠ undocumented · used by `createAffixRoller`: Rolls randomized gear from a base item: a weighted rarity tier scales base stats and picks prefix/suffix affixes that name and modify the it…
|
|
@@ -2263,6 +2312,25 @@
|
|
|
2263
2312
|
- `createWeaponStats` (function): function createWeaponStats(resolveEntry: (itemId: string) => WeaponEntry | null | undefined): WeaponStats — Resolve per-weapon stat values — damage, fire rate, spread — for combat math.
|
|
2264
2313
|
- `getWeaponStat` (function): function getWeaponStat(entry: WeaponEntry | null | undefined, stat: string): number | null — ⚠ undocumented
|
|
2265
2314
|
|
|
2315
|
+
## @jgengine/core/loot/drop
|
|
2316
|
+
|
|
2317
|
+
- `EquippedComparison` (interface): interface EquippedComparison — What a HUD renders when a drop lands: per-stat deltas against the worn item and one verdict.
|
|
2318
|
+
- `EquippedStatDelta` (interface): interface EquippedStatDelta extends LootStatDelta — A per-stat delta from the equipped item to the candidate; positive `delta` means the candidate is larger.
|
|
2319
|
+
- `ItemDrop` (interface): interface ItemDrop extends LootItem — A rolled item with a stable instance id, so a stash, ledger and vendor stock can name it.
|
|
2320
|
+
- `RollDropOptions` (interface): interface RollDropOptions extends LootRollOptions — {@link rollDrop} options: the loot roll plus the instance id (defaults to `drop:<seed>`).
|
|
2321
|
+
- `compareToEquipped` (function): function compareToEquipped(candidate: ItemDrop, equipped: ItemDrop | null, options: LootComparisonOptions = {}): EquippedComparison — Compare a dropped item against what is equipped, with one delta per stat and a weighted verdict. The delta is `candidate - equipped`; `better` flips for stats the caller lists in `lowerIsBetter`, so reload time improving does not read as worse. A missing equipped item compares against zeros.
|
|
2322
|
+
- `itemMass` (function): function itemMass(item: Pick<LootItem, "mass">): number — Carried mass of a rolled item. Older saves may predate `mass`; those default to `1`.
|
|
2323
|
+
- `rollDrop` (function): function rollDrop(catalog: LootCatalog, seed: string | number, options: RollDropOptions = {}): ItemDrop — Roll one item from a seed. Same seed, catalog and options produce the same id, name, affixes, stats and mass on every run; level gates which bases and affixes are in the pool.
|
|
2324
|
+
|
|
2325
|
+
## @jgengine/core/loot/haul
|
|
2326
|
+
|
|
2327
|
+
- `CarryOutcome` (type): type CarryOutcome = | { ok: true; mass: number; remaining: number } | { ok: false; reason: "over-capacity" | "no-space" | "invalid-count"; mass: number; remaining: number } — What a carry attempt returns: the new load, or the rule that refused it.
|
|
2328
|
+
- `HaulTransfer` (interface): interface HaulTransfer — Stacks moved between a run and its stash, or lost when the run fails.
|
|
2329
|
+
- `RunHaul` (interface): interface RunHaul — A run's carried loot and the stash that outlives it.
|
|
2330
|
+
- `RunHaulConfig` (interface): interface RunHaulConfig<TId extends string> — What a run haul needs: two inventories, a weight rule and an optional save cell.
|
|
2331
|
+
- `RunHaulState` (interface): interface RunHaulState — Detached run and stash contents, for a save or a debug readout.
|
|
2332
|
+
- `createRunHaul` (function): function createRunHaul<TId extends string>(config: RunHaulConfig<TId>): RunHaul — A run's haul beside a persistent stash. Carrying is gated by the shared encumbrance rules; `bank` moves what the stash fits and saves; `fail` throws the run's haul away and leaves the stash as it was. The game decides when a run succeeds or fails and calls one of them.
|
|
2333
|
+
|
|
2266
2334
|
## @jgengine/core/loot/lootGen
|
|
2267
2335
|
|
|
2268
2336
|
- `LootBaseDef` (interface): interface LootBaseDef extends ItemBaseDef — A base family (frame, housing, chassis — caller nouns) with its slots, stats and affix pools.
|
|
@@ -2270,7 +2338,7 @@
|
|
|
2270
2338
|
- `LootComparison` (interface): interface LootComparison — How two items compare: every stat delta plus one weighted score for each.
|
|
2271
2339
|
- `LootComparisonOptions` (interface): interface LootComparisonOptions — Caller policy for the comparison score.
|
|
2272
2340
|
- `LootInstalledPart` (interface): interface LootInstalledPart — One part occupying one slot of a rolled item.
|
|
2273
|
-
- `LootIssueCode` (type): type LootIssueCode = | "invalid-level" | "unknown-base" | "unknown-manufacturer" | "manufacturer-base" | "unknown-rarity" | "duplicate-slot" | "unknown-slot" | "missing-slot-part" | "unknown-part" | "part-category" | "unknown-affix" | "duplicate-affix" | "invalid-affix" | "non-
|
|
2341
|
+
- `LootIssueCode` (type): type LootIssueCode = | "invalid-level" | "unknown-base" | "level-gated-base" | "unknown-manufacturer" | "manufacturer-base" | "unknown-rarity" | "duplicate-slot" | "unknown-slot" | "missing-slot-part" | "unknown-part" | "part-category" | "unknown-affix" | "duplicate-affix" | "invalid-affix" | "non-f… — A defect found by {@link validateLootItem}.
|
|
2274
2342
|
- `LootItem` (interface): interface LootItem — One rolled item: the base, maker, rarity, level, composed name, chosen parts, rolled affixes and resolved stats. Plain JSON, so a save stores it and the instance registry can key it.
|
|
2275
2343
|
- `LootManufacturerDef` (interface): interface LootManufacturerDef — A maker with a stat personality: its mark goes in the name, `bias` shifts stats additively, `scale` multiplies them, and favored affix pools weigh more when this maker rolls the item.
|
|
2276
2344
|
- `LootPartDef` (interface): interface LootPartDef extends PartDef — A part a base can mount: its stats, a pick weight, and an optional word for the item name.
|
|
@@ -2296,6 +2364,30 @@
|
|
|
2296
2364
|
- `LootWeaponStatMap` (interface): interface LootWeaponStatMap — Which rolled stat feeds which part of a {@link WeaponDefinition}. Every field is a stat id from the item; an omitted field keeps the template's value, so a catalog only maps what it rolls.
|
|
2297
2365
|
- `lootWeaponDefinition` (function): function lootWeaponDefinition(item: LootItem, projection: LootWeaponProjection): WeaponDefinition — Project a rolled item onto a plain weapon definition: copy the template, then write the mapped stats into rate, damage, magazine, spread, range and projectile speed. The result is a {@link WeaponDefinition}, so `assertWeaponDefinition` and `createWeaponSystem` accept it unchanged.
|
|
2298
2366
|
|
|
2367
|
+
## @jgengine/core/loot/salvage
|
|
2368
|
+
|
|
2369
|
+
- `ItemDropLike` (interface): interface ItemDropLike — The fields salvage reads; an `ItemDrop` from `./drop` satisfies it.
|
|
2370
|
+
- `ItemLedger` (interface): interface ItemLedger — One record of what happened to rolled item instances. Every path that removes an item - salvage, sale, granting a duplicate - claims it here first, so a second claim returns false and the item cannot be equipped, sold or broken down twice.
|
|
2371
|
+
- `ItemLedgerState` (type): type ItemLedgerState = Readonly<Record<string, Exclude<ItemLifecycle, "owned">>> — Items that have left play, keyed by instance id; anything absent is still `owned`.
|
|
2372
|
+
- `ItemLifecycle` (type): type ItemLifecycle = "owned" | "salvaged" | "sold" — Where an item instance stands: still usable, broken down, or sold.
|
|
2373
|
+
- `SalvageResult` (type): type SalvageResult = | { ok: true; id: string; yield: SalvageYield } | { ok: false; reason: "already-spent" | "invalid-item" } — Salvage outcome: the yield, or the reason the instance could not be broken down.
|
|
2374
|
+
- `SalvageTable` (interface): interface SalvageTable — What a broken-down item is worth: a game-authored currency, rate and per-rarity multiplier.
|
|
2375
|
+
- `SalvageYield` (interface): interface SalvageYield — The currency and amount one salvage yields. `currency` is the game's key, never interpreted.
|
|
2376
|
+
- `createItemLedger` (function): function createItemLedger(initial: ItemLedgerState = {}): ItemLedger — Build an item ledger. Ids are consumed once: the first successful {@link ItemLedger.spend} wins, every later call fails, which is what makes salvage and sale mutually exclusive.
|
|
2377
|
+
- `salvageItem` (function): function salvageItem(item: ItemDropLike, table: SalvageTable, ledger: ItemLedger): SalvageResult — Break one item down through its ledger. The ledger is claimed before the yield is computed, so a salvaged item cannot be salvaged, equipped or sold again; pass the game's own wallet update from `yield`.
|
|
2378
|
+
- `salvageValue` (function): function salvageValue(item: Pick<ItemDropLike, "level" | "rarity">, table: SalvageTable): SalvageYield — The yield an item of this level and rarity breaks down into. The formula is `round(perLevel x level x rarityMultiplier)`, floored by `minimum`, so the same item always yields the same amount and a higher level never yields less.
|
|
2379
|
+
|
|
2380
|
+
## @jgengine/core/loot/vendor
|
|
2381
|
+
|
|
2382
|
+
- `StockVendorOptions` (interface): interface StockVendorOptions — What stocking a vendor reads: the roll catalog, a seeded rng and the pricing policy.
|
|
2383
|
+
- `VendorOffer` (interface): interface VendorOffer — One stocked item and the shop entry that prices it.
|
|
2384
|
+
- `VendorPriceRule` (interface): interface VendorPriceRule — How a vendor price is derived from an item: a base, a per-level rate and a rarity multiplier.
|
|
2385
|
+
- `VendorRotation` (interface): interface VendorRotation — A run-count refresh rule; the game owns how many runs between restocks.
|
|
2386
|
+
- `VendorRotationState` (interface): interface VendorRotationState — Where a vendor shelf stands: the run number it was last stocked on.
|
|
2387
|
+
- `createVendorRotation` (function): function createVendorRotation(everyRuns: number, start = 0): VendorRotation — A restock rule the game authors as "every N runs". It only answers when a refresh is due; the game calls {@link stockVendor} and then `mark`. No timer, so a paused or reloaded save keeps the same shelf until the run count moves.
|
|
2388
|
+
- `itemPrice` (function): function itemPrice(item: Pick<LootItem, "level" | "rarity">, rule: VendorPriceRule): { buy: number; sell: number | null } — The price of one item under a `VendorPriceRule`: `round(base + perLevel x level)` times the rarity multiplier, never below 1. Sell-back is `floor(price x sellFactor)` when the rule sets one.
|
|
2389
|
+
- `stockVendor` (function): function stockVendor(shop: ShopStock, options: StockVendorOptions): VendorOffer[] — Roll a vendor's shelf and add it to an existing {@link ShopStock}. Entries added by a previous call with the same `idPrefix` are removed first, so a refresh replaces the shelf instead of throwing on duplicate ids. Buying, selling and wallet charging stay in the economy owner.
|
|
2390
|
+
|
|
2299
2391
|
## @jgengine/core/mission/compositions
|
|
2300
2392
|
|
|
2301
2393
|
- `ChaseMissionOptions` (interface): interface ChaseMissionOptions extends ComposedMissionOptions — Options for {@link chaseMission}: a target kept close until it is caught.
|
|
@@ -2309,6 +2401,7 @@
|
|
|
2309
2401
|
## @jgengine/core/mission/mission
|
|
2310
2402
|
|
|
2311
2403
|
- `DEFAULT_ESCAPE_MULTIPLE` (const): const DEFAULT_ESCAPE_MULTIPLE: 3 — A pursuit escapes at this multiple of its keep-close distance when none is declared.
|
|
2404
|
+
- `DEFAULT_MARKER_TOLERANCE` (const): const DEFAULT_MARKER_TOLERANCE: 8 — Default vertical allowance (m) for a horizontal marker proximity test.
|
|
2312
2405
|
- `DEFAULT_PURSUE_DISTANCE` (const): const DEFAULT_PURSUE_DISTANCE: 3 — Default keep-close distance (m) for a `pursue` objective.
|
|
2313
2406
|
- `DEFAULT_REACH_RADIUS` (const): const DEFAULT_REACH_RADIUS: 2.5 — Default proximity radius (m) for a `reach` / `interact` objective.
|
|
2314
2407
|
- `MISSION_COUNTER_KINDS` (const): const MISSION_COUNTER_KINDS: readonly MissionObjectiveKind[] — Objective kinds that are satisfied only by a counter — a mission made of these alone is a lint error.
|
|
@@ -2321,6 +2414,8 @@
|
|
|
2321
2414
|
- `MissionFailCondition` (interface): interface MissionFailCondition — A condition that fails the mission while the objective is active. `to` names a {@link MissionFailDef}; with no `to` the mission fails with the generic `"failed"` state.
|
|
2322
2415
|
- `MissionFailDef` (interface): interface MissionFailDef — A named fail state an objective, timer or branch can land in.
|
|
2323
2416
|
- `MissionIssue` (interface): interface MissionIssue — A located mission-catalog problem; `path` is a JSON Pointer into the supplied catalog.
|
|
2417
|
+
- `MissionMarkerPlane` (type): type MissionMarkerPlane = "horizontal" | "spatial" — Which plane a marker proximity test compares in.
|
|
2418
|
+
- `MissionMarkerProximity` (interface): interface MissionMarkerProximity — How a positional objective compares an actor to its authored marker.
|
|
2324
2419
|
- `MissionObjectiveDef` (interface): interface MissionObjectiveDef — One objective. Every field is content; the machine reads only these.
|
|
2325
2420
|
- `MissionObjectiveKind` (type): type MissionObjectiveKind = "reach" | "interact" | "survive" | "pursue" | "kill" | "collect" — The primitive goals an objective can have. Escort, chase and defend are not kinds — they are compositions of these plus fail conditions and timers (see `compositions.ts`).
|
|
2326
2421
|
- `MissionRewardAppliers` (interface): interface MissionRewardAppliers — Appliers a mission reward routes through — the same shape quests use, plus a loot-table hook.
|
|
@@ -2349,6 +2444,26 @@
|
|
|
2349
2444
|
- `missionMarkers` (function): function missionMarkers(document: SceneDocumentLike): Record<string, ScenePoint3> — Marker positions from an authored scene, keyed by marker id — what a positional objective reads.
|
|
2350
2445
|
- `registerMissionTriggerActions` (function): function registerMissionTriggerActions(): void — Register {@link MISSION_TRIGGER_ACTIONS} so the editor lists them. Idempotent; call at module load next to the game's catalogs.
|
|
2351
2446
|
|
|
2447
|
+
## @jgengine/core/progression/careerBank
|
|
2448
|
+
|
|
2449
|
+
- `CareerBank` (interface): interface CareerBank — Persistent career money plus the run in progress, which a failed run loses.
|
|
2450
|
+
- `CareerBankConfig` (interface): interface CareerBankConfig — Configuration for {@link createCareerBank}.
|
|
2451
|
+
- `CareerBankState` (interface): interface CareerBankState — Everything a career bank stores, as plain JSON. `banked` is the persistent balance; `haul` is the run in progress and is the part a failed run loses. `deposited`, `lost`, `bestHaul` and `runs` are lifetime counters a results screen reads.
|
|
2452
|
+
- `createCareerBank` (function): function createCareerBank(config: CareerBankConfig): CareerBank — Money that means something between runs: a persistent balance plus a run's haul that is banked on a cash-out and lost on a failure. Amounts are snapped to the currency's minor units, so a save round-trip and a display never drift. Pair it with `economy/upgrades` for a career upgrade tree.
|
|
2453
|
+
|
|
2454
|
+
## @jgengine/core/progression/gates
|
|
2455
|
+
|
|
2456
|
+
- `GateDef` (interface): interface GateDef — A milestone: content that opens exactly when its requirements hold and names the unlock it grants. `unlock` must resolve in the catalog the set is built with, or the set refuses to load.
|
|
2457
|
+
- `GateFacts` (interface): interface GateFacts — What a gate is evaluated against: standing by track and money by currency.
|
|
2458
|
+
- `GateMiss` (type): type GateMiss = "standing" | "balance" — Which named condition a closed gate still needs.
|
|
2459
|
+
- `GateRequirement` (interface): interface GateRequirement — Every condition a gate can require; all named conditions must hold.
|
|
2460
|
+
- `GateSet` (interface): interface GateSet — Evaluated gate content: the table, its status against facts, and the grants a caller applies.
|
|
2461
|
+
- `GateSetConfig` (interface): interface GateSetConfig — Content and the resolvers a {@link GateSet} is built from.
|
|
2462
|
+
- `GateStatus` (interface): interface GateStatus — One gate against live facts.
|
|
2463
|
+
- `assertGates` (function): function assertGates(config: GateSetConfig): void — Check an authored gate table against the content it grants; throws `RangeError` naming the gate.
|
|
2464
|
+
- `createGateSet` (function): function createGateSet(config: GateSetConfig): GateSet — Authored unlock gates: each one names the unlock it grants and the standing or balance it reads. Evaluated against caller-supplied facts, so one table serves a single-player save, a server actor or a probe. Load refuses a gate whose unlock id resolves to nothing (the D3.8 class), so a dead milestone fails at startup instead of in play. A gate with no requirements is open from the start.
|
|
2465
|
+
- `gateFacts` (function): function gateFacts(standing: Pick<Standing, "standing">, actorId: string, balances: Readonly<Record<string, number>> = {}): GateFacts — Read a {@link Standing} and a balance record as {@link GateFacts}.
|
|
2466
|
+
|
|
2352
2467
|
## @jgengine/core/progression/gearScore
|
|
2353
2468
|
|
|
2354
2469
|
- `GearScore` (interface): interface GearScore — A scored loadout: the total, each slot's share, each stat's signed contribution, and how complete it is.
|
|
@@ -2372,6 +2487,18 @@
|
|
|
2372
2487
|
- `assertSkillTreeConfig` (function): function assertSkillTreeConfig<TStat extends string>(config: SkillTreeConfig<TStat>): void — Check tree data before play; throws `RangeError` naming the offending node, requirement or synergy.
|
|
2373
2488
|
- `createSkillTree` (function): function createSkillTree<TStat extends string = string>(config: SkillTreeConfig<TStat>, options: { devtoolsName?: string } = {}): SkillTree<TStat> — A data-authored skill tree: nodes carry a point cost, prerequisites, passives and unlocked actions, synergies pay off two or more nodes together, and `respec` refunds exactly the points spent. Spend against `canSpend` for a reason a UI can show; read `resolved()` for the stat modifiers and action list to feed `createStats` / `createAbilityKit`.
|
|
2374
2489
|
|
|
2490
|
+
## @jgengine/core/progression/standing
|
|
2491
|
+
|
|
2492
|
+
- `Standing` (interface): interface Standing — Live standing over authored tracks, moved by named outcomes or direct adjustments, with the tier on each track and a serializable snapshot.
|
|
2493
|
+
- `StandingChange` (interface): interface StandingChange — One track's movement, with the tier on both sides so a caller can announce a crossing.
|
|
2494
|
+
- `StandingConfig` (interface): interface StandingConfig — Authored standing content: the tracks an actor stands with, and the outcomes that move them.
|
|
2495
|
+
- `StandingOutcomeDef` (interface): interface StandingOutcomeDef — One outcome a game can report, and the standing it moves on each track.
|
|
2496
|
+
- `StandingRequirement` (interface): interface StandingRequirement — A threshold on one track, as an unlock or an upgrade reads it.
|
|
2497
|
+
- `StandingState` (interface): interface StandingState — Plain JSON state of a {@link Standing}; tracks left at their authored `initial` are absent.
|
|
2498
|
+
- `StandingTrackDef` (interface): interface StandingTrackDef — One standing track: a faction, a district, a licence board — whatever an authored outcome moves.
|
|
2499
|
+
- `assertStandingConfig` (function): function assertStandingConfig(config: StandingConfig): void — Check authored standing content before use; throws `RangeError` naming the offending field.
|
|
2500
|
+
- `createStanding` (function): function createStanding(config: StandingConfig): Standing — Per-faction or per-district standing that rises and falls from named outcomes, over authored tracks. Numbers live in the shared reputation ledger (`faction/reputation`); this adds the authored outcome table, tier-per-track and the one snapshot a save stores. An outcome id or track id that resolves to nothing throws at the call, so authored content fails loudly.
|
|
2501
|
+
|
|
2375
2502
|
## @jgengine/core/progression/statGraph
|
|
2376
2503
|
|
|
2377
2504
|
- `StatContribution` (interface): interface StatContribution — One folded contribution to a value, retained so a sheet can explain "why is this value 42?" — every step carries the source that produced it.
|
|
@@ -2389,6 +2516,17 @@
|
|
|
2389
2516
|
- `createStatGraph` (function): function createStatGraph(def: StatGraphDef): StatGraph — A data-driven stat graph: game-owned named inputs feed caller-authored derived formulas, with contribution provenance, uncommitted previews, cycle detection, and selective recomputation. Formula semantics and numeric tables stay entirely game-defined.
|
|
2390
2517
|
- `statModifierContributions` (function): function statModifierContributions<TStat extends string>(source: string, set: StatModifierSet<TStat>): Record<string, StatContribution[]> — Bridge the shared {@link StatModifierSet} shape (add/multiply, used by talents, items, and buffs) into stat-graph contributions, so a ranked talent tree or gear roll feeds the graph as one named source instead of a parallel store.
|
|
2391
2518
|
|
|
2519
|
+
## @jgengine/core/progression/upgrades
|
|
2520
|
+
|
|
2521
|
+
- `UpgradeBuy` (type): type UpgradeBuy = { ok: true; nodeId: string; rank: number; cost: number } | { ok: false; reason: UpgradeRejection } — Result of {@link UpgradeTree.buy}: the rank reached and what it cost, or why it was refused.
|
|
2522
|
+
- `UpgradeNodeDef` (interface): interface UpgradeNodeDef — One upgrade node. Its price is the shared progression {@link Curve} evaluated at the rank being bought, so a game authors one curve and every rank of every node follows it.
|
|
2523
|
+
- `UpgradeRejection` (type): type UpgradeRejection = "unknown-node" | "max-rank" | "missing-prerequisites" | "standing" | "insufficient-funds" — Why a purchase was refused; the tree is left untouched on any rejection.
|
|
2524
|
+
- `UpgradeState` (interface): interface UpgradeState — Plain JSON state: node id → ranks owned. Nodes at rank `0` are absent.
|
|
2525
|
+
- `UpgradeTree` (interface): interface UpgradeTree — Authored upgrade nodes with a per-node cost curve, prerequisites, a standing gate and one snapshot.
|
|
2526
|
+
- `UpgradeTreeConfig` (interface): interface UpgradeTreeConfig — Authored nodes plus the standing a node's requirement reads.
|
|
2527
|
+
- `assertUpgrades` (function): function assertUpgrades(config: UpgradeTreeConfig): void — Check authored upgrade nodes; throws `RangeError` naming the offending node or field.
|
|
2528
|
+
- `createUpgradeTree` (function): function createUpgradeTree(config: UpgradeTreeConfig): UpgradeTree — An upgrade tree whose prices come from the shared curve primitive: each node authors one cost curve, each rank is that curve at the rank, and a purchase passes prerequisites, a standing gate and the caller's own payment step. State is one rank map that JSON round-trips, so a career save stores what a player paid for rather than the arithmetic behind it.
|
|
2529
|
+
|
|
2392
2530
|
## @jgengine/core/progression/xpCurve
|
|
2393
2531
|
|
|
2394
2532
|
- `XpCurve` (interface): interface XpCurve — A resolved, validated {@link XpCurveDef}: thresholds, lifetime totals, a stated rule, and a leveling track bound to the same numbers.
|
|
@@ -16,6 +16,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
16
16
|
|
|
17
17
|
- `createAffixRoller` (function) · `import { createAffixRoller } from "@jgengine/core/gameplay"`
|
|
18
18
|
|
|
19
|
+
## agent-schedule — an authored per-agent day plan with a work beat and off-hours behaviour
|
|
20
|
+
|
|
21
|
+
- `createAgentSchedule` (function) · `import { createAgentSchedule } from "@jgengine/core/agents/schedule"`
|
|
22
|
+
|
|
23
|
+
## agents — a needs, thought, action and mood loop an agent runs on the world clock
|
|
24
|
+
|
|
25
|
+
- `createAgent` (function) · `import { createAgent } from "@jgengine/core/agents/agent"`
|
|
26
|
+
|
|
19
27
|
## auction-book — timed bid auctions with escrowed bids, buyout, anti-snipe close extension, and collection-box settlement
|
|
20
28
|
|
|
21
29
|
- `createAuctionBook` (function) · `import { createAuctionBook } from "@jgengine/core/gameplay"`
|
|
@@ -36,6 +44,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
36
44
|
|
|
37
45
|
- `cancelJob` (function) · `import { cancelJob } from "@jgengine/core/gameplay"`
|
|
38
46
|
|
|
47
|
+
## career-bank — persistent career money with a run haul that can be banked or lost
|
|
48
|
+
|
|
49
|
+
- `createCareerBank` (function) · `import { createCareerBank } from "@jgengine/core/progression/careerBank"`
|
|
50
|
+
|
|
39
51
|
## carry-encumbrance — gate a pickup against the carrier's remaining capacity
|
|
40
52
|
|
|
41
53
|
- `canCarry` (function) · `import { canCarry } from "@jgengine/core/inventory/carryLoad"`
|
|
@@ -232,6 +244,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
232
244
|
|
|
233
245
|
- `splitStack` (function) · `import { splitStack } from "@jgengine/core/inventory/inventoryModel"`
|
|
234
246
|
|
|
247
|
+
## item-comparison — show per-stat deltas and a weighted verdict for a drop against equipped gear
|
|
248
|
+
|
|
249
|
+
- `compareToEquipped` (function) · `import { compareToEquipped } from "@jgengine/core/loot/drop"`
|
|
250
|
+
|
|
235
251
|
## item-generation — compose constraints, weighted pools, transforms, and provenance into a deterministic generated item
|
|
236
252
|
|
|
237
253
|
- `generate` (function) · `import { generate } from "@jgengine/core/gameplay"`
|
|
@@ -253,10 +269,46 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
253
269
|
- `createItemInstanceRegistry` (function) · `import { createItemInstanceRegistry } from "@jgengine/core/gameplay"`
|
|
254
270
|
- `proceduralLootEntry` (function) · `import { proceduralLootEntry } from "@jgengine/core/gameplay"`
|
|
255
271
|
|
|
272
|
+
## item-ledger — track which rolled item instances are still owned, salvaged or sold
|
|
273
|
+
|
|
274
|
+
- `createItemLedger` (function) · `import { createItemLedger } from "@jgengine/core/loot/salvage"`
|
|
275
|
+
|
|
276
|
+
## item-roll — roll one deterministic, level-gated item from a seed
|
|
277
|
+
|
|
278
|
+
- `rollDrop` (function) · `import { rollDrop } from "@jgengine/core/loot/drop"`
|
|
279
|
+
|
|
280
|
+
## item-run-haul — carry loot with a weight gate, bank it into a persistent stash, or lose it when the run fails
|
|
281
|
+
|
|
282
|
+
- `createRunHaul` (function) · `import { createRunHaul } from "@jgengine/core/loot/haul"`
|
|
283
|
+
|
|
284
|
+
## item-salvage — break an item down for the currency its level and rarity say, once
|
|
285
|
+
|
|
286
|
+
- `salvageItem` (function) · `import { salvageItem } from "@jgengine/core/loot/salvage"`
|
|
287
|
+
|
|
288
|
+
## item-salvage-value — compute the currency an item of a level and rarity breaks down into
|
|
289
|
+
|
|
290
|
+
- `salvageValue` (function) · `import { salvageValue } from "@jgengine/core/loot/salvage"`
|
|
291
|
+
|
|
256
292
|
## item-use-composition — compose pluggable, ordered item use-behaviors from serializable references
|
|
257
293
|
|
|
258
294
|
- `createUseBehaviorRegistry` (function) · `import { createUseBehaviorRegistry } from "@jgengine/core/gameplay"`
|
|
259
295
|
|
|
296
|
+
## item-vendor — stock a vendor with rolled items priced through the shared economy
|
|
297
|
+
|
|
298
|
+
- `stockVendor` (function) · `import { stockVendor } from "@jgengine/core/loot/vendor"`
|
|
299
|
+
|
|
300
|
+
## item-vendor-price — price an item from its level and rarity through the economy's currency
|
|
301
|
+
|
|
302
|
+
- `itemPrice` (function) · `import { itemPrice } from "@jgengine/core/loot/vendor"`
|
|
303
|
+
|
|
304
|
+
## item-vendor-rotation — decide when a vendor's shelf is due for a restock
|
|
305
|
+
|
|
306
|
+
- `createVendorRotation` (function) · `import { createVendorRotation } from "@jgengine/core/loot/vendor"`
|
|
307
|
+
|
|
308
|
+
## item-weight — read a rolled item's mass for a carry check
|
|
309
|
+
|
|
310
|
+
- `itemMass` (function) · `import { itemMass } from "@jgengine/core/loot/drop"`
|
|
311
|
+
|
|
260
312
|
## key-rebinding — conflict-aware key-remap session over the action-binding model — capture/reassign, conflict detection, reset-to-defaults, persist overrides, snapshot/restore
|
|
261
313
|
|
|
262
314
|
- `createRebindSession` (function) · `import { createRebindSession } from "@jgengine/core/gameplay"`
|
|
@@ -392,6 +444,12 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
392
444
|
|
|
393
445
|
- `slotAccepts` (function) · `import { slotAccepts } from "@jgengine/core/gameplay"`
|
|
394
446
|
|
|
447
|
+
## mood — a mood derived from how satisfied an agent's needs are
|
|
448
|
+
|
|
449
|
+
- `createMoodReader` (function) · `import { createMoodReader } from "@jgengine/core/agents/mood"`
|
|
450
|
+
- `moodTierOf` (function) · `import { moodTierOf } from "@jgengine/core/agents/mood"`
|
|
451
|
+
- `readMood` (function) · `import { readMood } from "@jgengine/core/agents/mood"`
|
|
452
|
+
|
|
395
453
|
## move — a keyed value toward a target by a bounded step
|
|
396
454
|
|
|
397
455
|
- `towardValue` (function) · `import { towardValue } from "@jgengine/core/gameplay"`
|
|
@@ -537,6 +595,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
537
595
|
|
|
538
596
|
- `attachRefusalNotices` (function) · `import { attachRefusalNotices } from "@jgengine/core/game/notices"`
|
|
539
597
|
|
|
598
|
+
## relationships — familiarity and opinion between agents, with a bond that changes behaviour
|
|
599
|
+
|
|
600
|
+
- `createRelationshipBook` (function) · `import { createRelationshipBook } from "@jgengine/core/agents/relationships"`
|
|
601
|
+
|
|
540
602
|
## resolve — the current tier/band label for a keyed or scalar value
|
|
541
603
|
|
|
542
604
|
- `tierAt` (function) · `import { tierAt } from "@jgengine/core/gameplay"`
|
|
@@ -651,6 +713,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
651
713
|
|
|
652
714
|
- `stationOutputRate` (function) · `import { stationOutputRate } from "@jgengine/core/gameplay"`
|
|
653
715
|
|
|
716
|
+
## standing — per-track standing moved by authored outcomes, with tiers and one snapshot
|
|
717
|
+
|
|
718
|
+
- `createStanding` (function) · `import { createStanding } from "@jgengine/core/progression/standing"`
|
|
719
|
+
|
|
654
720
|
## stat-graph — derive game-defined stats from named inputs with provenance and preview
|
|
655
721
|
|
|
656
722
|
- `createStatGraph` (function) · `import { createStatGraph } from "@jgengine/core/gameplay"`
|
|
@@ -724,6 +790,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
724
790
|
|
|
725
791
|
- `unitTrainingConfig` (function) · `import { unitTrainingConfig } from "@jgengine/core/gameplay"`
|
|
726
792
|
|
|
793
|
+
## unlock-gates — milestones gated by standing or money, validated against the content they grant
|
|
794
|
+
|
|
795
|
+
- `createGateSet` (function) · `import { createGateSet } from "@jgengine/core/progression/gates"`
|
|
796
|
+
|
|
727
797
|
## unlock-points — spendable point pool earned per level and spent to unlock nodes, with refund and respec
|
|
728
798
|
|
|
729
799
|
- `createUnlockPoints` (function) · `import { createUnlockPoints } from "@jgengine/core/economy/unlockPoints"`
|
|
@@ -732,6 +802,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
732
802
|
|
|
733
803
|
- `createUnlockCatalog` (function) · `import { createUnlockCatalog } from "@jgengine/core/gameplay"`
|
|
734
804
|
|
|
805
|
+
## upgrade-tree — ranked upgrades priced by a cost curve, gated by prerequisites and standing
|
|
806
|
+
|
|
807
|
+
- `createUpgradeTree` (function) · `import { createUpgradeTree } from "@jgengine/core/progression/upgrades"`
|
|
808
|
+
|
|
735
809
|
## visitor-population — a crowd whose on-site and daily counts are the bodies the game made
|
|
736
810
|
|
|
737
811
|
- `createVisitorPopulation` (function) · `import { createVisitorPopulation } from "@jgengine/core/gameplay"`
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Agent lives: needs, mood, relationships, schedules
|
|
2
|
+
|
|
3
|
+
One system for every resident that wants something. `createAgent` (`@jgengine/core/agents/agent`)
|
|
4
|
+
composes four owners that already exist: `createNeedSet` (`sim/needs`) decays the needs,
|
|
5
|
+
`createUtilityBrain` (`ai/utilityBrain`) scores what to do, `resolveRoutine` (`sim/routine`) says
|
|
6
|
+
where the day plan puts the agent, and `createMoodReader` (`agents/mood`) reads how satisfied it is.
|
|
7
|
+
A game supplies the targets its world offers and owns the body, the nav and the art.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createAgent } from "@jgengine/core/agents/agent";
|
|
11
|
+
import { createRelationshipBook } from "@jgengine/core/agents/relationships";
|
|
12
|
+
|
|
13
|
+
const needs = [
|
|
14
|
+
{ id: "hunger", max: 100, rate: 0.9 },
|
|
15
|
+
{ id: "fun", max: 100, rate: 0.6 },
|
|
16
|
+
{ id: "energy", max: 100, rate: 0.5 },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const agent = createAgent({
|
|
20
|
+
id: "guest:1",
|
|
21
|
+
needs,
|
|
22
|
+
options: [
|
|
23
|
+
{ id: "eat", terms: [{ need: "hunger", curve: { kind: "linear", from: 0.2, to: 0.95 } }] },
|
|
24
|
+
{ id: "ride", terms: [{ need: "fun", curve: { kind: "linear", from: 0.15, to: 0.9 } }] },
|
|
25
|
+
{ id: "seat", base: 0.05, terms: [{ need: "energy", curve: { kind: "linear", from: 0.1, to: 1 } }] },
|
|
26
|
+
{ id: "work", base: 0.2 },
|
|
27
|
+
],
|
|
28
|
+
schedule: {
|
|
29
|
+
cycleSeconds: 86_400,
|
|
30
|
+
stops: [
|
|
31
|
+
{ place: "home", arriveAt: 0, activity: "sleep", refill: { energy: 70 } },
|
|
32
|
+
{ place: "lobby", arriveAt: 30_000, activity: "work", beat: ["hall-a", "hall-b", "hall-c"] },
|
|
33
|
+
{ place: "cafe", arriveAt: 50_000, activity: "meal", refill: { hunger: 60 } },
|
|
34
|
+
],
|
|
35
|
+
travelSeconds: (from, to) => distances.get(`${from}>${to}`) ?? 60,
|
|
36
|
+
},
|
|
37
|
+
mood: { weights: [{ need: "hunger", weight: 2 }, { need: "fun", weight: 1 }] },
|
|
38
|
+
rng: ctx.rng,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
agent.update({
|
|
42
|
+
dt,
|
|
43
|
+
now: ctx.time.now(),
|
|
44
|
+
targets: [
|
|
45
|
+
{ option: "ride", target: "coaster", cost: queueSeconds, fallback: { option: "seat", target: "bench" } },
|
|
46
|
+
{ option: "eat", target: "burger-stall", cost: walkSeconds },
|
|
47
|
+
{ option: "seat", target: "bench" },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Read the frame, not the internals: `frame.action` (`option`, `target`, `usedFallback`), `frame.mood`
|
|
53
|
+
(`score`, `tier`, `label`), `frame.phase` and `frame.post` (where the schedule and the work beat put
|
|
54
|
+
the agent), and `frame.changed` with `frame.reason`. `targets` is the world as it is this tick, so a
|
|
55
|
+
sold-out stall simply is not in the list, and a pick whose target disappears moves to its declared
|
|
56
|
+
`fallback` instead of freezing.
|
|
57
|
+
|
|
58
|
+
- **Saved state.** `agent.snapshot()` carries needs (values, rates, modifiers), mood sources, the held
|
|
59
|
+
pick and its dials, the action, the far flag and the schedule stop the agent last ate at. The day
|
|
60
|
+
plan itself is authored data resolved from the clock, so it is never saved. Put every agent's
|
|
61
|
+
snapshot and one `book.snapshot()` in the save document.
|
|
62
|
+
- **Far agents.** `agent.setFar(true)` stops the utility pass: the agent keeps decaying and follows
|
|
63
|
+
its schedule, and its action reads off the phase. A far agent costs no decision and no nav.
|
|
64
|
+
- **Mood from the world.** `agent.moodSources.setSource("queue", -20)` and `clearSource` let a queue,
|
|
65
|
+
litter, a broken ride or a cold snap move the feeling without giving mood its own state machine.
|
|
66
|
+
- **One relationship book per world.** `book.socialise(a, b, { opinion: 6, familiarity: 0.1 })` returns
|
|
67
|
+
the pair before and after plus the milestones crossed. `book.prefersCompanyOf(a, b)` is a friendship
|
|
68
|
+
a game can act on by seating them together; `book.refusesToSplit(a, b)` is a bond that must not be
|
|
69
|
+
posted to separate shifts. A bond needs opinion *and* time shared, so one good afternoon is not
|
|
70
|
+
enough.
|
|
71
|
+
- **A work beat is not a special case.** A stop with `beat` walks those places in order for
|
|
72
|
+
`beatDwellSeconds` each; a janitor, a security guard and a tour guide are the same agent with
|
|
73
|
+
different authored beats. Off-hours behaviour is a stop without a beat.
|
|
74
|
+
- **Determinism.** Pass `ctx.rng` and set `jitter`; two agents built from the same seeded stream make
|
|
75
|
+
the same choices. The game owns the RNG cursor in its save (`createGameSave` `rng`), since one
|
|
76
|
+
stream is usually shared by the whole world.
|