@its-not-rocket-science/ananke 0.1.69 → 0.5.4
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 +288 -0
- package/README.md +150 -432
- package/STABLE_API.md +113 -490
- package/conformance/README.md +46 -44
- package/conformance/lockstep-sequence.json +395 -395
- package/conformance/phase-order.json +21 -0
- package/conformance/replay-parity.json +65 -65
- package/conformance/state-hash-regression.json +39 -0
- package/conformance/state-hash.json +23 -22
- package/dist/src/conformance.d.ts +8 -2
- package/dist/src/content/index.d.ts +3 -0
- package/dist/src/content/index.js +2 -0
- package/dist/src/content/injector.d.ts +19 -0
- package/dist/src/content/injector.js +61 -0
- package/dist/src/content/loader.d.ts +3 -0
- package/dist/src/content/loader.js +170 -0
- package/dist/src/content/types.d.ts +56 -0
- package/dist/src/content/types.js +1 -0
- package/dist/src/content/validator.d.ts +6 -0
- package/dist/src/content/validator.js +14 -0
- package/dist/src/content-pack.d.ts +3 -2
- package/dist/src/content-pack.js +205 -10
- package/dist/src/data-governance.d.ts +73 -0
- package/dist/src/data-governance.js +188 -0
- package/dist/src/debug.d.ts +6 -0
- package/dist/src/debug.js +9 -0
- package/dist/src/determinism.d.ts +12 -0
- package/dist/src/determinism.js +93 -0
- package/dist/src/dialogue.d.ts +41 -0
- package/dist/src/dialogue.js +113 -2
- package/dist/src/economy.d.ts +97 -3
- package/dist/src/economy.js +191 -37
- package/dist/src/history/autosave.d.ts +19 -0
- package/dist/src/history/autosave.js +98 -0
- package/dist/src/history/timetravel.d.ts +28 -0
- package/dist/src/history/timetravel.js +61 -0
- package/dist/src/index.d.ts +19 -31
- package/dist/src/index.js +20 -38
- package/dist/src/model3d.js +63 -2
- package/dist/src/narrative/combat-logger.d.ts +25 -0
- package/dist/src/narrative/combat-logger.js +97 -0
- package/dist/src/narrative/plausibility.d.ts +30 -0
- package/dist/src/narrative/plausibility.js +70 -0
- package/dist/src/narrative-layer.d.ts +2 -0
- package/dist/src/narrative-layer.js +2 -0
- package/dist/src/navigation/causal-chain.d.ts +26 -0
- package/dist/src/navigation/causal-chain.js +74 -0
- package/dist/src/netcode.d.ts +1 -1
- package/dist/src/netcode.js +1 -1
- package/dist/src/performance/adaptive-tick.d.ts +23 -0
- package/dist/src/performance/adaptive-tick.js +50 -0
- package/dist/src/plugins/loader.d.ts +23 -0
- package/dist/src/plugins/loader.js +86 -0
- package/dist/src/plugins/registry.d.ts +11 -0
- package/dist/src/plugins/registry.js +28 -0
- package/dist/src/plugins/types.d.ts +31 -0
- package/dist/src/plugins/types.js +7 -0
- package/dist/src/replay.js +5 -2
- package/dist/src/scenario.js +2 -1
- package/dist/src/serialization/binary.d.ts +5 -0
- package/dist/src/serialization/binary.js +193 -0
- package/dist/src/session.d.ts +115 -0
- package/dist/src/session.js +228 -0
- package/dist/src/sim/ai/decide.js +1 -1
- package/dist/src/sim/ai/personality.js +2 -2
- package/dist/src/sim/bodyplan.d.ts +30 -0
- package/dist/src/sim/context.d.ts +2 -0
- package/dist/src/sim/kernel.js +80 -1294
- package/dist/src/sim/normalization.d.ts +12 -0
- package/dist/src/sim/normalization.js +92 -0
- package/dist/src/sim/resolvers/attack-resolver.d.ts +30 -0
- package/dist/src/sim/resolvers/attack-resolver.js +310 -0
- package/dist/src/sim/resolvers/capability-resolver.d.ts +15 -0
- package/dist/src/sim/resolvers/capability-resolver.js +270 -0
- package/dist/src/sim/resolvers/grapple-resolver.d.ts +28 -0
- package/dist/src/sim/resolvers/grapple-resolver.js +70 -0
- package/dist/src/sim/resolvers/shoot-resolver.d.ts +27 -0
- package/dist/src/sim/resolvers/shoot-resolver.js +212 -0
- package/dist/src/sim/resolvers/treat-resolver.d.ts +16 -0
- package/dist/src/sim/resolvers/treat-resolver.js +77 -0
- package/dist/src/sim/step/apply/intents.d.ts +6 -0
- package/dist/src/sim/step/apply/intents.js +83 -0
- package/dist/src/sim/step/kernel-pipeline.d.ts +9 -0
- package/dist/src/sim/step/kernel-pipeline.js +15 -0
- package/dist/src/sim/step/phases/capability-phase.d.ts +8 -0
- package/dist/src/sim/step/phases/capability-phase.js +65 -0
- package/dist/src/sim/step/phases/cooldowns-phase.d.ts +3 -0
- package/dist/src/sim/step/phases/cooldowns-phase.js +37 -0
- package/dist/src/sim/step/phases/prepare-phase.d.ts +10 -0
- package/dist/src/sim/step/phases/prepare-phase.js +33 -0
- package/dist/src/sim/step/pipeline.d.ts +2 -0
- package/dist/src/sim/step/pipeline.js +15 -0
- package/dist/src/sim/step/resolvers/impact-resolver.d.ts +9 -0
- package/dist/src/sim/step/resolvers/impact-resolver.js +55 -0
- package/dist/src/sim/step/world-phases.d.ts +7 -0
- package/dist/src/sim/step/world-phases.js +15 -0
- package/dist/src/sim/step/world-step-context.d.ts +23 -0
- package/dist/src/sim/step/world-step-context.js +1 -0
- package/dist/src/sim/systemic-toxicology.js +1 -1
- package/dist/src/sim/team.js +2 -2
- package/dist/src/sim/testing.js +4 -3
- package/dist/src/sim/toxicology.d.ts +1 -1
- package/dist/src/sim/toxicology.js +1 -1
- package/dist/src/sim/world.d.ts +17 -24
- package/dist/src/snapshot.js +3 -6
- package/dist/src/tier2.d.ts +19 -0
- package/dist/src/tier2.js +21 -0
- package/dist/src/tier3.d.ts +10 -0
- package/dist/src/tier3.js +12 -0
- package/dist/src/version.d.ts +6 -0
- package/dist/src/version.js +8 -0
- package/dist/src/wasm/bridge.d.ts +14 -0
- package/dist/src/wasm/bridge.js +84 -0
- package/dist/src/wasm-kernel.d.ts +2 -0
- package/dist/src/wasm-kernel.js +1 -0
- package/dist/src/world-evolution-backend/engine.d.ts +4 -0
- package/dist/src/world-evolution-backend/engine.js +347 -0
- package/dist/src/world-evolution-backend/host-schema.d.ts +145 -0
- package/dist/src/world-evolution-backend/host-schema.js +375 -0
- package/dist/src/world-evolution-backend/index.d.ts +1 -0
- package/dist/src/world-evolution-backend/index.js +1 -0
- package/dist/src/world-evolution-backend/open-world-host-adapter.d.ts +96 -0
- package/dist/src/world-evolution-backend/open-world-host-adapter.js +208 -0
- package/dist/src/world-evolution-backend/profiles.d.ts +4 -0
- package/dist/src/world-evolution-backend/profiles.js +136 -0
- package/dist/src/world-evolution-backend/public.d.ts +11 -0
- package/dist/src/world-evolution-backend/public.js +7 -0
- package/dist/src/world-evolution-backend/reproducibility.d.ts +8 -0
- package/dist/src/world-evolution-backend/reproducibility.js +35 -0
- package/dist/src/world-evolution-backend/timeline.d.ts +6 -0
- package/dist/src/world-evolution-backend/timeline.js +337 -0
- package/dist/src/world-evolution-backend/types.d.ts +138 -0
- package/dist/src/world-evolution-backend/types.js +1 -0
- package/dist/src/world-evolution-host-backend.d.ts +51 -0
- package/dist/src/world-evolution-host-backend.js +97 -0
- package/dist/src/world-evolution.d.ts +201 -0
- package/dist/src/world-evolution.js +543 -0
- package/dist/src/world-factory.js +2 -1
- package/dist/tools/pack-cli.js +55 -2
- package/docs/bridge-contract.md +140 -332
- package/docs/completion-evidence-dashboard.md +63 -0
- package/docs/host-contract.md +86 -310
- package/docs/host-world-evolution-schema.md +155 -0
- package/docs/integration-primer.md +29 -22
- package/docs/migration-monolith-to-modular.md +102 -120
- package/docs/package-architecture.md +116 -323
- package/docs/plugins/README.md +42 -0
- package/docs/plugins/hooks.md +22 -0
- package/docs/plugins/publishing.md +29 -0
- package/docs/plugins/security-model.md +273 -0
- package/docs/project-overview.md +30 -30
- package/docs/release-dashboard.md +47 -0
- package/docs/session-api.md +127 -0
- package/docs/stable-api-manifest.json +52 -0
- package/docs/trust-dashboard.md +37 -0
- package/docs/versioning.md +256 -245
- package/docs/wire-protocol.md +140 -209
- package/docs/world-evolution-backend.md +200 -0
- package/docs/world-evolution-engine.md +150 -0
- package/docs/world-evolution-host-backend.md +70 -0
- package/docs/world-evolution-orchestration.md +248 -0
- package/package.json +125 -6
- package/schema/content-pack.schema.json +85 -0
- package/schema/plugin.schema.json +57 -0
- package/schema/world-evolution-input.schema.json +196 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SCALE, clampQ, mulDiv, q } from "../../units.js";
|
|
2
|
+
import { FRACTURE_THRESHOLD } from "../injury.js";
|
|
3
|
+
import { TIER_RANK, TIER_MUL, ACTION_MIN_TIER, TIER_TECH_REQ } from "../medical.js";
|
|
4
|
+
import { getSkill } from "../skills.js";
|
|
5
|
+
import { TraceKinds } from "../kinds.js";
|
|
6
|
+
export function resolveTreat(options) {
|
|
7
|
+
const { world, treater, cmd, index, trace, ctx } = options;
|
|
8
|
+
if (treater.injury.dead)
|
|
9
|
+
return;
|
|
10
|
+
const target = index.byId.get(cmd.targetId);
|
|
11
|
+
if (!target || target.injury.dead)
|
|
12
|
+
return;
|
|
13
|
+
const dx = target.position_m.x - treater.position_m.x;
|
|
14
|
+
const dy = target.position_m.y - treater.position_m.y;
|
|
15
|
+
const dist2 = dx * dx + dy * dy;
|
|
16
|
+
const MAX_TREAT_DIST_m = Math.trunc(2 * SCALE.m);
|
|
17
|
+
if (dist2 > MAX_TREAT_DIST_m * MAX_TREAT_DIST_m)
|
|
18
|
+
return;
|
|
19
|
+
const tierRank = TIER_RANK[cmd.tier];
|
|
20
|
+
const actionMinRank = TIER_RANK[ACTION_MIN_TIER[cmd.action]];
|
|
21
|
+
if (tierRank < actionMinRank)
|
|
22
|
+
return;
|
|
23
|
+
const techReq = TIER_TECH_REQ[cmd.tier];
|
|
24
|
+
if (techReq && ctx.techCtx && !ctx.techCtx.available.has(techReq))
|
|
25
|
+
return;
|
|
26
|
+
const tierMul = TIER_MUL[cmd.tier];
|
|
27
|
+
const medSkill = getSkill(treater.skills, "medical");
|
|
28
|
+
const effectMul = mulDiv(tierMul, medSkill.treatmentRateMul, SCALE.Q);
|
|
29
|
+
if (cmd.action === "tourniquet") {
|
|
30
|
+
const reg = cmd.regionId ? target.injury.byRegion[cmd.regionId] : undefined;
|
|
31
|
+
if (!reg)
|
|
32
|
+
return;
|
|
33
|
+
reg.bleedingRate = q(0);
|
|
34
|
+
reg.bleedDuration_ticks = 0;
|
|
35
|
+
target.injury.shock = clampQ(target.injury.shock + q(0.005), 0, SCALE.Q);
|
|
36
|
+
}
|
|
37
|
+
else if (cmd.action === "bandage") {
|
|
38
|
+
const reg = cmd.regionId ? target.injury.byRegion[cmd.regionId] : undefined;
|
|
39
|
+
if (!reg)
|
|
40
|
+
return;
|
|
41
|
+
const BASE_BANDAGE_RATE = q(0.0050);
|
|
42
|
+
const reduction = mulDiv(BASE_BANDAGE_RATE, effectMul, SCALE.Q);
|
|
43
|
+
reg.bleedingRate = clampQ((reg.bleedingRate - reduction), q(0), q(1.0));
|
|
44
|
+
}
|
|
45
|
+
else if (cmd.action === "surgery") {
|
|
46
|
+
const reg = cmd.regionId ? target.injury.byRegion[cmd.regionId] : undefined;
|
|
47
|
+
if (!reg)
|
|
48
|
+
return;
|
|
49
|
+
const BASE_SURGERY_RATE = q(0.0020);
|
|
50
|
+
const BASE_BANDAGE_RATE = q(0.0050);
|
|
51
|
+
const strReduction = mulDiv(BASE_SURGERY_RATE, effectMul, SCALE.Q);
|
|
52
|
+
const newStr = clampQ((reg.structuralDamage - strReduction), reg.permanentDamage, SCALE.Q);
|
|
53
|
+
reg.structuralDamage = newStr;
|
|
54
|
+
const bleedReduction = mulDiv(BASE_BANDAGE_RATE, effectMul, SCALE.Q);
|
|
55
|
+
reg.bleedingRate = clampQ((reg.bleedingRate - bleedReduction), q(0), q(1.0));
|
|
56
|
+
if (reg.fractured && reg.structuralDamage < FRACTURE_THRESHOLD) {
|
|
57
|
+
reg.fractured = false;
|
|
58
|
+
}
|
|
59
|
+
if (reg.infectedTick >= 0 && tierRank >= TIER_RANK["surgicalKit"]) {
|
|
60
|
+
reg.infectedTick = -1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (cmd.action === "fluidReplacement") {
|
|
64
|
+
const BASE_FLUID_RATE = q(0.0050);
|
|
65
|
+
const recovery = mulDiv(BASE_FLUID_RATE, effectMul, SCALE.Q);
|
|
66
|
+
target.injury.fluidLoss = clampQ((target.injury.fluidLoss - recovery), q(0), SCALE.Q);
|
|
67
|
+
target.injury.shock = clampQ((target.injury.shock - q(0.002)), q(0), SCALE.Q);
|
|
68
|
+
}
|
|
69
|
+
trace.onEvent({
|
|
70
|
+
kind: TraceKinds.TreatmentApplied,
|
|
71
|
+
tick: world.tick,
|
|
72
|
+
treaterId: treater.id,
|
|
73
|
+
targetId: target.id,
|
|
74
|
+
action: cmd.action,
|
|
75
|
+
...(cmd.regionId !== undefined ? { regionId: cmd.regionId } : {}),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "../../commands.js";
|
|
2
|
+
import type { Entity } from "../../entity.js";
|
|
3
|
+
import type { SimulationTuning } from "../../tuning.js";
|
|
4
|
+
export declare function applyCommands(e: Entity, commands: readonly Command[]): void;
|
|
5
|
+
export declare function applyFunctionalGating(e: Entity, tuning: SimulationTuning): void;
|
|
6
|
+
export declare function applyStandAndKO(e: Entity, tuning: SimulationTuning): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { deriveFunctionalState, hasAllDisabledFunctions } from "../../impairment.js";
|
|
2
|
+
import { SCALE, clampQ, q } from "../../../units.js";
|
|
3
|
+
export function applyCommands(e, commands) {
|
|
4
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
5
|
+
for (const c of commands) {
|
|
6
|
+
if (c.kind === "setProne")
|
|
7
|
+
e.condition.prone = c.prone;
|
|
8
|
+
else if (c.kind === "move")
|
|
9
|
+
e.intent.move = { dir: c.dir, intensity: c.intensity, mode: c.mode };
|
|
10
|
+
else if (c.kind === "defend")
|
|
11
|
+
e.intent.defence = { mode: c.mode, intensity: clampQ(c.intensity, 0, SCALE.Q) };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function applyFunctionalGating(e, tuning) {
|
|
15
|
+
const func = deriveFunctionalState(e, tuning);
|
|
16
|
+
if (!func.canAct) {
|
|
17
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
18
|
+
e.intent.move = { dir: { x: 0, y: 0, z: 0 }, intensity: q(0), mode: "walk" };
|
|
19
|
+
if (tuning.realism !== "arcade")
|
|
20
|
+
e.condition.prone = true;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (e.condition.pinned && tuning.realism !== "arcade") {
|
|
24
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
25
|
+
e.condition.prone = true;
|
|
26
|
+
}
|
|
27
|
+
if (e.energy.reserveEnergy_J <= 0 && tuning.realism !== "arcade") {
|
|
28
|
+
e.condition.prone = true;
|
|
29
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
30
|
+
}
|
|
31
|
+
if (!func.canStand && tuning.realism !== "arcade")
|
|
32
|
+
e.condition.prone = true;
|
|
33
|
+
if (tuning.realism !== "arcade") {
|
|
34
|
+
const armsOut = hasAllDisabledFunctions(func, "leftManipulation", "rightManipulation");
|
|
35
|
+
if (armsOut && (e.intent.defence.mode === "block" || e.intent.defence.mode === "parry")) {
|
|
36
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
37
|
+
}
|
|
38
|
+
const legsOut = hasAllDisabledFunctions(func, "leftLocomotion", "rightLocomotion");
|
|
39
|
+
if (legsOut && e.intent.move.mode === "sprint") {
|
|
40
|
+
e.intent.move = { ...e.intent.move, mode: "walk" };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function applyStandAndKO(e, tuning) {
|
|
45
|
+
const wasUnconscious = e.condition.unconsciousTicks > 0;
|
|
46
|
+
if (e.injury.consciousness <= tuning.unconsciousThreshold) {
|
|
47
|
+
if (!wasUnconscious) {
|
|
48
|
+
e.condition.unconsciousTicks = tuning.unconsciousBaseTicks;
|
|
49
|
+
e.condition.prone = true;
|
|
50
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
51
|
+
e.intent.move = { dir: { x: 0, y: 0, z: 0 }, intensity: q(0), mode: "walk" };
|
|
52
|
+
if (tuning.dropWeaponsOnUnconscious) {
|
|
53
|
+
e.loadout.items = e.loadout.items.filter(it => it.kind !== "weapon");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
e.condition.prone = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (e.condition.unconsciousTicks > 0) {
|
|
61
|
+
e.intent.defence = { mode: "none", intensity: q(0) };
|
|
62
|
+
e.intent.move = { dir: { x: 0, y: 0, z: 0 }, intensity: q(0), mode: "walk" };
|
|
63
|
+
e.condition.prone = true;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!e.intent.prone && e.condition.prone) {
|
|
67
|
+
if (tuning.realism === "arcade") {
|
|
68
|
+
e.condition.prone = false;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (e.condition.standBlockedTicks > 0) {
|
|
72
|
+
e.condition.prone = true;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const func = deriveFunctionalState(e, tuning);
|
|
76
|
+
const slow = (SCALE.Q - func.mobilityMul);
|
|
77
|
+
const extra = Math.trunc((slow * tuning.standUpMaxExtraTicks) / SCALE.Q);
|
|
78
|
+
const ticks = tuning.standUpBaseTicks + extra;
|
|
79
|
+
e.condition.standBlockedTicks = Math.max(1, ticks);
|
|
80
|
+
e.condition.prone = true;
|
|
81
|
+
e.intent.prone = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandMap } from "../commands.js";
|
|
2
|
+
import type { KernelContext } from "../context.js";
|
|
3
|
+
import type { WorldState } from "../world.js";
|
|
4
|
+
export interface KernelPhase {
|
|
5
|
+
name: string;
|
|
6
|
+
responsibility: string;
|
|
7
|
+
run(world: WorldState, cmds: CommandMap, ctx: KernelContext): void;
|
|
8
|
+
}
|
|
9
|
+
export declare const KERNEL_PHASE_ORDER: readonly ["prepare", "cooldowns", "capabilityCasting", "intent", "movement", "hazardsAndPush", "actions", "grappleMaintenance", "impactResolution", "effectPropagation", "physiology", "morale", "finalize"];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const KERNEL_PHASE_ORDER = [
|
|
2
|
+
"prepare",
|
|
3
|
+
"cooldowns",
|
|
4
|
+
"capabilityCasting",
|
|
5
|
+
"intent",
|
|
6
|
+
"movement",
|
|
7
|
+
"hazardsAndPush",
|
|
8
|
+
"actions",
|
|
9
|
+
"grappleMaintenance",
|
|
10
|
+
"impactResolution",
|
|
11
|
+
"effectPropagation",
|
|
12
|
+
"physiology",
|
|
13
|
+
"morale",
|
|
14
|
+
"finalize",
|
|
15
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CapabilityEffect } from "../../capability.js";
|
|
2
|
+
import type { Entity } from "../../entity.js";
|
|
3
|
+
import type { TraceSink } from "../../trace.js";
|
|
4
|
+
import type { WorldState } from "../../world.js";
|
|
5
|
+
export interface CapabilityPhaseDeps {
|
|
6
|
+
applyCapabilityEffect: (world: WorldState, actor: Entity, targetId: number | undefined, effect: CapabilityEffect, trace: TraceSink, tick: number) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function runCapabilityPhase(world: WorldState, trace: TraceSink, deps: CapabilityPhaseDeps): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { q } from "../../../units.js";
|
|
2
|
+
import { TraceKinds } from "../../kinds.js";
|
|
3
|
+
import { stepConcentration } from "../concentration.js";
|
|
4
|
+
const CAST_INTERRUPT_SHOCK = q(0.30);
|
|
5
|
+
export function runCapabilityPhase(world, trace, deps) {
|
|
6
|
+
for (const e of world.entities) {
|
|
7
|
+
if (!e.pendingActivation || e.injury.dead)
|
|
8
|
+
continue;
|
|
9
|
+
if (e.injury.shock >= CAST_INTERRUPT_SHOCK) {
|
|
10
|
+
trace.onEvent({ kind: TraceKinds.CastInterrupted, tick: world.tick, entityId: e.id });
|
|
11
|
+
delete e.pendingActivation;
|
|
12
|
+
}
|
|
13
|
+
else if (world.tick >= e.pendingActivation.resolveAtTick) {
|
|
14
|
+
const src = e.capabilitySources?.find(s => s.id === e.pendingActivation.sourceId);
|
|
15
|
+
const eff = src?.effects.find(ef => ef.id === e.pendingActivation.effectId);
|
|
16
|
+
if (src && eff) {
|
|
17
|
+
deps.applyCapabilityEffect(world, e, e.pendingActivation.targetId, eff, trace, world.tick);
|
|
18
|
+
trace.onEvent({ kind: TraceKinds.CapabilityActivated, tick: world.tick, entityId: e.id, sourceId: src.id, effectId: eff.id });
|
|
19
|
+
if (eff.sustainedTicks && eff.sustainedTicks > 1) {
|
|
20
|
+
e.action.sustainedEmission = {
|
|
21
|
+
sourceId: src.id,
|
|
22
|
+
effectId: eff.id,
|
|
23
|
+
...(e.pendingActivation.targetId !== undefined ? { targetId: e.pendingActivation.targetId } : {}),
|
|
24
|
+
remainingTicks: eff.sustainedTicks - 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
delete e.pendingActivation;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const e of world.entities) {
|
|
32
|
+
if (!e.activeConcentration || e.injury.dead)
|
|
33
|
+
continue;
|
|
34
|
+
stepConcentration(e, world, trace, world.tick);
|
|
35
|
+
}
|
|
36
|
+
for (const e of world.entities) {
|
|
37
|
+
if (!e.action.sustainedEmission || e.injury.dead)
|
|
38
|
+
continue;
|
|
39
|
+
const em = e.action.sustainedEmission;
|
|
40
|
+
if (e.injury.shock >= CAST_INTERRUPT_SHOCK) {
|
|
41
|
+
trace.onEvent({ kind: TraceKinds.CastInterrupted, tick: world.tick, entityId: e.id });
|
|
42
|
+
delete e.action.sustainedEmission;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const src = e.capabilitySources?.find(s => s.id === em.sourceId);
|
|
46
|
+
const eff = src?.effects.find(ef => ef.id === em.effectId);
|
|
47
|
+
if (!src || !eff) {
|
|
48
|
+
delete e.action.sustainedEmission;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const isBoundless = src.regenModel.type === "boundless";
|
|
52
|
+
if (!isBoundless) {
|
|
53
|
+
if (src.reserve_J < eff.cost_J) {
|
|
54
|
+
delete e.action.sustainedEmission;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
src.reserve_J -= eff.cost_J;
|
|
58
|
+
}
|
|
59
|
+
deps.applyCapabilityEffect(world, e, em.targetId, eff, trace, world.tick);
|
|
60
|
+
em.remainingTicks -= 1;
|
|
61
|
+
if (em.remainingTicks <= 0) {
|
|
62
|
+
delete e.action.sustainedEmission;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { q, qMul } from "../../../units.js";
|
|
2
|
+
import { TraceKinds } from "../../kinds.js";
|
|
3
|
+
const SWING_MOMENTUM_DECAY = q(0.95);
|
|
4
|
+
export function runCooldownsPhase(world, trace) {
|
|
5
|
+
for (const e of world.entities) {
|
|
6
|
+
e.action.attackCooldownTicks = Math.max(0, e.action.attackCooldownTicks - 1);
|
|
7
|
+
e.action.defenceCooldownTicks = Math.max(0, e.action.defenceCooldownTicks - 1);
|
|
8
|
+
e.action.grappleCooldownTicks = Math.max(0, e.action.grappleCooldownTicks - 1);
|
|
9
|
+
e.action.shootCooldownTicks = Math.max(0, e.action.shootCooldownTicks - 1);
|
|
10
|
+
e.action.swingMomentumQ = qMul(e.action.swingMomentumQ, SWING_MOMENTUM_DECAY);
|
|
11
|
+
if (e.action.capabilityCooldowns) {
|
|
12
|
+
for (const [key, ticks] of e.action.capabilityCooldowns) {
|
|
13
|
+
if (ticks <= 1)
|
|
14
|
+
e.action.capabilityCooldowns.delete(key);
|
|
15
|
+
else
|
|
16
|
+
e.action.capabilityCooldowns.set(key, ticks - 1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
e.condition.standBlockedTicks = Math.max(0, e.condition.standBlockedTicks - 1);
|
|
20
|
+
e.condition.unconsciousTicks = Math.max(0, e.condition.unconsciousTicks - 1);
|
|
21
|
+
e.condition.suppressedTicks = Math.max(0, e.condition.suppressedTicks - 1);
|
|
22
|
+
e.condition.blindTicks = Math.max(0, e.condition.blindTicks - 1);
|
|
23
|
+
if (e.action.staggerTicks)
|
|
24
|
+
e.action.staggerTicks = Math.max(0, e.action.staggerTicks - 1);
|
|
25
|
+
e.condition.rallyCooldownTicks = Math.max(0, e.condition.rallyCooldownTicks - 1);
|
|
26
|
+
if (e.action.weaponBindTicks > 0) {
|
|
27
|
+
e.action.weaponBindTicks = Math.max(0, e.action.weaponBindTicks - 1);
|
|
28
|
+
if (e.action.weaponBindTicks === 0) {
|
|
29
|
+
const partnerId = e.action.weaponBindPartnerId;
|
|
30
|
+
e.action.weaponBindPartnerId = 0;
|
|
31
|
+
if (partnerId !== 0 && e.id < partnerId) {
|
|
32
|
+
trace.onEvent({ kind: TraceKinds.WeaponBindBreak, tick: world.tick, entityId: e.id, partnerId, reason: "timeout" });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type WorldIndex } from "../../indexing.js";
|
|
2
|
+
import { type SpatialIndex } from "../../spatial.js";
|
|
3
|
+
import type { KernelContext } from "../../context.js";
|
|
4
|
+
import type { WorldState } from "../../world.js";
|
|
5
|
+
export interface PreparedKernelFrame {
|
|
6
|
+
cellSize_m: number;
|
|
7
|
+
index: WorldIndex;
|
|
8
|
+
spatial: SpatialIndex;
|
|
9
|
+
}
|
|
10
|
+
export declare function runPreparePhase(world: WorldState, ctx: KernelContext): PreparedKernelFrame;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SCALE } from "../../../units.js";
|
|
2
|
+
import { buildWorldIndex } from "../../indexing.js";
|
|
3
|
+
import { buildSpatialIndex } from "../../spatial.js";
|
|
4
|
+
import { computeDensityField } from "../../density.js";
|
|
5
|
+
import { DEFAULT_SENSORY_ENV } from "../../sensory.js";
|
|
6
|
+
import { deriveWeatherModifiers } from "../../weather.js";
|
|
7
|
+
export function runPreparePhase(world, ctx) {
|
|
8
|
+
const runtimeState = world.runtimeState ?? (world.runtimeState = {});
|
|
9
|
+
runtimeState.sensoryEnv = ctx.sensoryEnv ?? DEFAULT_SENSORY_ENV;
|
|
10
|
+
if (ctx.weather) {
|
|
11
|
+
const wMod = deriveWeatherModifiers(ctx.weather);
|
|
12
|
+
ctx.tractionCoeff = Math.trunc((ctx.tractionCoeff * wMod.tractionMul_Q) / SCALE.Q);
|
|
13
|
+
const baseEnv = runtimeState.sensoryEnv;
|
|
14
|
+
runtimeState.sensoryEnv = {
|
|
15
|
+
...baseEnv,
|
|
16
|
+
lightMul: Math.trunc((baseEnv.lightMul * wMod.lightMul_Q) / SCALE.Q),
|
|
17
|
+
smokeMul: Math.trunc((baseEnv.smokeMul * wMod.precipVisionMul_Q) / SCALE.Q),
|
|
18
|
+
};
|
|
19
|
+
if (ctx.thermalAmbient_Q !== undefined && wMod.thermalOffset_Q !== 0) {
|
|
20
|
+
ctx.thermalAmbient_Q = (ctx.thermalAmbient_Q + wMod.thermalOffset_Q);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
world.entities.sort((a, b) => a.id - b.id);
|
|
24
|
+
const index = buildWorldIndex(world);
|
|
25
|
+
const cellSize_m = ctx.cellSize_m ?? Math.trunc(4 * SCALE.m);
|
|
26
|
+
const spatial = buildSpatialIndex(world, cellSize_m);
|
|
27
|
+
ctx.density = computeDensityField(world, index, spatial, {
|
|
28
|
+
personalRadius_m: Math.trunc(0.45 * SCALE.m),
|
|
29
|
+
maxNeighbours: 12,
|
|
30
|
+
crowdingAt: 6,
|
|
31
|
+
});
|
|
32
|
+
return { cellSize_m, index, spatial };
|
|
33
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const STEP_PHASE_ORDER: readonly ["prepare", "cooldowns", "capabilityLifecycle", "intent", "movement", "hazardsAndPush", "actions", "grappleMaintenance", "impactResolution", "effects", "physiology", "morale", "finalize"];
|
|
2
|
+
export type StepPhaseName = (typeof STEP_PHASE_ORDER)[number];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const STEP_PHASE_ORDER = [
|
|
2
|
+
"prepare",
|
|
3
|
+
"cooldowns",
|
|
4
|
+
"capabilityLifecycle",
|
|
5
|
+
"intent",
|
|
6
|
+
"movement",
|
|
7
|
+
"hazardsAndPush",
|
|
8
|
+
"actions",
|
|
9
|
+
"grappleMaintenance",
|
|
10
|
+
"impactResolution",
|
|
11
|
+
"effects",
|
|
12
|
+
"physiology",
|
|
13
|
+
"morale",
|
|
14
|
+
"finalize",
|
|
15
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TraceSink } from "../../trace.js";
|
|
2
|
+
import type { WorldState } from "../../world.js";
|
|
3
|
+
import type { WorldIndex } from "../../indexing.js";
|
|
4
|
+
import type { ImpactEvent } from "../../events.js";
|
|
5
|
+
import type { Weapon } from "../../../equipment.js";
|
|
6
|
+
export interface ImpactResolverDeps {
|
|
7
|
+
applyImpactToInjury: (target: import("../../entity.js").Entity, wpn: Weapon, energy_J: number, region: string, armoured: boolean, trace: TraceSink, tick: number, tempCavityMul_Q?: number) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function applyResolvedImpacts(world: WorldState, index: WorldIndex, impacts: readonly ImpactEvent[], trace: TraceSink, deps: ImpactResolverDeps): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { computeKnockback, applyKnockback } from "../../knockback.js";
|
|
2
|
+
import { computeTemporaryCavityMul, computeCavitationBleed } from "../../hydrostatic.js";
|
|
3
|
+
import { TraceKinds } from "../../kinds.js";
|
|
4
|
+
export function applyResolvedImpacts(world, index, impacts, trace, deps) {
|
|
5
|
+
for (const ev of impacts) {
|
|
6
|
+
const target = index.byId.get(ev.targetId);
|
|
7
|
+
if (!target || target.injury.dead)
|
|
8
|
+
continue;
|
|
9
|
+
let effectiveEnergy = ev.energy_J;
|
|
10
|
+
if ((target.condition.shieldReserve_J ?? 0) > 0 &&
|
|
11
|
+
target.condition.shieldExpiry_tick !== undefined &&
|
|
12
|
+
world.tick <= target.condition.shieldExpiry_tick) {
|
|
13
|
+
const absorbed = Math.min(target.condition.shieldReserve_J, effectiveEnergy);
|
|
14
|
+
target.condition.shieldReserve_J -= absorbed;
|
|
15
|
+
effectiveEnergy -= absorbed;
|
|
16
|
+
}
|
|
17
|
+
if (effectiveEnergy > 0) {
|
|
18
|
+
const region = ev.region;
|
|
19
|
+
const tempCavMul = ev.v_impact_mps
|
|
20
|
+
? computeTemporaryCavityMul(ev.v_impact_mps, region)
|
|
21
|
+
: undefined;
|
|
22
|
+
deps.applyImpactToInjury(target, ev.wpn, effectiveEnergy, region, ev.protectedByArmour, trace, world.tick, tempCavMul);
|
|
23
|
+
if (ev.v_impact_mps) {
|
|
24
|
+
const rs = target.injury.byRegion[region];
|
|
25
|
+
if (rs) {
|
|
26
|
+
rs.bleedingRate = computeCavitationBleed(ev.v_impact_mps, rs.bleedingRate, region);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (effectiveEnergy > 0 && (ev.massEff_kg ?? 0) > 0) {
|
|
31
|
+
const attacker = index.byId.get(ev.attackerId);
|
|
32
|
+
if (attacker) {
|
|
33
|
+
const kbResult = computeKnockback(effectiveEnergy, ev.massEff_kg, target);
|
|
34
|
+
applyKnockback(target, kbResult, {
|
|
35
|
+
dx: target.position_m.x - attacker.position_m.x,
|
|
36
|
+
dy: target.position_m.y - attacker.position_m.y,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
trace.onEvent({
|
|
41
|
+
kind: TraceKinds.Attack,
|
|
42
|
+
tick: world.tick,
|
|
43
|
+
attackerId: ev.attackerId,
|
|
44
|
+
targetId: ev.targetId,
|
|
45
|
+
weaponId: ev.weaponId,
|
|
46
|
+
region: ev.region,
|
|
47
|
+
energy_J: ev.energy_J,
|
|
48
|
+
blocked: ev.blocked,
|
|
49
|
+
parried: ev.parried,
|
|
50
|
+
shieldBlocked: ev.shieldBlocked,
|
|
51
|
+
armoured: ev.protectedByArmour,
|
|
52
|
+
hitQuality: ev.hitQuality,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WorldStepContext } from "./world-step-context.js";
|
|
2
|
+
export interface WorldStepPhase {
|
|
3
|
+
name: string;
|
|
4
|
+
run(stepCtx: WorldStepContext): void;
|
|
5
|
+
}
|
|
6
|
+
export declare const WORLD_STEP_PHASE_ORDER: readonly ["prepare", "cooldowns", "input", "movement", "actions", "impacts", "systems", "finalize"];
|
|
7
|
+
export declare function runWorldStepPhases(stepCtx: WorldStepContext, phases: readonly WorldStepPhase[]): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const WORLD_STEP_PHASE_ORDER = [
|
|
2
|
+
"prepare",
|
|
3
|
+
"cooldowns",
|
|
4
|
+
"input",
|
|
5
|
+
"movement",
|
|
6
|
+
"actions",
|
|
7
|
+
"impacts",
|
|
8
|
+
"systems",
|
|
9
|
+
"finalize",
|
|
10
|
+
];
|
|
11
|
+
export function runWorldStepPhases(stepCtx, phases) {
|
|
12
|
+
for (const phase of phases) {
|
|
13
|
+
phase.run(stepCtx);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CommandMap } from "../commands.js";
|
|
2
|
+
import type { KernelContext } from "../context.js";
|
|
3
|
+
import type { ImpactEvent } from "../events.js";
|
|
4
|
+
import type { WorldIndex } from "../indexing.js";
|
|
5
|
+
import type { SpatialIndex } from "../spatial.js";
|
|
6
|
+
import type { TraceSink } from "../trace.js";
|
|
7
|
+
import type { SimulationTuning } from "../tuning.js";
|
|
8
|
+
import type { WorldState } from "../world.js";
|
|
9
|
+
export interface WorldStepContext {
|
|
10
|
+
world: WorldState;
|
|
11
|
+
cmds: CommandMap;
|
|
12
|
+
ctx: KernelContext;
|
|
13
|
+
tuning: SimulationTuning;
|
|
14
|
+
trace: TraceSink;
|
|
15
|
+
cellSize_m: number;
|
|
16
|
+
index: WorldIndex;
|
|
17
|
+
spatial: SpatialIndex;
|
|
18
|
+
spatialAfterMove: SpatialIndex;
|
|
19
|
+
impacts: ImpactEvent[];
|
|
20
|
+
finalImpacts: ImpactEvent[];
|
|
21
|
+
aliveBeforeTick: Set<number>;
|
|
22
|
+
teamRoutingFrac: Map<number, number>;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// - Cumulative exposure: heavy metals, radiation — irreversible dose accumulation
|
|
6
6
|
// - Withdrawal states after sustained addictive toxin use
|
|
7
7
|
//
|
|
8
|
-
// Follows the 1 Hz accumulator pattern; called from the kernel's
|
|
8
|
+
// Follows the 1 Hz accumulator pattern; called from the kernel's runtimeState.nutritionAccum gate.
|
|
9
9
|
//
|
|
10
10
|
// Data flow:
|
|
11
11
|
// ingestToxin(entity, id) → entity.activeIngestedToxins[]
|
package/dist/src/sim/team.js
CHANGED
|
@@ -14,7 +14,7 @@ export function areEntitiesHostile(a, b, world) {
|
|
|
14
14
|
return false;
|
|
15
15
|
const isInjured = a.injury.shock > 0 || a.injury.fluidLoss > 0;
|
|
16
16
|
// party check
|
|
17
|
-
const partyRegistry = world.
|
|
17
|
+
const partyRegistry = world.runtimeState?.partyRegistry;
|
|
18
18
|
if (partyRegistry) {
|
|
19
19
|
// if parties are friendly, not hostile (unless injured)
|
|
20
20
|
if (!isInjured && areEntitiesFriendlyByParty(partyRegistry, a, b))
|
|
@@ -24,7 +24,7 @@ export function areEntitiesHostile(a, b, world) {
|
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
// faction check
|
|
27
|
-
const factionRegistry = world.
|
|
27
|
+
const factionRegistry = world.runtimeState?.factionRegistry;
|
|
28
28
|
if (factionRegistry && a.faction && b.faction) {
|
|
29
29
|
const standing = effectiveStanding(factionRegistry, a, b);
|
|
30
30
|
if (!isInjured && standing >= STANDING_FRIENDLY_THRESHOLD)
|
package/dist/src/sim/testing.js
CHANGED
|
@@ -6,6 +6,7 @@ import { defaultCondition } from "./condition.js";
|
|
|
6
6
|
import { defaultInjury } from "./injury.js";
|
|
7
7
|
import { v3 } from "./vec3.js";
|
|
8
8
|
import { q, SCALE } from "../units.js";
|
|
9
|
+
import { normalizeWorldInPlace } from "./normalization.js";
|
|
9
10
|
/**
|
|
10
11
|
* Minimal humanoid entity for tests (deterministic attributes via generateIndividual()).
|
|
11
12
|
* Positions are fixed-point metres (SCALE.m).
|
|
@@ -39,13 +40,13 @@ export function mkWorld(seed, arg) {
|
|
|
39
40
|
if (dupes.length > 0) {
|
|
40
41
|
throw new Error(`mkWorld: duplicate entity IDs detected: ${[...new Set(dupes)].join(", ")}`);
|
|
41
42
|
}
|
|
42
|
-
return { tick: 0, seed, entities };
|
|
43
|
+
return normalizeWorldInPlace({ tick: 0, seed, entities });
|
|
43
44
|
}
|
|
44
45
|
// Back-compat duel form: mkWorld(seed, loadoutA)
|
|
45
46
|
const loadoutA = (('items' in arg) ? arg : { items: [] });
|
|
46
47
|
const aAttrs = generateIndividual(1, HUMAN_BASE);
|
|
47
48
|
const bAttrs = generateIndividual(2, HUMAN_BASE);
|
|
48
|
-
return {
|
|
49
|
+
return normalizeWorldInPlace({
|
|
49
50
|
tick: 0,
|
|
50
51
|
seed,
|
|
51
52
|
entities: [
|
|
@@ -80,7 +81,7 @@ export function mkWorld(seed, arg) {
|
|
|
80
81
|
grapple: { holdingTargetId: 0, heldByIds: [], gripQ: q(0), position: "standing" },
|
|
81
82
|
},
|
|
82
83
|
],
|
|
83
|
-
};
|
|
84
|
+
});
|
|
84
85
|
}
|
|
85
86
|
export function mkImpactEvent(attackerId, targetId, region = "", energy_J = 0, protectedByArmour = false, blocked = false, parried = false, weaponId = "", wpn = {}, hitQuality = q(0), shieldBlocked = false) {
|
|
86
87
|
return {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* accumulation, duration, and antidote clearance.
|
|
6
6
|
*
|
|
7
7
|
* Follows the 1 Hz accumulator pattern from Phase 30 (nutrition). Called from the
|
|
8
|
-
* kernel's
|
|
8
|
+
* kernel's runtimeState.nutritionAccum gate and from downtime.ts per-second loop.
|
|
9
9
|
*/
|
|
10
10
|
import { type Q } from "../units.js";
|
|
11
11
|
import type { Entity } from "./entity.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* accumulation, duration, and antidote clearance.
|
|
6
6
|
*
|
|
7
7
|
* Follows the 1 Hz accumulator pattern from Phase 30 (nutrition). Called from the
|
|
8
|
-
* kernel's
|
|
8
|
+
* kernel's runtimeState.nutritionAccum gate and from downtime.ts per-second loop.
|
|
9
9
|
*/
|
|
10
10
|
import { q, SCALE, clampQ } from "../units.js";
|
|
11
11
|
// ── Venom catalogue ───────────────────────────────────────────────────────────
|
package/dist/src/sim/world.d.ts
CHANGED
|
@@ -4,6 +4,20 @@ import { SensoryEnvironment } from "./sensory.js";
|
|
|
4
4
|
import { FactionRegistry } from "../faction.js";
|
|
5
5
|
import { PartyRegistry } from "../party.js";
|
|
6
6
|
import { RelationshipGraph } from "../relationships.js";
|
|
7
|
+
export interface WorldRuntimeState {
|
|
8
|
+
sensoryEnv?: SensoryEnvironment;
|
|
9
|
+
factionRegistry?: FactionRegistry;
|
|
10
|
+
partyRegistry?: PartyRegistry;
|
|
11
|
+
relationshipGraph?: RelationshipGraph;
|
|
12
|
+
nutritionAccum?: number;
|
|
13
|
+
contentRegistry?: {
|
|
14
|
+
packs: Map<string, unknown>;
|
|
15
|
+
archetypes: Map<string, unknown>;
|
|
16
|
+
weapons: Map<string, unknown>;
|
|
17
|
+
armour: Map<string, unknown>;
|
|
18
|
+
terrain: Map<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
7
21
|
/**
|
|
8
22
|
* Top-level simulation container.
|
|
9
23
|
*
|
|
@@ -24,29 +38,8 @@ export interface WorldState {
|
|
|
24
38
|
*/
|
|
25
39
|
activeFieldEffects?: FieldEffect[];
|
|
26
40
|
/**
|
|
27
|
-
* @subsystem(
|
|
28
|
-
*
|
|
29
|
-
* direct access — use the sensory API instead.
|
|
30
|
-
*/
|
|
31
|
-
__sensoryEnv?: SensoryEnvironment;
|
|
32
|
-
/**
|
|
33
|
-
* @subsystem(faction) Global faction-standing registry.
|
|
34
|
-
* Consumed by `src/faction.ts` and AI targeting.
|
|
35
|
-
*/
|
|
36
|
-
__factionRegistry?: FactionRegistry;
|
|
37
|
-
/**
|
|
38
|
-
* @subsystem(party) Global party registry.
|
|
39
|
-
* Consumed by `src/party.ts` for morale and formation bonuses.
|
|
40
|
-
*/
|
|
41
|
-
__partyRegistry?: PartyRegistry;
|
|
42
|
-
/**
|
|
43
|
-
* @subsystem(relationships) Inter-entity relationship graph.
|
|
44
|
-
* Consumed by `src/relationships.ts`.
|
|
45
|
-
*/
|
|
46
|
-
__relationshipGraph?: RelationshipGraph;
|
|
47
|
-
/**
|
|
48
|
-
* @subsystem(nutrition) Cross-tick nutrition accumulator.
|
|
49
|
-
* Consumed by the nutrition sub-step in `src/sim/kernel.ts`.
|
|
41
|
+
* @subsystem(runtime) Explicit host/runtime state consumed by optional subsystems.
|
|
42
|
+
* Kept separate from core deterministic world data.
|
|
50
43
|
*/
|
|
51
|
-
|
|
44
|
+
runtimeState?: WorldRuntimeState;
|
|
52
45
|
}
|