@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +71 -5
  2. package/dist/src/bridge/bridge-engine.js +0 -1
  3. package/dist/src/competence/acoustic.d.ts +1 -1
  4. package/dist/src/competence/acoustic.js +1 -7
  5. package/dist/src/competence/catalogue.js +1 -1
  6. package/dist/src/competence/engineering.js +0 -2
  7. package/dist/src/competence/framework.js +2 -4
  8. package/dist/src/competence/interspecies.js +0 -2
  9. package/dist/src/competence/language.js +0 -2
  10. package/dist/src/competence/naturalist.js +1 -1
  11. package/dist/src/crafting/index.d.ts +2 -2
  12. package/dist/src/crafting/index.js +6 -7
  13. package/dist/src/crafting/manufacturing.d.ts +11 -15
  14. package/dist/src/crafting/manufacturing.js +7 -11
  15. package/dist/src/crafting/materials.d.ts +1 -1
  16. package/dist/src/crafting/materials.js +12 -4
  17. package/dist/src/crafting/recipes.d.ts +1 -1
  18. package/dist/src/crafting/recipes.js +1 -1
  19. package/dist/src/item-durability.d.ts +1 -1
  20. package/dist/src/item-durability.js +1 -1
  21. package/dist/src/legend.d.ts +1 -1
  22. package/dist/src/legend.js +1 -1
  23. package/dist/src/modding.d.ts +11 -11
  24. package/dist/src/modding.js +10 -10
  25. package/dist/src/monetary.d.ts +104 -0
  26. package/dist/src/monetary.js +158 -0
  27. package/dist/src/narrative-render.js +3 -3
  28. package/dist/src/quest-generators.js +4 -4
  29. package/dist/src/settlement-services.js +2 -2
  30. package/dist/src/settlement.d.ts +1 -1
  31. package/dist/src/settlement.js +2 -2
  32. package/dist/src/sim/ai/behavior-trees.d.ts +2 -2
  33. package/dist/src/sim/ai/behavior-trees.js +2 -2
  34. package/dist/src/sim/bodyplan.d.ts +1 -1
  35. package/dist/src/sim/kernel.js +1 -1
  36. package/dist/src/sim/step/morale.js +1 -1
  37. package/dist/src/wonders.d.ts +125 -0
  38. package/dist/src/wonders.js +264 -0
  39. 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 behavior hooks: `registerPostTickHook / unregisterPostTickHook /
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 behavior node registry: `registerBehaviorNode / unregisterBehaviorNode /
801
+ - Layer 3 — AI behaviour node registry: `registerBehaviorNode / unregisterBehaviorNode /
736
802
  getBehaviorNode / listBehaviorNodes / clearBehaviorNodes`; custom `BehaviorNode`
737
- factories registered by id for scenario and behavior-tree composition.
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 behavior nodes (catalog unchanged).
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 behavior node factories. computeModManifest()
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 analyze.
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 analyze.
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: "Analyze tactical situation and propose solution",
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, world) {
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, world);
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
- let trust_Q = clampQ((baseTrust - fearPenalty + experienceBonus), q(0), q(1.0));
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: any, // Placeholder: Item type
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 primaryWorkerId = workers.length > 0 ? workers[0].id : 0;
111
- const lineSeed = eventSeed(worldSeed, tick, primaryWorkerId, hashString(recipe.id), salt);
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, lineSeed);
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 seed = eventSeed(worldSeed, tick, lineIdHash, 0, salt);
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, workshop, seed);
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, // Placeholder: Item type
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[], seed: number): ProductionLine;
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[], // Must match assignedWorkers IDs
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[], workshop: WorkshopInstance, seed: number): {
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[], workshop: WorkshopInstance): number;
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, seed) {
15
+ export function setupProductionLine(order, workers) {
18
16
  const workerIds = workers.map(w => w.id);
19
- const qualityRange = calculateBatchQualityRange(workers, order.workshop, seed);
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, // Must match assignedWorkers IDs
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 remainingItems = productionLine.batchSize - productionLine.itemsProduced;
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, workshop, seed) {
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, workers, remainingItems, seed) {
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, workshop) {
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(inventory: any): Map<string, {
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(inventory) {
94
+ export function getAvailableMaterials(materials) {
96
95
  const map = new Map();
97
- // TODO: iterate through inventory items, filter by kind === "material"
98
- // For each material item, accumulate quantity and weighted quality.
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, workshopQualityBonus_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), workshopQualityBonus_Q = q(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, seed: number): RepairResult;
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, seed) {
164
+ export function resolveRepair(item, crafterCognitionQ, toolQuality_Q, _seed) {
165
165
  if (!hasDurability(item)) {
166
166
  return {
167
167
  success: false,
@@ -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-behavior modifiers from a legend. */
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.
@@ -141,7 +141,7 @@ export function getLegendsBySubject(registry, subjectId) {
141
141
  .filter((l) => l !== undefined);
142
142
  }
143
143
  // ── Effects ───────────────────────────────────────────────────────────────────
144
- /** Derive NPC-behavior modifiers from a legend. */
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) {
@@ -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 behavior hooks**
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 behavior node overrides**
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 behavior
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 behavior nodes (verified via `computeModManifest`)
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 behavior node factory.
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 behavior node factory by id.
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 behavior node factories in registration order. */
106
+ /** Return the ids of all registered behaviour node factories in registration order. */
107
107
  export declare function listBehaviorNodes(): string[];
108
- /** Remove all behavior node factories (useful for testing and hot-reload scenarios). */
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 behavior node ids. */
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 behavior node overrides).
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 behavior node factories. Does not affect the CE-12 catalog. */
136
+ /** Remove all hooks and behaviour node factories. Does not affect the CE-12 catalog. */
137
137
  export declare function clearAllMods(): void;