@its-not-rocket-science/ananke 0.1.46 → 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 +20 -6
- 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/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/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ 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
|
+
|
|
9
23
|
## [0.1.46] — 2026-03-27
|
|
10
24
|
|
|
11
25
|
### Added
|
|
@@ -41,7 +55,7 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
41
55
|
- `WonderEffects { stabilityBonus_Q, moraleBonus_Q, researchPointBonus, unrestReduction_Q, tradeIncomeBonus_Q, defenseBonus_Q, epidemicResistance_Q }` — advisory bundle.
|
|
42
56
|
- `WONDER_BASE_COST_CU`: grand_library 150k → great_pyramid 1,000k cu.
|
|
43
57
|
- `WONDER_TYPICAL_DAYS`: grand_library 180 → great_pyramid 3,650 days (10 years).
|
|
44
|
-
- `WONDER_BASE_EFFECTS`: distinct niches — great_wall highest
|
|
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)).
|
|
45
59
|
- `WONDER_DAMAGED_EFFECT_MUL = q(0.50)` — damaged wonders provide half effects.
|
|
46
60
|
- `WONDER_REPAIR_COST_FRAC = q(0.25)` — repair costs 25% of base construction cost.
|
|
47
61
|
- `createWonderProject(projectId, polityId, type, startTick)` — factory.
|
|
@@ -781,15 +795,15 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
781
795
|
- **CE-16 · Modding Support** (`src/modding.ts`)
|
|
782
796
|
- Layer 1 — `hashMod(json)`: deterministic FNV-1a fingerprint (8-char hex) for any
|
|
783
797
|
parsed JSON mod file; canonical key-sorted serialisation ensures order-independence.
|
|
784
|
-
- Layer 2 — Post-tick
|
|
798
|
+
- Layer 2 — Post-tick behaviour hooks: `registerPostTickHook / unregisterPostTickHook /
|
|
785
799
|
runPostTickHooks / listPostTickHooks / clearPostTickHooks`; hooks fire after
|
|
786
800
|
`stepWorld`, are purely observational (logging, analytics, renderer updates).
|
|
787
|
-
- Layer 3 — AI
|
|
801
|
+
- Layer 3 — AI behaviour node registry: `registerBehaviorNode / unregisterBehaviorNode /
|
|
788
802
|
getBehaviorNode / listBehaviorNodes / clearBehaviorNodes`; custom `BehaviorNode`
|
|
789
|
-
factories registered by id for scenario and
|
|
803
|
+
factories registered by id for scenario and behaviour-tree composition.
|
|
790
804
|
- Session fingerprint: `computeModManifest(catalogIds)` returns sorted id lists and a
|
|
791
805
|
single fingerprint covering all three layers for multiplayer client validation.
|
|
792
|
-
- `clearAllMods()` resets hooks and
|
|
806
|
+
- `clearAllMods()` resets hooks and behaviour nodes (catalog unchanged).
|
|
793
807
|
- 42 tests in `test/modding.test.ts`; exported via `src/index.ts`.
|
|
794
808
|
|
|
795
809
|
- **CE-14 · Socio-Economic Campaign Layer → Stable Promotion**
|
|
@@ -884,7 +898,7 @@ Adding new **optional** fields to these interfaces is never a breaking change.
|
|
|
884
898
|
|
|
885
899
|
- **CE-16 · Modding Support — HashMod, Post-tick Hooks, Behaviour Node Registry** (`src/parallel.ts`)
|
|
886
900
|
- Three-layer modding contract: FNV-1a data fingerprinting, observational
|
|
887
|
-
post-tick hooks, and named AI
|
|
901
|
+
post-tick hooks, and named AI behaviour node factories. computeModManifest()
|
|
888
902
|
provides a single session fingerprint for multiplayer client validation.
|
|
889
903
|
- exported via src/index.ts.
|
|
890
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;
|
package/dist/src/modding.js
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.
|
|
@@ -117,7 +117,7 @@ const _behaviorNodes = new Map();
|
|
|
117
117
|
* Register a named factory for a custom `BehaviorNode` implementation.
|
|
118
118
|
*
|
|
119
119
|
* The factory will be looked up by id when `loadScenario` (CE-3) encounters an
|
|
120
|
-
* `"aiOverride"` reference in scenario JSON, or when a host builds a
|
|
120
|
+
* `"aiOverride"` reference in scenario JSON, or when a host builds a behaviour
|
|
121
121
|
* tree programmatically:
|
|
122
122
|
*
|
|
123
123
|
* ```typescript
|
|
@@ -129,7 +129,7 @@ const _behaviorNodes = new Map();
|
|
|
129
129
|
* ```
|
|
130
130
|
*
|
|
131
131
|
* **Deterministic multiplayer**: AI overrides affect simulation output. All
|
|
132
|
-
* clients must register the same
|
|
132
|
+
* clients must register the same behaviour nodes (verified via `computeModManifest`)
|
|
133
133
|
* before joining a session.
|
|
134
134
|
*
|
|
135
135
|
* Re-registering an existing id overwrites the previous factory.
|
|
@@ -140,30 +140,30 @@ export function registerBehaviorNode(id, factory) {
|
|
|
140
140
|
_behaviorNodes.set(id, factory);
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
|
-
* Remove a previously registered
|
|
143
|
+
* Remove a previously registered behaviour node factory.
|
|
144
144
|
* Returns `true` if the factory existed and was removed.
|
|
145
145
|
*/
|
|
146
146
|
export function unregisterBehaviorNode(id) {
|
|
147
147
|
return _behaviorNodes.delete(id);
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
150
|
-
* Look up a registered
|
|
150
|
+
* Look up a registered behaviour node factory by id.
|
|
151
151
|
* Returns `undefined` if not found.
|
|
152
152
|
*/
|
|
153
153
|
export function getBehaviorNode(id) {
|
|
154
154
|
return _behaviorNodes.get(id);
|
|
155
155
|
}
|
|
156
|
-
/** Return the ids of all registered
|
|
156
|
+
/** Return the ids of all registered behaviour node factories in registration order. */
|
|
157
157
|
export function listBehaviorNodes() {
|
|
158
158
|
return [..._behaviorNodes.keys()];
|
|
159
159
|
}
|
|
160
|
-
/** Remove all
|
|
160
|
+
/** Remove all behaviour node factories (useful for testing and hot-reload scenarios). */
|
|
161
161
|
export function clearBehaviorNodes() {
|
|
162
162
|
_behaviorNodes.clear();
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
165
|
* Compute a session manifest covering all active mods (CE-12 catalog entries,
|
|
166
|
-
* post-tick hooks, and AI
|
|
166
|
+
* post-tick hooks, and AI behaviour node overrides).
|
|
167
167
|
*
|
|
168
168
|
* The `fingerprint` is a deterministic 8-char hex string suitable for
|
|
169
169
|
* multiplayer session comparison. Clients are considered mod-compatible iff
|
|
@@ -181,7 +181,7 @@ export function computeModManifest(catalogIds = []) {
|
|
|
181
181
|
const fingerprint = fnv1a32(combined).toString(16).padStart(8, "0");
|
|
182
182
|
return { dataIds, hookIds, behaviorIds, fingerprint };
|
|
183
183
|
}
|
|
184
|
-
/** Remove all hooks and
|
|
184
|
+
/** Remove all hooks and behaviour node factories. Does not affect the CE-12 catalog. */
|
|
185
185
|
export function clearAllMods() {
|
|
186
186
|
_hooks.clear();
|
|
187
187
|
_behaviorNodes.clear();
|
|
@@ -31,9 +31,9 @@ export function renderArcSummary(arc) {
|
|
|
31
31
|
rise_of_hero: (a) => `The hero's journey of ${actorNames(a)} spans ${entryCount(a)} pivotal moments.`,
|
|
32
32
|
tragic_fall: (a) => `${actorNames(a)}'s descent from grace, marked by betrayal and loss.`,
|
|
33
33
|
rivalry: (a) => `An enduring rivalry between ${actorNames(a)} unfolding across ${entryCount(a)} confrontations.`,
|
|
34
|
-
great_migration: (
|
|
35
|
-
settlement_growth: (
|
|
36
|
-
fallen_settlement: (
|
|
34
|
+
great_migration: (_a) => `A mass migration that reshaped the region.`,
|
|
35
|
+
settlement_growth: (_a) => `The rise of a settlement from humble beginnings to prosperity.`,
|
|
36
|
+
fallen_settlement: (_a) => `The tragic fall of a once-great settlement.`,
|
|
37
37
|
legendary_craftsman: (a) => `${actorNames(a)}'s masterworks will be remembered for generations.`,
|
|
38
38
|
notorious_villain: (a) => `The terrifying rise of ${actorNames(a)}.`,
|
|
39
39
|
unlikely_friendship: (a) => `An unexpected bond formed between ${actorNames(a)} against all odds.`,
|
|
@@ -40,7 +40,7 @@ const DELIVERY_TEMPLATE = {
|
|
|
40
40
|
currency: 50,
|
|
41
41
|
},
|
|
42
42
|
objectiveGenerators: [
|
|
43
|
-
(
|
|
43
|
+
(_ctx) => ({
|
|
44
44
|
objectiveId: "collect_package",
|
|
45
45
|
description: "Collect the package from the quest giver",
|
|
46
46
|
type: "collect_item",
|
|
@@ -129,7 +129,7 @@ const INVESTIGATION_TEMPLATE = {
|
|
|
129
129
|
state: "available",
|
|
130
130
|
hidden: false,
|
|
131
131
|
}),
|
|
132
|
-
(
|
|
132
|
+
(_ctx) => ({
|
|
133
133
|
objectiveId: "report_findings",
|
|
134
134
|
description: "Return and report your findings",
|
|
135
135
|
type: "dialogue_choice",
|
|
@@ -185,7 +185,7 @@ const COLLECTION_TEMPLATE = {
|
|
|
185
185
|
currency: 60,
|
|
186
186
|
},
|
|
187
187
|
objectiveGenerators: [
|
|
188
|
-
(
|
|
188
|
+
(_ctx) => ({
|
|
189
189
|
objectiveId: "collect_items",
|
|
190
190
|
description: "Collect the requested items",
|
|
191
191
|
type: "collect_item",
|
|
@@ -224,7 +224,7 @@ const WAIT_TEMPLATE = {
|
|
|
224
224
|
state: "available",
|
|
225
225
|
hidden: false,
|
|
226
226
|
}),
|
|
227
|
-
(
|
|
227
|
+
(_ctx) => ({
|
|
228
228
|
objectiveId: "stand_watch",
|
|
229
229
|
description: "Stand watch for the required duration",
|
|
230
230
|
type: "wait_duration",
|
|
@@ -88,12 +88,12 @@ export function generateSettlementNeeds(settlement) {
|
|
|
88
88
|
suggestedReward: 200,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
// Recent raid →
|
|
91
|
+
// Recent raid → defence need
|
|
92
92
|
if (settlement.safetyStatus.ticksSinceLastRaid < 100) {
|
|
93
93
|
needs.push({
|
|
94
94
|
type: "defense",
|
|
95
95
|
priority: 8,
|
|
96
|
-
description: "Recent raid requires improved
|
|
96
|
+
description: "Recent raid requires improved defences",
|
|
97
97
|
suggestedReward: 300,
|
|
98
98
|
});
|
|
99
99
|
}
|
package/dist/src/settlement.d.ts
CHANGED
|
@@ -135,7 +135,7 @@ export interface AvailableServices {
|
|
|
135
135
|
export declare function getAvailableServices(settlement: Settlement): AvailableServices;
|
|
136
136
|
/** Record a raid/siege on a settlement. */
|
|
137
137
|
export declare function recordRaid(settlement: Settlement, attackerFactionId: number, casualties: number, tick: number): void;
|
|
138
|
-
/** Update settlement
|
|
138
|
+
/** Update settlement defences. */
|
|
139
139
|
export declare function updateDefenses(settlement: Settlement, hasDefenses: boolean): void;
|
|
140
140
|
/** Serialize settlement to JSON-friendly format. */
|
|
141
141
|
export declare function serializeSettlement(settlement: Settlement): unknown;
|
package/dist/src/settlement.js
CHANGED
|
@@ -351,7 +351,7 @@ function getMedicalCareLevel(level) {
|
|
|
351
351
|
default: return "none";
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
|
-
// ── Settlement
|
|
354
|
+
// ── Settlement Defence ─────────────────────────────────────────────────────────
|
|
355
355
|
/** Record a raid/siege on a settlement. */
|
|
356
356
|
export function recordRaid(settlement, attackerFactionId, casualties, tick) {
|
|
357
357
|
settlement.safetyStatus.ticksSinceLastRaid = 0;
|
|
@@ -367,7 +367,7 @@ export function recordRaid(settlement, attackerFactionId, casualties, tick) {
|
|
|
367
367
|
settlement.population = Math.max(0, settlement.population - casualties);
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
|
-
/** Update settlement
|
|
370
|
+
/** Update settlement defences. */
|
|
371
371
|
export function updateDefenses(settlement, hasDefenses) {
|
|
372
372
|
settlement.safetyStatus.hasDefenses = hasDefenses;
|
|
373
373
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CE-10 — Pre-built AI
|
|
2
|
+
* CE-10 — Pre-built AI Behaviour Tree Library
|
|
3
3
|
*
|
|
4
4
|
* A thin, composable layer over the existing AI decision system. Each
|
|
5
5
|
* `BehaviorNode` receives the ticking entity, the current world state, and
|
|
@@ -31,7 +31,7 @@ import type { KernelContext } from "../context.js";
|
|
|
31
31
|
import type { Command } from "../commands.js";
|
|
32
32
|
import { type Q } from "../../units.js";
|
|
33
33
|
/**
|
|
34
|
-
* A single node in a
|
|
34
|
+
* A single node in a behaviour tree.
|
|
35
35
|
*
|
|
36
36
|
* `tick` is called once per AI frame (typically once per `stepWorld` tick).
|
|
37
37
|
* Returns a `Command` if this node produces an action, or `null` if the node's
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CE-10 — Pre-built AI
|
|
2
|
+
* CE-10 — Pre-built AI Behaviour Tree Library
|
|
3
3
|
*
|
|
4
4
|
* A thin, composable layer over the existing AI decision system. Each
|
|
5
5
|
* `BehaviorNode` receives the ticking entity, the current world state, and
|
|
@@ -302,7 +302,7 @@ export function WithProbability(probability_Q, inner, salt = 0) {
|
|
|
302
302
|
},
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
-
// ── Pre-built
|
|
305
|
+
// ── Pre-built behaviour tree presets ─────────────────────────────────────────
|
|
306
306
|
/**
|
|
307
307
|
* Standard aggressive attacker: attack `targetId` at full intensity.
|
|
308
308
|
* Falls back to retreat if badly shocked (shock ≥ q(0.70)).
|
|
@@ -81,7 +81,7 @@ export interface BodySegment {
|
|
|
81
81
|
*/
|
|
82
82
|
regeneratesViaMolting?: boolean;
|
|
83
83
|
/**
|
|
84
|
-
* Intrinsic structural
|
|
84
|
+
* Intrinsic structural armour resist (joules) — energy absorbed by the shell
|
|
85
85
|
* before damage channels are allocated. Distinct from worn equipment armour.
|
|
86
86
|
* Absent or 0 = no intrinsic resistance.
|
|
87
87
|
*/
|
package/dist/src/sim/kernel.js
CHANGED
|
@@ -1368,7 +1368,7 @@ export function applyImpactToInjury(target, wpn, energy_J, region, armoured, tra
|
|
|
1368
1368
|
}
|
|
1369
1369
|
}
|
|
1370
1370
|
const armourShift = armoured ? q(0.75) : q(1.0);
|
|
1371
|
-
// Phase 8C: intrinsic exoskeleton
|
|
1371
|
+
// Phase 8C: intrinsic exoskeleton armour — absorbed before damage channels are allocated
|
|
1372
1372
|
if (seg?.intrinsicArmor_J !== undefined && seg.intrinsicArmor_J > 0) {
|
|
1373
1373
|
energy_J = Math.max(0, energy_J - seg.intrinsicArmor_J);
|
|
1374
1374
|
if (energy_J === 0)
|
|
@@ -48,7 +48,7 @@ export function stepMoraleForEntity(world, e, index, spatial, aliveBeforeTick, t
|
|
|
48
48
|
}
|
|
49
49
|
let fearQ = e.condition.fearQ;
|
|
50
50
|
const wasRouting = isRouting(fearQ, distressTol);
|
|
51
|
-
// 1. Suppression ticks add fear per tick — scaled by
|
|
51
|
+
// 1. Suppression ticks add fear per tick — scaled by calibre multiplier (Feature 1)
|
|
52
52
|
if (e.condition.suppressedTicks > 0) {
|
|
53
53
|
const supMul = e.condition.suppressionFearMul ?? SCALE.Q;
|
|
54
54
|
fearQ = clampQ(fearQ + qMul(FEAR_PER_SUPPRESSION_TICK, supMul), 0, SCALE.Q);
|