@its-not-rocket-science/ananke 0.1.44 → 0.1.47
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 +71 -5
- package/dist/src/bridge/bridge-engine.js +0 -1
- package/dist/src/competence/acoustic.d.ts +1 -1
- package/dist/src/competence/acoustic.js +1 -7
- package/dist/src/competence/catalogue.js +1 -1
- package/dist/src/competence/engineering.js +0 -2
- package/dist/src/competence/framework.js +2 -4
- package/dist/src/competence/interspecies.js +0 -2
- package/dist/src/competence/language.js +0 -2
- package/dist/src/competence/naturalist.js +1 -1
- package/dist/src/crafting/index.d.ts +2 -2
- package/dist/src/crafting/index.js +6 -7
- package/dist/src/crafting/manufacturing.d.ts +11 -15
- package/dist/src/crafting/manufacturing.js +7 -11
- package/dist/src/crafting/materials.d.ts +1 -1
- package/dist/src/crafting/materials.js +12 -4
- package/dist/src/crafting/recipes.d.ts +1 -1
- package/dist/src/crafting/recipes.js +1 -1
- package/dist/src/item-durability.d.ts +1 -1
- package/dist/src/item-durability.js +1 -1
- package/dist/src/legend.d.ts +1 -1
- package/dist/src/legend.js +1 -1
- package/dist/src/modding.d.ts +11 -11
- package/dist/src/modding.js +10 -10
- package/dist/src/monetary.d.ts +104 -0
- package/dist/src/monetary.js +158 -0
- package/dist/src/narrative-render.js +3 -3
- package/dist/src/quest-generators.js +4 -4
- package/dist/src/settlement-services.js +2 -2
- package/dist/src/settlement.d.ts +1 -1
- package/dist/src/settlement.js +2 -2
- package/dist/src/sim/ai/behavior-trees.d.ts +2 -2
- package/dist/src/sim/ai/behavior-trees.js +2 -2
- package/dist/src/sim/bodyplan.d.ts +1 -1
- package/dist/src/sim/kernel.js +1 -1
- package/dist/src/sim/step/morale.js +1 -1
- package/dist/src/wonders.d.ts +125 -0
- package/dist/src/wonders.js +264 -0
- package/package.json +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,72 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.1.47] — 2026-03-27
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Lint clean-up (zero issues)** — eliminated all 574 ESLint errors and warnings across `src/` and `test/`:
|
|
14
|
+
- Replaced all `as any` casts with proper types (`as Q`, `as TechEra`, `as WonderType`, `as unknown as TypeName`, etc.)
|
|
15
|
+
- Removed unused imports and prefixed unused locals with `_` across 50+ test files
|
|
16
|
+
- Fixed `getAvailableMaterials` TODO in `src/crafting/materials.ts` — now accepts `readonly Material[]` and derives per-type totals
|
|
17
|
+
- Removed `@ts-nocheck` from `as/injury.ts`; applied `const` fixes and removed dead imports throughout `src/`
|
|
18
|
+
- **UK English** — updated all comments, JSDoc, and documentation prose to British spelling (`armour`, `defence`, `behaviour`, `analyse`, `calibre`, `colour`); exported API identifiers unchanged
|
|
19
|
+
- Build: clean. Tests: 5,261 passing. Coverage: statements 97.12 %, branches 87.87 %, functions 95.65 %, lines 97.12 %.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [0.1.46] — 2026-03-27
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Phase 101 · Currency & Monetary Policy** (`src/monetary.ts`)
|
|
28
|
+
- `CoinagePolicy`: `"stable" | "slight_debasement" | "heavy_debasement" | "emergency_printing"`.
|
|
29
|
+
- `MonetaryState { polityId, coinPurity_Q, inflationLevel_Q, monetaryCrisis }` — per-polity mutable tracker stored externally.
|
|
30
|
+
- `coinPurity_Q` [0, SCALE.Q]: intrinsic metal content; trade partners check this. Starts at SCALE.Q.
|
|
31
|
+
- `inflationLevel_Q` [0, SCALE.Q]: accumulated price inflation; drives purchasing power loss and unrest. Starts at 0.
|
|
32
|
+
- `monetaryCrisis`: activates when `inflationLevel_Q >= MONETARY_CRISIS_THRESHOLD_Q = q(0.60)`.
|
|
33
|
+
- `POLICY_PURITY_DELTA_PER_DAY`: stable +3 (recovery) → emergency_printing −40/day.
|
|
34
|
+
- `POLICY_INFLATION_DELTA_PER_DAY`: stable −3 (deflation) → emergency_printing +50/day.
|
|
35
|
+
- `POLICY_DAILY_MINT_FRAC_Q`: stable 0 → emergency_printing 30/SCALE.Q (+110%/year).
|
|
36
|
+
- `computePurchasingPower_Q(state)` → `coinPurity × (1 − inflation) / SCALE.Q`; floor q(0.05).
|
|
37
|
+
- `computeMonetaryTradeMultiplier_Q(state)` → `[MONETARY_TRADE_FLOOR_Q, SCALE.Q]`; based on purity; feeds Phase-92.
|
|
38
|
+
- `computeMonetaryUnrest_Q(state)` → `[0, MONETARY_MAX_UNREST_Q=q(0.25)]`; linear on inflation; feeds Phase-90.
|
|
39
|
+
- `computeDebasementGain_cu(polity, policy, elapsedDays)` → advisory preview of mint gain.
|
|
40
|
+
- `stepMonetary(polity, state, policy, elapsedDays)` — mints extra treasury, updates purity/inflation, sets crisis flag.
|
|
41
|
+
- `isMonetaryCrisis(state)` / `isCoinageSound(state, threshold_Q?)` — predicates.
|
|
42
|
+
- Added `./monetary` subpath export to `package.json`.
|
|
43
|
+
- 45 new tests; 5,261 total. Coverage: 100% statements/branches/functions/lines on `monetary.ts`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## [0.1.45] — 2026-03-27
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **Phase 100 · Wonders & Monuments** (`src/wonders.ts`)
|
|
52
|
+
- `WonderType`: `"great_pyramid" | "colosseum" | "grand_library" | "great_wall" | "grand_harbour" | "aqueduct_system" | "grand_temple"`.
|
|
53
|
+
- `WonderProject { projectId, polityId, type, progress_Q, investedCost_cu, startTick }` — in-progress construction.
|
|
54
|
+
- `Wonder { wonderId, polityId, type, completedAtTick, damaged }` — completed monument.
|
|
55
|
+
- `WonderEffects { stabilityBonus_Q, moraleBonus_Q, researchPointBonus, unrestReduction_Q, tradeIncomeBonus_Q, defenseBonus_Q, epidemicResistance_Q }` — advisory bundle.
|
|
56
|
+
- `WONDER_BASE_COST_CU`: grand_library 150k → great_pyramid 1,000k cu.
|
|
57
|
+
- `WONDER_TYPICAL_DAYS`: grand_library 180 → great_pyramid 3,650 days (10 years).
|
|
58
|
+
- `WONDER_BASE_EFFECTS`: distinct niches — great_wall highest defence (q(0.20)), grand_harbour highest trade (q(0.25)), aqueduct_system highest epidemic resistance (q(0.15)), colosseum highest unrest reduction (q(0.12)), grand_library +3 RP/day, great_pyramid highest stability (q(0.08)).
|
|
59
|
+
- `WONDER_DAMAGED_EFFECT_MUL = q(0.50)` — damaged wonders provide half effects.
|
|
60
|
+
- `WONDER_REPAIR_COST_FRAC = q(0.25)` — repair costs 25% of base construction cost.
|
|
61
|
+
- `createWonderProject(projectId, polityId, type, startTick)` — factory.
|
|
62
|
+
- `contributeToWonder(project, polity, contribution_cu)` — deducts treasury, advances progress_Q; capped by treasury and remaining cost; returns new progress.
|
|
63
|
+
- `isWonderProjectComplete(project)` → `progress_Q >= SCALE.Q`.
|
|
64
|
+
- `completeWonder(project, tick)` → `Wonder`.
|
|
65
|
+
- `damageWonder(wonder)` — set by Phase-96 earthquake or Phase-93 siege callers.
|
|
66
|
+
- `repairWonder(wonder, polity)` → `boolean` — spends repair cost; returns false if funds insufficient.
|
|
67
|
+
- `computeWonderEffects(wonder)` — full or half effects based on damage state.
|
|
68
|
+
- `aggregateWonderEffects(wonders)` — sums Q fields (clamped to SCALE.Q); sums researchPointBonus uncapped.
|
|
69
|
+
- `isWonderIntact(wonder)` / `computeRepairCost(type)` — helpers.
|
|
70
|
+
- Added `./wonders` subpath export to `package.json`.
|
|
71
|
+
- 43 new tests; 5,216 total. Coverage: 100% statements/branches/functions/lines on `wonders.ts`.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
9
75
|
## [0.1.44] — 2026-03-27
|
|
10
76
|
|
|
11
77
|
### Added
|
|
@@ -729,15 +795,15 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
729
795
|
- **CE-16 · Modding Support** (`src/modding.ts`)
|
|
730
796
|
- Layer 1 — `hashMod(json)`: deterministic FNV-1a fingerprint (8-char hex) for any
|
|
731
797
|
parsed JSON mod file; canonical key-sorted serialisation ensures order-independence.
|
|
732
|
-
- Layer 2 — Post-tick
|
|
798
|
+
- Layer 2 — Post-tick behaviour hooks: `registerPostTickHook / unregisterPostTickHook /
|
|
733
799
|
runPostTickHooks / listPostTickHooks / clearPostTickHooks`; hooks fire after
|
|
734
800
|
`stepWorld`, are purely observational (logging, analytics, renderer updates).
|
|
735
|
-
- Layer 3 — AI
|
|
801
|
+
- Layer 3 — AI behaviour node registry: `registerBehaviorNode / unregisterBehaviorNode /
|
|
736
802
|
getBehaviorNode / listBehaviorNodes / clearBehaviorNodes`; custom `BehaviorNode`
|
|
737
|
-
factories registered by id for scenario and
|
|
803
|
+
factories registered by id for scenario and behaviour-tree composition.
|
|
738
804
|
- Session fingerprint: `computeModManifest(catalogIds)` returns sorted id lists and a
|
|
739
805
|
single fingerprint covering all three layers for multiplayer client validation.
|
|
740
|
-
- `clearAllMods()` resets hooks and
|
|
806
|
+
- `clearAllMods()` resets hooks and behaviour nodes (catalog unchanged).
|
|
741
807
|
- 42 tests in `test/modding.test.ts`; exported via `src/index.ts`.
|
|
742
808
|
|
|
743
809
|
- **CE-14 · Socio-Economic Campaign Layer → Stable Promotion**
|
|
@@ -832,7 +898,7 @@ Adding new **optional** fields to these interfaces is never a breaking change.
|
|
|
832
898
|
|
|
833
899
|
- **CE-16 · Modding Support — HashMod, Post-tick Hooks, Behaviour Node Registry** (`src/parallel.ts`)
|
|
834
900
|
- Three-layer modding contract: FNV-1a data fingerprinting, observational
|
|
835
|
-
post-tick hooks, and named AI
|
|
901
|
+
post-tick hooks, and named AI behaviour node factories. computeModManifest()
|
|
836
902
|
provides a single session fingerprint for multiplayer client validation.
|
|
837
903
|
- exported via src/index.ts.
|
|
838
904
|
|
|
@@ -151,7 +151,6 @@ export class BridgeEngine {
|
|
|
151
151
|
// Render time at or after current snapshot
|
|
152
152
|
if (this.config.extrapolationAllowed) {
|
|
153
153
|
// Extrapolate forward using velocity
|
|
154
|
-
const delta = renderTime_s - this.currTime_s;
|
|
155
154
|
t = SCALE.Q;
|
|
156
155
|
fromTick = curr.tick;
|
|
157
156
|
toTick = curr.tick;
|
|
@@ -42,7 +42,7 @@ export interface AcousticDetectionOutcome {
|
|
|
42
42
|
* movementNoise = velocity_mps × 10
|
|
43
43
|
* equipmentNoise = based on armour material and weapon mass
|
|
44
44
|
*
|
|
45
|
-
* @param entity - The entity to
|
|
45
|
+
* @param entity - The entity to analyse.
|
|
46
46
|
* @returns Acoustic signature with all noise components.
|
|
47
47
|
*/
|
|
48
48
|
export declare function deriveAcousticSignature(entity: Entity): AcousticSignature;
|
|
@@ -10,8 +10,6 @@ import { SCALE, q, clampQ, mulDiv } from "../units.js";
|
|
|
10
10
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
11
11
|
/** Scale factor for acoustic detection formula. */
|
|
12
12
|
const SCALE_ACOUSTIC = 100; // multiplier to get useful detection ranges
|
|
13
|
-
/** Base detection range in metres at average musical intelligence. */
|
|
14
|
-
const BASE_DETECTION_RANGE_m = 50;
|
|
15
13
|
/** Maximum effective detection range. */
|
|
16
14
|
const MAX_DETECTION_RANGE_m = 500;
|
|
17
15
|
/** Range factor: clarity degrades with distance. */
|
|
@@ -23,11 +21,7 @@ const BASE_SIGNAL_LATENCY_MS = 200;
|
|
|
23
21
|
/** Latency reduction from high musical intelligence. */
|
|
24
22
|
const MUSICAL_LATENCY_REDUCTION_FACTOR = 0.5; // up to 50% faster
|
|
25
23
|
// Noise level constants
|
|
26
|
-
const NOISE_SILENT = 0;
|
|
27
|
-
const NOISE_VERY_QUIET = 10;
|
|
28
|
-
const NOISE_QUIET = 25;
|
|
29
24
|
const NOISE_NORMAL = 50;
|
|
30
|
-
const NOISE_LOUD = 75;
|
|
31
25
|
const NOISE_VERY_LOUD = 100;
|
|
32
26
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
33
27
|
/**
|
|
@@ -85,7 +79,7 @@ function calculateEquipmentNoise(entity) {
|
|
|
85
79
|
* movementNoise = velocity_mps × 10
|
|
86
80
|
* equipmentNoise = based on armour material and weapon mass
|
|
87
81
|
*
|
|
88
|
-
* @param entity - The entity to
|
|
82
|
+
* @param entity - The entity to analyse.
|
|
89
83
|
* @returns Acoustic signature with all noise components.
|
|
90
84
|
*/
|
|
91
85
|
export function deriveAcousticSignature(entity) {
|
|
@@ -181,7 +181,7 @@ const entries = [
|
|
|
181
181
|
domain: "logicalMathematical",
|
|
182
182
|
difficulty_Q: q(0.55),
|
|
183
183
|
timeBase_s: 1800, // 30 minutes
|
|
184
|
-
description: "
|
|
184
|
+
description: "Analyse tactical situation and propose solution",
|
|
185
185
|
},
|
|
186
186
|
// ── Musical (performance, signaling) ─────────────────────────────────────────
|
|
187
187
|
{
|
|
@@ -13,8 +13,6 @@ const BASE_QUALITY_MUL = q(0.70);
|
|
|
13
13
|
const MAX_QUALITY_MUL = q(1.20);
|
|
14
14
|
/** Minimum quality multiplier for success. */
|
|
15
15
|
const MIN_SUCCESS_QUALITY = q(0.30);
|
|
16
|
-
/** Time factor: rushed work reduces quality. */
|
|
17
|
-
const TIME_FACTOR_BASE = 1.0;
|
|
18
16
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
19
17
|
/**
|
|
20
18
|
* Resolve an engineering project.
|
|
@@ -31,8 +31,6 @@ const QUALITY_XP_MULTIPLIERS = {
|
|
|
31
31
|
};
|
|
32
32
|
/** Difficulty XP bonus: harder tasks grant more XP. */
|
|
33
33
|
const MAX_DIFFICULTY_BONUS = 15;
|
|
34
|
-
/** Tool quality bonus lookup (default if tool not specified). */
|
|
35
|
-
const DEFAULT_TOOL_BONUS = q(1.0);
|
|
36
34
|
// ── Helper Functions ──────────────────────────────────────────────────────────
|
|
37
35
|
/**
|
|
38
36
|
* Map domain-specific descriptors to canonical descriptors.
|
|
@@ -279,7 +277,7 @@ function resolveNaturalist(actor, task, action) {
|
|
|
279
277
|
/**
|
|
280
278
|
* Resolve inter-species tasks.
|
|
281
279
|
*/
|
|
282
|
-
function resolveInterSpecies(actor, task, action
|
|
280
|
+
function resolveInterSpecies(actor, task, action) {
|
|
283
281
|
const seed = action.seed;
|
|
284
282
|
const spec = {
|
|
285
283
|
targetSpecies: "unknown",
|
|
@@ -647,7 +645,7 @@ export function resolveCompetence(actor, action, world) {
|
|
|
647
645
|
result = resolveNaturalist(actor, task, action);
|
|
648
646
|
break;
|
|
649
647
|
case "interSpecies":
|
|
650
|
-
result = resolveInterSpecies(actor, task, action
|
|
648
|
+
result = resolveInterSpecies(actor, task, action);
|
|
651
649
|
break;
|
|
652
650
|
case "linguistic":
|
|
653
651
|
result = resolveLinguistic(actor, task, action, world);
|
|
@@ -11,8 +11,6 @@ import { makeRng } from "../rng.js";
|
|
|
11
11
|
const MAX_LATENCY_PENALTY_MS = 80 * SCALE.s / 1000; // 80ms in Q-units
|
|
12
12
|
/** Scaling factor: penalty = (1.0 − interSpecies) × MAX_LATENCY_PENALTY_MS. */
|
|
13
13
|
const LATENCY_PENALTY_SCALE = MAX_LATENCY_PENALTY_MS;
|
|
14
|
-
/** Base success probability for signaling without vocabulary. */
|
|
15
|
-
const SIGNAL_BASE_PROBABILITY = q(0.10);
|
|
16
14
|
/** Aggravation threshold: low empathy + high fear → possible hostile reaction. */
|
|
17
15
|
const AGGRAVATION_THRESHOLD = q(0.30);
|
|
18
16
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
// No kernel import — pure resolution module.
|
|
7
7
|
import { SCALE, q, clampQ, qMul, mulDiv } from "../units.js";
|
|
8
8
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
9
|
-
/** Base reception rate at linguistic q(0.50) for formation size 1. */
|
|
10
|
-
const BASE_RECEPTION_RATE = q(0.70);
|
|
11
9
|
/** Maximum formation size before reception penalties apply. */
|
|
12
10
|
const OPTIMAL_FORMATION_SIZE = 10;
|
|
13
11
|
/** Base delay divisor: linguistic × BASE_DELAY_DIVISOR. */
|
|
@@ -162,7 +162,7 @@ export function resolveTaming(entity, spec, seed) {
|
|
|
162
162
|
const fearPenalty = mulDiv(spec.animalFearQ, q(0.50), SCALE.Q);
|
|
163
163
|
// Prior successes bonus: +5% per success, max +25%
|
|
164
164
|
const experienceBonus = Math.min(spec.priorSuccesses * q(0.05), q(0.25));
|
|
165
|
-
|
|
165
|
+
const trust_Q = clampQ((baseTrust - fearPenalty + experienceBonus), q(0), q(1.0));
|
|
166
166
|
// Attack check: high fear with low trust is dangerous
|
|
167
167
|
// P(attack) = max(0, (animalFearQ − trust_Q) × 0.30)
|
|
168
168
|
const fearFloat = mulDiv(spec.animalFearQ, q(1.0), SCALE.Q);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Q } from "../units.js";
|
|
2
2
|
import type { Entity } from "../sim/entity.js";
|
|
3
3
|
import type { Inventory } from "../inventory.js";
|
|
4
|
+
import type { ItemBase } from "../equipment.js";
|
|
4
5
|
import type { Recipe } from "./recipes.js";
|
|
5
6
|
import type { Material } from "./materials.js";
|
|
6
7
|
import type { WorkshopInstance } from "./workshops.js";
|
|
@@ -41,8 +42,7 @@ export declare function getAvailableRecipes(entity: Entity, inventory: Inventory
|
|
|
41
42
|
* Get material properties for a crafted item.
|
|
42
43
|
* Applies material property modifiers to base item stats.
|
|
43
44
|
*/
|
|
44
|
-
export declare function applyMaterialProperties(baseItem:
|
|
45
|
-
material: Material): MaterialPropertyModifier;
|
|
45
|
+
export declare function applyMaterialProperties(baseItem: ItemBase, material: Material): MaterialPropertyModifier;
|
|
46
46
|
/**
|
|
47
47
|
* Integrate crafting result into inventory.
|
|
48
48
|
* Consumes ingredients and adds crafted item.
|
|
@@ -107,8 +107,8 @@ export function startManufacturing(recipeId, quantity, workshop, workers, worldS
|
|
|
107
107
|
if (!skilledWorkerExists) {
|
|
108
108
|
return { success: false, error: "No worker meets skill requirements" };
|
|
109
109
|
}
|
|
110
|
-
const
|
|
111
|
-
const
|
|
110
|
+
const _primaryWorkerId = workers.length > 0 ? workers[0].id : 0;
|
|
111
|
+
const _lineSeed = eventSeed(worldSeed, tick, _primaryWorkerId, hashString(recipe.id), salt);
|
|
112
112
|
const orderId = `order_${worldSeed}_${tick}_${recipe.id}_${salt}`;
|
|
113
113
|
// Create manufacturing order
|
|
114
114
|
const order = {
|
|
@@ -118,7 +118,7 @@ export function startManufacturing(recipeId, quantity, workshop, workers, worldS
|
|
|
118
118
|
workshop,
|
|
119
119
|
};
|
|
120
120
|
// Setup production line
|
|
121
|
-
const line = setupProductionLine(order, workers
|
|
121
|
+
const line = setupProductionLine(order, workers);
|
|
122
122
|
// TODO: store production line in persistent state
|
|
123
123
|
return { success: true, lineId: line.lineId };
|
|
124
124
|
}
|
|
@@ -131,7 +131,7 @@ export function advanceManufacturing(lineId, deltaTime_s, workers, workshop, wor
|
|
|
131
131
|
let lineIdHash = 0;
|
|
132
132
|
for (let i = 0; i < lineId.length; i++)
|
|
133
133
|
lineIdHash += lineId.charCodeAt(i);
|
|
134
|
-
const
|
|
134
|
+
const _seed = eventSeed(worldSeed, tick, lineIdHash, 0, salt);
|
|
135
135
|
// TODO: retrieve production line by lineId
|
|
136
136
|
const line = {
|
|
137
137
|
lineId,
|
|
@@ -143,7 +143,7 @@ export function advanceManufacturing(lineId, deltaTime_s, workers, workshop, wor
|
|
|
143
143
|
priority: 1,
|
|
144
144
|
qualityRange: { min_Q: q(0.30), max_Q: q(0.90), avg_Q: q(0.60) },
|
|
145
145
|
};
|
|
146
|
-
const result = advanceProduction(line, deltaTime_s, workers
|
|
146
|
+
const result = advanceProduction(line, deltaTime_s, workers);
|
|
147
147
|
// TODO: update stored line
|
|
148
148
|
return {
|
|
149
149
|
itemsCompleted: result.itemsCompleted,
|
|
@@ -171,8 +171,7 @@ export function getAvailableRecipes(entity, inventory, workshop) {
|
|
|
171
171
|
* Get material properties for a crafted item.
|
|
172
172
|
* Applies material property modifiers to base item stats.
|
|
173
173
|
*/
|
|
174
|
-
export function applyMaterialProperties(baseItem,
|
|
175
|
-
material) {
|
|
174
|
+
export function applyMaterialProperties(baseItem, material) {
|
|
176
175
|
return calculateMaterialEffect(baseItem, material);
|
|
177
176
|
}
|
|
178
177
|
// ── Integration with Existing Systems ─────────────────────────────────────────
|
|
@@ -3,6 +3,11 @@ import type { Entity } from "../sim/entity.js";
|
|
|
3
3
|
import type { Recipe } from "./recipes.js";
|
|
4
4
|
import type { WorkshopInstance } from "./workshops.js";
|
|
5
5
|
/** Production line state for batch manufacturing. */
|
|
6
|
+
export type ProductQualityRange = {
|
|
7
|
+
min_Q: Q;
|
|
8
|
+
max_Q: Q;
|
|
9
|
+
avg_Q: Q;
|
|
10
|
+
};
|
|
6
11
|
export interface ProductionLine {
|
|
7
12
|
lineId: string;
|
|
8
13
|
recipeId: string;
|
|
@@ -11,11 +16,7 @@ export interface ProductionLine {
|
|
|
11
16
|
progress_Q: Q;
|
|
12
17
|
assignedWorkers: number[];
|
|
13
18
|
priority: number;
|
|
14
|
-
qualityRange:
|
|
15
|
-
min_Q: Q;
|
|
16
|
-
max_Q: Q;
|
|
17
|
-
avg_Q: Q;
|
|
18
|
-
};
|
|
19
|
+
qualityRange: ProductQualityRange;
|
|
19
20
|
}
|
|
20
21
|
/** Manufacturing order for starting batch production. */
|
|
21
22
|
export interface ManufacturingOrder {
|
|
@@ -30,11 +31,7 @@ export interface ProductionAdvanceResult {
|
|
|
30
31
|
itemsCompleted: number;
|
|
31
32
|
totalItemsProduced: number;
|
|
32
33
|
progress_Q: Q;
|
|
33
|
-
qualityRange:
|
|
34
|
-
min_Q: Q;
|
|
35
|
-
max_Q: Q;
|
|
36
|
-
avg_Q: Q;
|
|
37
|
-
};
|
|
34
|
+
qualityRange: ProductQualityRange;
|
|
38
35
|
}
|
|
39
36
|
/** Assembly step for multi-stage crafting. */
|
|
40
37
|
export interface AssemblyStep {
|
|
@@ -47,19 +44,18 @@ export interface AssemblyStep {
|
|
|
47
44
|
/**
|
|
48
45
|
* Initialize a production line for batch manufacturing.
|
|
49
46
|
*/
|
|
50
|
-
export declare function setupProductionLine(order: ManufacturingOrder, workers: Entity[]
|
|
47
|
+
export declare function setupProductionLine(order: ManufacturingOrder, workers: Entity[]): ProductionLine;
|
|
51
48
|
/**
|
|
52
49
|
* Advance production line by deltaTime seconds.
|
|
53
50
|
* Progress accumulates based on number of workers and their skills.
|
|
54
51
|
* Returns new items completed and updated progress.
|
|
55
52
|
*/
|
|
56
|
-
export declare function advanceProduction(productionLine: ProductionLine, deltaTime_s: number, workers: Entity[]
|
|
57
|
-
workshop: WorkshopInstance, seed: number): ProductionAdvanceResult;
|
|
53
|
+
export declare function advanceProduction(productionLine: ProductionLine, deltaTime_s: number, workers: Entity[]): ProductionAdvanceResult;
|
|
58
54
|
/**
|
|
59
55
|
* Calculate predicted quality range for a batch based on workers, materials, workshop.
|
|
60
56
|
* Returns min, max, and average expected quality.
|
|
61
57
|
*/
|
|
62
|
-
export declare function calculateBatchQualityRange(workers: Entity[]
|
|
58
|
+
export declare function calculateBatchQualityRange(workers: Entity[]): {
|
|
63
59
|
min_Q: Q;
|
|
64
60
|
max_Q: Q;
|
|
65
61
|
avg_Q: Q;
|
|
@@ -76,7 +72,7 @@ export declare function advanceAssemblyStep(step: AssemblyStep, worker: Entity,
|
|
|
76
72
|
completed: boolean;
|
|
77
73
|
};
|
|
78
74
|
/** Estimate time to complete a batch given workers and workshop. */
|
|
79
|
-
export declare function estimateBatchCompletionTime(batchSize: number, workers: Entity[]
|
|
75
|
+
export declare function estimateBatchCompletionTime(batchSize: number, workers: Entity[]): number;
|
|
80
76
|
/** Check if production line is complete. */
|
|
81
77
|
export declare function isProductionLineComplete(line: ProductionLine): boolean;
|
|
82
78
|
/** Get progress percentage (0–1). */
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
// Deterministic batch quality range based on workers, materials, workshop.
|
|
5
5
|
import { SCALE, q, clampQ, qMul, mulDiv } from "../units.js";
|
|
6
6
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
7
|
-
/** Default progress per worker per second (Q units). */
|
|
8
|
-
const PROGRESS_PER_WORKER_PER_SECOND = q(0.001);
|
|
9
7
|
/** Quality variance factor based on number of workers (more workers → less variance). */
|
|
10
8
|
const WORKER_VARIANCE_REDUCTION = q(0.10);
|
|
11
9
|
/** Minimum quality variance per batch. */
|
|
@@ -14,9 +12,9 @@ const MIN_QUALITY_VARIANCE = q(0.05);
|
|
|
14
12
|
/**
|
|
15
13
|
* Initialize a production line for batch manufacturing.
|
|
16
14
|
*/
|
|
17
|
-
export function setupProductionLine(order, workers
|
|
15
|
+
export function setupProductionLine(order, workers) {
|
|
18
16
|
const workerIds = workers.map(w => w.id);
|
|
19
|
-
const qualityRange = calculateBatchQualityRange(workers
|
|
17
|
+
const qualityRange = calculateBatchQualityRange(workers);
|
|
20
18
|
return {
|
|
21
19
|
lineId: `line_${order.orderId}`,
|
|
22
20
|
recipeId: order.recipeId,
|
|
@@ -33,8 +31,7 @@ export function setupProductionLine(order, workers, seed) {
|
|
|
33
31
|
* Progress accumulates based on number of workers and their skills.
|
|
34
32
|
* Returns new items completed and updated progress.
|
|
35
33
|
*/
|
|
36
|
-
export function advanceProduction(productionLine, deltaTime_s, workers
|
|
37
|
-
workshop, seed) {
|
|
34
|
+
export function advanceProduction(productionLine, deltaTime_s, workers) {
|
|
38
35
|
if (productionLine.itemsProduced >= productionLine.batchSize) {
|
|
39
36
|
return {
|
|
40
37
|
itemsCompleted: 0,
|
|
@@ -64,8 +61,7 @@ workshop, seed) {
|
|
|
64
61
|
}
|
|
65
62
|
productionLine.progress_Q = clampQ(newProgress, q(0), SCALE.Q);
|
|
66
63
|
// Update quality range based on remaining workers (variance reduces as more items produced)
|
|
67
|
-
const
|
|
68
|
-
const updatedRange = updateQualityRange(productionLine.qualityRange, workers, remainingItems, seed);
|
|
64
|
+
const updatedRange = updateQualityRange(productionLine.qualityRange);
|
|
69
65
|
return {
|
|
70
66
|
itemsCompleted,
|
|
71
67
|
totalItemsProduced: productionLine.itemsProduced,
|
|
@@ -77,7 +73,7 @@ workshop, seed) {
|
|
|
77
73
|
* Calculate predicted quality range for a batch based on workers, materials, workshop.
|
|
78
74
|
* Returns min, max, and average expected quality.
|
|
79
75
|
*/
|
|
80
|
-
export function calculateBatchQualityRange(workers
|
|
76
|
+
export function calculateBatchQualityRange(workers) {
|
|
81
77
|
if (workers.length === 0) {
|
|
82
78
|
return { min_Q: q(0), max_Q: q(0), avg_Q: q(0) };
|
|
83
79
|
}
|
|
@@ -98,7 +94,7 @@ export function calculateBatchQualityRange(workers, workshop, seed) {
|
|
|
98
94
|
return { min_Q, max_Q, avg_Q };
|
|
99
95
|
}
|
|
100
96
|
/** Update quality range as production progresses (variance may change). */
|
|
101
|
-
function updateQualityRange(currentRange
|
|
97
|
+
function updateQualityRange(currentRange) {
|
|
102
98
|
// For simplicity, keep range constant; could adjust based on worker fatigue, etc.
|
|
103
99
|
return currentRange;
|
|
104
100
|
}
|
|
@@ -139,7 +135,7 @@ export function advanceAssemblyStep(step, worker, deltaTime_s, availableTools) {
|
|
|
139
135
|
}
|
|
140
136
|
// ── Utility Functions ────────────────────────────────────────────────────────
|
|
141
137
|
/** Estimate time to complete a batch given workers and workshop. */
|
|
142
|
-
export function estimateBatchCompletionTime(batchSize, workers
|
|
138
|
+
export function estimateBatchCompletionTime(batchSize, workers) {
|
|
143
139
|
if (workers.length === 0)
|
|
144
140
|
return Infinity;
|
|
145
141
|
let totalSkill = 0;
|
|
@@ -43,7 +43,7 @@ material: Material): MaterialPropertyModifier;
|
|
|
43
43
|
* Extract materials from inventory items that are of kind "material".
|
|
44
44
|
* Returns map of materialTypeId to total quantity (kg) and average quality.
|
|
45
45
|
*/
|
|
46
|
-
export declare function getAvailableMaterials(
|
|
46
|
+
export declare function getAvailableMaterials(materials: readonly Material[]): Map<string, {
|
|
47
47
|
totalKg: number;
|
|
48
48
|
avgQuality_Q: Q;
|
|
49
49
|
}>;
|
|
@@ -74,7 +74,6 @@ material) {
|
|
|
74
74
|
if (!materialType) {
|
|
75
75
|
return {}; // No effect for unknown material
|
|
76
76
|
}
|
|
77
|
-
const qualityFactor = material.quality_Q / SCALE.Q; // 0–1
|
|
78
77
|
const modifiers = {};
|
|
79
78
|
// Strength affects durability and damage
|
|
80
79
|
modifiers.durabilityMul = clampQ(Math.round(q(0.80) + mulDiv(materialType.strength_Q, q(0.40), SCALE.Q)), q(0.50), q(1.50));
|
|
@@ -92,10 +91,19 @@ material) {
|
|
|
92
91
|
* Extract materials from inventory items that are of kind "material".
|
|
93
92
|
* Returns map of materialTypeId to total quantity (kg) and average quality.
|
|
94
93
|
*/
|
|
95
|
-
export function getAvailableMaterials(
|
|
94
|
+
export function getAvailableMaterials(materials) {
|
|
96
95
|
const map = new Map();
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
for (const mat of materials) {
|
|
97
|
+
const existing = map.get(mat.materialTypeId);
|
|
98
|
+
if (existing) {
|
|
99
|
+
const newTotalKg = existing.totalKg + mat.quantity_kg;
|
|
100
|
+
const avgQuality_Q = Math.round((existing.avgQuality_Q * existing.totalKg + mat.quality_Q * mat.quantity_kg) / newTotalKg);
|
|
101
|
+
map.set(mat.materialTypeId, { totalKg: newTotalKg, avgQuality_Q });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
map.set(mat.materialTypeId, { totalKg: mat.quantity_kg, avgQuality_Q: mat.quality_Q });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
99
107
|
return map;
|
|
100
108
|
}
|
|
101
109
|
// ── Utility Functions ────────────────────────────────────────────────────────
|
|
@@ -60,7 +60,7 @@ export declare function validateRecipeFeasibility(recipe: Recipe, inventory: Inv
|
|
|
60
60
|
* Calculate crafting cost (time, material consumption) based on recipe, materials, and workshop bonuses.
|
|
61
61
|
*/
|
|
62
62
|
export declare function calculateCraftingCost(recipe: Recipe, materialQualities: Map<string, Q>, // itemId -> quality_Q
|
|
63
|
-
workshopTimeReduction_Q?: Q
|
|
63
|
+
workshopTimeReduction_Q?: Q): {
|
|
64
64
|
time_s: number;
|
|
65
65
|
materialQualityAvg_Q: Q;
|
|
66
66
|
};
|
|
@@ -116,7 +116,7 @@ function getEntitySkill(entity, skillType) {
|
|
|
116
116
|
* Calculate crafting cost (time, material consumption) based on recipe, materials, and workshop bonuses.
|
|
117
117
|
*/
|
|
118
118
|
export function calculateCraftingCost(recipe, materialQualities, // itemId -> quality_Q
|
|
119
|
-
workshopTimeReduction_Q = q(1.0)
|
|
119
|
+
workshopTimeReduction_Q = q(1.0)) {
|
|
120
120
|
// Average material quality (default q(0.50))
|
|
121
121
|
let totalQuality = 0;
|
|
122
122
|
let count = 0;
|
|
@@ -62,7 +62,7 @@ export declare function calculateRepairNeed(item: ItemInstance): {
|
|
|
62
62
|
* @param toolQuality_Q Quality of tools being used (0-1)
|
|
63
63
|
* @param seed RNG seed for determinism
|
|
64
64
|
*/
|
|
65
|
-
export declare function resolveRepair(item: ItemInstance, crafterCognitionQ: Q, toolQuality_Q: Q,
|
|
65
|
+
export declare function resolveRepair(item: ItemInstance, crafterCognitionQ: Q, toolQuality_Q: Q, _seed: number): RepairResult;
|
|
66
66
|
/** Quick repair with no skill check (field repair). */
|
|
67
67
|
export declare function fieldRepair(item: ItemInstance): RepairResult;
|
|
68
68
|
/** Calculate item value based on durability and modifications. */
|
|
@@ -161,7 +161,7 @@ export function calculateRepairNeed(item) {
|
|
|
161
161
|
* @param toolQuality_Q Quality of tools being used (0-1)
|
|
162
162
|
* @param seed RNG seed for determinism
|
|
163
163
|
*/
|
|
164
|
-
export function resolveRepair(item, crafterCognitionQ, toolQuality_Q,
|
|
164
|
+
export function resolveRepair(item, crafterCognitionQ, toolQuality_Q, _seed) {
|
|
165
165
|
if (!hasDurability(item)) {
|
|
166
166
|
return {
|
|
167
167
|
success: false,
|
package/dist/src/legend.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export declare function createLegendRegistry(): LegendRegistry;
|
|
|
62
62
|
export declare function registerLegend(registry: LegendRegistry, legend: Legend): void;
|
|
63
63
|
/** Get all legends about a specific entity. */
|
|
64
64
|
export declare function getLegendsBySubject(registry: LegendRegistry, subjectId: number): Legend[];
|
|
65
|
-
/** Derive NPC-
|
|
65
|
+
/** Derive NPC-behaviour modifiers from a legend. */
|
|
66
66
|
export declare function getLegendEffect(legend: Legend): LegendEffect;
|
|
67
67
|
/**
|
|
68
68
|
* Determine whether an NPC "knows" a legend.
|
package/dist/src/legend.js
CHANGED
|
@@ -141,7 +141,7 @@ export function getLegendsBySubject(registry, subjectId) {
|
|
|
141
141
|
.filter((l) => l !== undefined);
|
|
142
142
|
}
|
|
143
143
|
// ── Effects ───────────────────────────────────────────────────────────────────
|
|
144
|
-
/** Derive NPC-
|
|
144
|
+
/** Derive NPC-behaviour modifiers from a legend. */
|
|
145
145
|
export function getLegendEffect(legend) {
|
|
146
146
|
const f = legend.fame_Q;
|
|
147
147
|
switch (legend.reputation) {
|
package/dist/src/modding.d.ts
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
* mod file. The network replication layer (CE-11) compares fingerprints across clients
|
|
9
9
|
* to guarantee all participants use identical mod definitions.
|
|
10
10
|
*
|
|
11
|
-
* **Layer 2 — Post-tick
|
|
11
|
+
* **Layer 2 — Post-tick behaviour hooks**
|
|
12
12
|
* `registerPostTickHook(id, fn)` registers an observer callback that the host fires
|
|
13
13
|
* after each `stepWorld` call via `runPostTickHooks(world)`. Hooks are purely
|
|
14
14
|
* observational — they MUST NOT mutate `WorldState` during the call. Because they run
|
|
15
15
|
* outside the kernel path they cannot break determinism.
|
|
16
16
|
*
|
|
17
|
-
* **Layer 3 — AI
|
|
17
|
+
* **Layer 3 — AI behaviour node overrides**
|
|
18
18
|
* `registerBehaviorNode(id, factory)` installs a named factory for custom
|
|
19
19
|
* `BehaviorNode` implementations. `loadScenario` (CE-3) can reference them by id in
|
|
20
20
|
* scenario JSON. AI overrides require explicit host opt-in.
|
|
@@ -75,7 +75,7 @@ export type BehaviorNodeFactory = (...args: unknown[]) => BehaviorNode;
|
|
|
75
75
|
* Register a named factory for a custom `BehaviorNode` implementation.
|
|
76
76
|
*
|
|
77
77
|
* The factory will be looked up by id when `loadScenario` (CE-3) encounters an
|
|
78
|
-
* `"aiOverride"` reference in scenario JSON, or when a host builds a
|
|
78
|
+
* `"aiOverride"` reference in scenario JSON, or when a host builds a behaviour
|
|
79
79
|
* tree programmatically:
|
|
80
80
|
*
|
|
81
81
|
* ```typescript
|
|
@@ -87,32 +87,32 @@ export type BehaviorNodeFactory = (...args: unknown[]) => BehaviorNode;
|
|
|
87
87
|
* ```
|
|
88
88
|
*
|
|
89
89
|
* **Deterministic multiplayer**: AI overrides affect simulation output. All
|
|
90
|
-
* clients must register the same
|
|
90
|
+
* clients must register the same behaviour nodes (verified via `computeModManifest`)
|
|
91
91
|
* before joining a session.
|
|
92
92
|
*
|
|
93
93
|
* Re-registering an existing id overwrites the previous factory.
|
|
94
94
|
*/
|
|
95
95
|
export declare function registerBehaviorNode(id: string, factory: BehaviorNodeFactory): void;
|
|
96
96
|
/**
|
|
97
|
-
* Remove a previously registered
|
|
97
|
+
* Remove a previously registered behaviour node factory.
|
|
98
98
|
* Returns `true` if the factory existed and was removed.
|
|
99
99
|
*/
|
|
100
100
|
export declare function unregisterBehaviorNode(id: string): boolean;
|
|
101
101
|
/**
|
|
102
|
-
* Look up a registered
|
|
102
|
+
* Look up a registered behaviour node factory by id.
|
|
103
103
|
* Returns `undefined` if not found.
|
|
104
104
|
*/
|
|
105
105
|
export declare function getBehaviorNode(id: string): BehaviorNodeFactory | undefined;
|
|
106
|
-
/** Return the ids of all registered
|
|
106
|
+
/** Return the ids of all registered behaviour node factories in registration order. */
|
|
107
107
|
export declare function listBehaviorNodes(): string[];
|
|
108
|
-
/** Remove all
|
|
108
|
+
/** Remove all behaviour node factories (useful for testing and hot-reload scenarios). */
|
|
109
109
|
export declare function clearBehaviorNodes(): void;
|
|
110
110
|
export interface ModManifest {
|
|
111
111
|
/** Sorted list of all data mod ids currently in the CE-12 catalog. */
|
|
112
112
|
dataIds: string[];
|
|
113
113
|
/** Sorted list of all registered post-tick hook ids. */
|
|
114
114
|
hookIds: string[];
|
|
115
|
-
/** Sorted list of all registered
|
|
115
|
+
/** Sorted list of all registered behaviour node ids. */
|
|
116
116
|
behaviorIds: string[];
|
|
117
117
|
/**
|
|
118
118
|
* Single fingerprint covering all three id lists.
|
|
@@ -122,7 +122,7 @@ export interface ModManifest {
|
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* Compute a session manifest covering all active mods (CE-12 catalog entries,
|
|
125
|
-
* post-tick hooks, and AI
|
|
125
|
+
* post-tick hooks, and AI behaviour node overrides).
|
|
126
126
|
*
|
|
127
127
|
* The `fingerprint` is a deterministic 8-char hex string suitable for
|
|
128
128
|
* multiplayer session comparison. Clients are considered mod-compatible iff
|
|
@@ -133,5 +133,5 @@ export interface ModManifest {
|
|
|
133
133
|
* on `catalog.ts`.
|
|
134
134
|
*/
|
|
135
135
|
export declare function computeModManifest(catalogIds?: string[]): ModManifest;
|
|
136
|
-
/** Remove all hooks and
|
|
136
|
+
/** Remove all hooks and behaviour node factories. Does not affect the CE-12 catalog. */
|
|
137
137
|
export declare function clearAllMods(): void;
|