@its-not-rocket-science/ananke 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/LICENSE +21 -0
- package/README.md +2199 -0
- package/STABLE_API.md +266 -0
- package/dist/src/anatomy/anatomy-compiler.d.ts +14 -0
- package/dist/src/anatomy/anatomy-compiler.js +277 -0
- package/dist/src/anatomy/anatomy-contracts.d.ts +94 -0
- package/dist/src/anatomy/anatomy-contracts.js +1 -0
- package/dist/src/anatomy/anatomy-helpers.d.ts +82 -0
- package/dist/src/anatomy/anatomy-helpers.js +233 -0
- package/dist/src/anatomy/anatomy-schema.d.ts +28 -0
- package/dist/src/anatomy/anatomy-schema.js +388 -0
- package/dist/src/anatomy/index.d.ts +4 -0
- package/dist/src/anatomy/index.js +4 -0
- package/dist/src/archetypes.d.ts +87 -0
- package/dist/src/archetypes.js +285 -0
- package/dist/src/arena.d.ts +173 -0
- package/dist/src/arena.js +695 -0
- package/dist/src/bridge/bridge-engine.d.ts +46 -0
- package/dist/src/bridge/bridge-engine.js +252 -0
- package/dist/src/bridge/index.d.ts +4 -0
- package/dist/src/bridge/index.js +5 -0
- package/dist/src/bridge/interpolation.d.ts +64 -0
- package/dist/src/bridge/interpolation.js +130 -0
- package/dist/src/bridge/mapping.d.ts +33 -0
- package/dist/src/bridge/mapping.js +54 -0
- package/dist/src/bridge/types.d.ts +94 -0
- package/dist/src/bridge/types.js +2 -0
- package/dist/src/campaign.d.ts +141 -0
- package/dist/src/campaign.js +235 -0
- package/dist/src/channels.d.ts +15 -0
- package/dist/src/channels.js +20 -0
- package/dist/src/chronicle.d.ts +124 -0
- package/dist/src/chronicle.js +232 -0
- package/dist/src/collective-activities.d.ts +154 -0
- package/dist/src/collective-activities.js +247 -0
- package/dist/src/competence/acoustic.d.ts +101 -0
- package/dist/src/competence/acoustic.js +242 -0
- package/dist/src/competence/catalogue.d.ts +30 -0
- package/dist/src/competence/catalogue.js +241 -0
- package/dist/src/competence/crafting.d.ts +35 -0
- package/dist/src/competence/crafting.js +88 -0
- package/dist/src/competence/engineering.d.ts +53 -0
- package/dist/src/competence/engineering.js +108 -0
- package/dist/src/competence/framework.d.ts +68 -0
- package/dist/src/competence/framework.js +694 -0
- package/dist/src/competence/index.d.ts +12 -0
- package/dist/src/competence/index.js +13 -0
- package/dist/src/competence/interspecies.d.ts +81 -0
- package/dist/src/competence/interspecies.js +108 -0
- package/dist/src/competence/language.d.ts +79 -0
- package/dist/src/competence/language.js +115 -0
- package/dist/src/competence/naturalist.d.ts +97 -0
- package/dist/src/competence/naturalist.js +187 -0
- package/dist/src/competence/navigation.d.ts +24 -0
- package/dist/src/competence/navigation.js +48 -0
- package/dist/src/competence/performance.d.ts +125 -0
- package/dist/src/competence/performance.js +210 -0
- package/dist/src/competence/teaching.d.ts +64 -0
- package/dist/src/competence/teaching.js +121 -0
- package/dist/src/competence/willpower.d.ts +74 -0
- package/dist/src/competence/willpower.js +114 -0
- package/dist/src/crafting/index.d.ts +55 -0
- package/dist/src/crafting/index.js +229 -0
- package/dist/src/crafting/manufacturing.d.ts +83 -0
- package/dist/src/crafting/manufacturing.js +165 -0
- package/dist/src/crafting/materials.d.ts +53 -0
- package/dist/src/crafting/materials.js +120 -0
- package/dist/src/crafting/recipes.d.ts +75 -0
- package/dist/src/crafting/recipes.js +233 -0
- package/dist/src/crafting/workshops.d.ts +61 -0
- package/dist/src/crafting/workshops.js +170 -0
- package/dist/src/debug.d.ts +86 -0
- package/dist/src/debug.js +76 -0
- package/dist/src/derive.d.ts +21 -0
- package/dist/src/derive.js +88 -0
- package/dist/src/describe.d.ts +29 -0
- package/dist/src/describe.js +276 -0
- package/dist/src/dialogue.d.ts +122 -0
- package/dist/src/dialogue.js +266 -0
- package/dist/src/dist.d.ts +20 -0
- package/dist/src/dist.js +39 -0
- package/dist/src/downtime.d.ts +89 -0
- package/dist/src/downtime.js +391 -0
- package/dist/src/economy.d.ts +116 -0
- package/dist/src/economy.js +182 -0
- package/dist/src/emotional-contagion.d.ts +142 -0
- package/dist/src/emotional-contagion.js +274 -0
- package/dist/src/equipment.d.ts +206 -0
- package/dist/src/equipment.js +598 -0
- package/dist/src/faction.d.ts +102 -0
- package/dist/src/faction.js +237 -0
- package/dist/src/generate.d.ts +35 -0
- package/dist/src/generate.js +166 -0
- package/dist/src/index.d.ts +42 -0
- package/dist/src/index.js +54 -0
- package/dist/src/inheritance.d.ts +69 -0
- package/dist/src/inheritance.js +136 -0
- package/dist/src/inventory.d.ts +194 -0
- package/dist/src/inventory.js +637 -0
- package/dist/src/item-durability.d.ts +69 -0
- package/dist/src/item-durability.js +308 -0
- package/dist/src/legend.d.ts +97 -0
- package/dist/src/legend.js +269 -0
- package/dist/src/lod.d.ts +9 -0
- package/dist/src/lod.js +84 -0
- package/dist/src/metrics.d.ts +51 -0
- package/dist/src/metrics.js +91 -0
- package/dist/src/model3d.d.ts +138 -0
- package/dist/src/model3d.js +214 -0
- package/dist/src/mythology.d.ts +101 -0
- package/dist/src/mythology.js +308 -0
- package/dist/src/narrative-render.d.ts +42 -0
- package/dist/src/narrative-render.js +194 -0
- package/dist/src/narrative-stress.d.ts +123 -0
- package/dist/src/narrative-stress.js +183 -0
- package/dist/src/narrative.d.ts +44 -0
- package/dist/src/narrative.js +257 -0
- package/dist/src/party.d.ts +70 -0
- package/dist/src/party.js +226 -0
- package/dist/src/polity.d.ts +262 -0
- package/dist/src/polity.js +398 -0
- package/dist/src/presets.d.ts +42 -0
- package/dist/src/presets.js +170 -0
- package/dist/src/progression.d.ts +170 -0
- package/dist/src/progression.js +256 -0
- package/dist/src/quest-generators.d.ts +76 -0
- package/dist/src/quest-generators.js +534 -0
- package/dist/src/quest.d.ts +239 -0
- package/dist/src/quest.js +520 -0
- package/dist/src/relationships-effects.d.ts +75 -0
- package/dist/src/relationships-effects.js +219 -0
- package/dist/src/relationships.d.ts +104 -0
- package/dist/src/relationships.js +347 -0
- package/dist/src/replay.d.ts +47 -0
- package/dist/src/replay.js +82 -0
- package/dist/src/rng.d.ts +9 -0
- package/dist/src/rng.js +37 -0
- package/dist/src/settlement-services.d.ts +67 -0
- package/dist/src/settlement-services.js +267 -0
- package/dist/src/settlement.d.ts +143 -0
- package/dist/src/settlement.js +419 -0
- package/dist/src/sim/action.d.ts +28 -0
- package/dist/src/sim/action.js +12 -0
- package/dist/src/sim/aging.d.ts +95 -0
- package/dist/src/sim/aging.js +243 -0
- package/dist/src/sim/ai/decide.d.ts +10 -0
- package/dist/src/sim/ai/decide.js +267 -0
- package/dist/src/sim/ai/perception.d.ts +12 -0
- package/dist/src/sim/ai/perception.js +54 -0
- package/dist/src/sim/ai/personality.d.ts +54 -0
- package/dist/src/sim/ai/personality.js +202 -0
- package/dist/src/sim/ai/presets.d.ts +2 -0
- package/dist/src/sim/ai/presets.js +28 -0
- package/dist/src/sim/ai/system.d.ts +6 -0
- package/dist/src/sim/ai/system.js +13 -0
- package/dist/src/sim/ai/targeting.d.ts +8 -0
- package/dist/src/sim/ai/targeting.js +42 -0
- package/dist/src/sim/ai/types.d.ts +14 -0
- package/dist/src/sim/ai/types.js +1 -0
- package/dist/src/sim/body.d.ts +9 -0
- package/dist/src/sim/body.js +32 -0
- package/dist/src/sim/bodyplan.d.ts +161 -0
- package/dist/src/sim/bodyplan.js +677 -0
- package/dist/src/sim/capability.d.ts +135 -0
- package/dist/src/sim/capability.js +8 -0
- package/dist/src/sim/combat.d.ts +21 -0
- package/dist/src/sim/combat.js +77 -0
- package/dist/src/sim/commandBuilders.d.ts +11 -0
- package/dist/src/sim/commandBuilders.js +39 -0
- package/dist/src/sim/commands.d.ts +71 -0
- package/dist/src/sim/commands.js +8 -0
- package/dist/src/sim/condition.d.ts +35 -0
- package/dist/src/sim/condition.js +21 -0
- package/dist/src/sim/cone.d.ts +40 -0
- package/dist/src/sim/cone.js +44 -0
- package/dist/src/sim/context.d.ts +68 -0
- package/dist/src/sim/context.js +1 -0
- package/dist/src/sim/density.d.ts +14 -0
- package/dist/src/sim/density.js +33 -0
- package/dist/src/sim/disease.d.ts +141 -0
- package/dist/src/sim/disease.js +353 -0
- package/dist/src/sim/entity.d.ts +251 -0
- package/dist/src/sim/entity.js +19 -0
- package/dist/src/sim/events.d.ts +25 -0
- package/dist/src/sim/events.js +5 -0
- package/dist/src/sim/explosion.d.ts +40 -0
- package/dist/src/sim/explosion.js +40 -0
- package/dist/src/sim/formation-unit.d.ts +138 -0
- package/dist/src/sim/formation-unit.js +197 -0
- package/dist/src/sim/formation.d.ts +12 -0
- package/dist/src/sim/formation.js +54 -0
- package/dist/src/sim/frontage.d.ts +30 -0
- package/dist/src/sim/frontage.js +84 -0
- package/dist/src/sim/grapple.d.ts +100 -0
- package/dist/src/sim/grapple.js +480 -0
- package/dist/src/sim/hazard.d.ts +104 -0
- package/dist/src/sim/hazard.js +201 -0
- package/dist/src/sim/hydrostatic.d.ts +58 -0
- package/dist/src/sim/hydrostatic.js +117 -0
- package/dist/src/sim/impairment.d.ts +20 -0
- package/dist/src/sim/impairment.js +162 -0
- package/dist/src/sim/indexing.d.ts +7 -0
- package/dist/src/sim/indexing.js +7 -0
- package/dist/src/sim/injury.d.ts +54 -0
- package/dist/src/sim/injury.js +66 -0
- package/dist/src/sim/intent.d.ts +26 -0
- package/dist/src/sim/intent.js +7 -0
- package/dist/src/sim/kernel.d.ts +45 -0
- package/dist/src/sim/kernel.js +1992 -0
- package/dist/src/sim/kinds.d.ts +64 -0
- package/dist/src/sim/kinds.js +56 -0
- package/dist/src/sim/knockback.d.ts +50 -0
- package/dist/src/sim/knockback.js +82 -0
- package/dist/src/sim/limb.d.ts +48 -0
- package/dist/src/sim/limb.js +78 -0
- package/dist/src/sim/medical.d.ts +32 -0
- package/dist/src/sim/medical.js +33 -0
- package/dist/src/sim/morale.d.ts +69 -0
- package/dist/src/sim/morale.js +92 -0
- package/dist/src/sim/mount.d.ts +150 -0
- package/dist/src/sim/mount.js +225 -0
- package/dist/src/sim/nutrition.d.ts +74 -0
- package/dist/src/sim/nutrition.js +168 -0
- package/dist/src/sim/occlusion.d.ts +8 -0
- package/dist/src/sim/occlusion.js +71 -0
- package/dist/src/sim/push.d.ts +11 -0
- package/dist/src/sim/push.js +79 -0
- package/dist/src/sim/ranged.d.ts +44 -0
- package/dist/src/sim/ranged.js +69 -0
- package/dist/src/sim/seeds.d.ts +3 -0
- package/dist/src/sim/seeds.js +16 -0
- package/dist/src/sim/sensory-extended.d.ts +103 -0
- package/dist/src/sim/sensory-extended.js +181 -0
- package/dist/src/sim/sensory.d.ts +38 -0
- package/dist/src/sim/sensory.js +109 -0
- package/dist/src/sim/skills.d.ts +70 -0
- package/dist/src/sim/skills.js +69 -0
- package/dist/src/sim/sleep.d.ts +107 -0
- package/dist/src/sim/sleep.js +215 -0
- package/dist/src/sim/spatial.d.ts +8 -0
- package/dist/src/sim/spatial.js +59 -0
- package/dist/src/sim/step/capability.d.ts +8 -0
- package/dist/src/sim/step/capability.js +77 -0
- package/dist/src/sim/step/concentration.d.ts +9 -0
- package/dist/src/sim/step/concentration.js +25 -0
- package/dist/src/sim/step/effects.d.ts +17 -0
- package/dist/src/sim/step/effects.js +96 -0
- package/dist/src/sim/step/energy.d.ts +3 -0
- package/dist/src/sim/step/energy.js +31 -0
- package/dist/src/sim/step/hazards.d.ts +4 -0
- package/dist/src/sim/step/hazards.js +19 -0
- package/dist/src/sim/step/injury.d.ts +10 -0
- package/dist/src/sim/step/injury.js +353 -0
- package/dist/src/sim/step/morale.d.ts +11 -0
- package/dist/src/sim/step/morale.js +130 -0
- package/dist/src/sim/step/movement.d.ts +5 -0
- package/dist/src/sim/step/movement.js +172 -0
- package/dist/src/sim/step/push.d.ts +11 -0
- package/dist/src/sim/step/push.js +79 -0
- package/dist/src/sim/step/substances.d.ts +3 -0
- package/dist/src/sim/step/substances.js +75 -0
- package/dist/src/sim/substance.d.ts +38 -0
- package/dist/src/sim/substance.js +57 -0
- package/dist/src/sim/systemic-toxicology.d.ts +109 -0
- package/dist/src/sim/systemic-toxicology.js +263 -0
- package/dist/src/sim/team.d.ts +9 -0
- package/dist/src/sim/team.js +37 -0
- package/dist/src/sim/tech.d.ts +36 -0
- package/dist/src/sim/tech.js +46 -0
- package/dist/src/sim/terrain.d.ts +121 -0
- package/dist/src/sim/terrain.js +141 -0
- package/dist/src/sim/testing.d.ts +13 -0
- package/dist/src/sim/testing.js +100 -0
- package/dist/src/sim/thermoregulation.d.ts +77 -0
- package/dist/src/sim/thermoregulation.js +161 -0
- package/dist/src/sim/tick.d.ts +3 -0
- package/dist/src/sim/tick.js +3 -0
- package/dist/src/sim/toxicology.d.ts +52 -0
- package/dist/src/sim/toxicology.js +104 -0
- package/dist/src/sim/trace.d.ts +141 -0
- package/dist/src/sim/trace.js +1 -0
- package/dist/src/sim/tuning.d.ts +16 -0
- package/dist/src/sim/tuning.js +42 -0
- package/dist/src/sim/vec3.d.ts +14 -0
- package/dist/src/sim/vec3.js +31 -0
- package/dist/src/sim/weapon_dynamics.d.ts +102 -0
- package/dist/src/sim/weapon_dynamics.js +142 -0
- package/dist/src/sim/weather.d.ts +95 -0
- package/dist/src/sim/weather.js +105 -0
- package/dist/src/sim/world.d.ts +52 -0
- package/dist/src/sim/world.js +1 -0
- package/dist/src/sim/wound-aging.d.ts +120 -0
- package/dist/src/sim/wound-aging.js +223 -0
- package/dist/src/species.d.ts +106 -0
- package/dist/src/species.js +664 -0
- package/dist/src/story-arcs.d.ts +17 -0
- package/dist/src/story-arcs.js +276 -0
- package/dist/src/tech-diffusion.d.ts +80 -0
- package/dist/src/tech-diffusion.js +185 -0
- package/dist/src/traits.d.ts +25 -0
- package/dist/src/traits.js +178 -0
- package/dist/src/types.d.ts +117 -0
- package/dist/src/types.js +1 -0
- package/dist/src/units.d.ts +41 -0
- package/dist/src/units.js +64 -0
- package/dist/src/weapons.d.ts +20 -0
- package/dist/src/weapons.js +824 -0
- package/dist/src/world-generation.d.ts +52 -0
- package/dist/src/world-generation.js +301 -0
- package/package.json +74 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// src/settlement.ts — Phase 44: Settlement & Base Building
|
|
2
|
+
//
|
|
3
|
+
// Persistent locations that can be constructed, upgraded, and populated.
|
|
4
|
+
// Settlements provide services, storage, and serve as quest hubs.
|
|
5
|
+
import { q, clampQ, SCALE, mulDiv } from "./units.js";
|
|
6
|
+
import { createInventory } from "./inventory.js";
|
|
7
|
+
export const SETTLEMENT_TIER_NAMES = {
|
|
8
|
+
0: "Camp",
|
|
9
|
+
1: "Hamlet",
|
|
10
|
+
2: "Village",
|
|
11
|
+
3: "Town",
|
|
12
|
+
4: "City",
|
|
13
|
+
};
|
|
14
|
+
// ── Settlement Creation ───────────────────────────────────────────────────────
|
|
15
|
+
/** Create a new settlement. */
|
|
16
|
+
export function createSettlement(settlementId, name, position, tick, tier = 0, factionId) {
|
|
17
|
+
const settlement = {
|
|
18
|
+
settlementId,
|
|
19
|
+
name,
|
|
20
|
+
position,
|
|
21
|
+
tier,
|
|
22
|
+
facilities: {
|
|
23
|
+
forge: 0,
|
|
24
|
+
medical: tier >= 1 ? 1 : 0,
|
|
25
|
+
market: tier >= 1 ? 1 : 0,
|
|
26
|
+
barracks: 0,
|
|
27
|
+
temple: 0,
|
|
28
|
+
},
|
|
29
|
+
population: tier === 0 ? 5 : tier * 50,
|
|
30
|
+
populationCap: calculatePopulationCap(tier, { forge: 0, medical: 0, market: 0, barracks: 0, temple: 0 }),
|
|
31
|
+
factionId,
|
|
32
|
+
sharedStorage: undefined,
|
|
33
|
+
activeProjects: [],
|
|
34
|
+
history: [{
|
|
35
|
+
tick,
|
|
36
|
+
type: "founded",
|
|
37
|
+
description: `${name} was founded as a ${SETTLEMENT_TIER_NAMES[tier]}`,
|
|
38
|
+
}],
|
|
39
|
+
safetyStatus: {
|
|
40
|
+
ticksSinceLastRaid: 1000,
|
|
41
|
+
hasDefenses: false,
|
|
42
|
+
recentCasualties: 0,
|
|
43
|
+
},
|
|
44
|
+
foodSurplus_Q: q(0.5),
|
|
45
|
+
foundedAtTick: tick,
|
|
46
|
+
lastUpdateTick: tick,
|
|
47
|
+
};
|
|
48
|
+
// Add shared storage for villages and above
|
|
49
|
+
if (tier >= 2) {
|
|
50
|
+
settlement.sharedStorage = createInventory(-1); // -1 indicates shared/npc inventory
|
|
51
|
+
}
|
|
52
|
+
return settlement;
|
|
53
|
+
}
|
|
54
|
+
/** Calculate population cap based on tier and facilities. */
|
|
55
|
+
export function calculatePopulationCap(tier, facilities) {
|
|
56
|
+
// Base capacity by tier
|
|
57
|
+
const baseCap = {
|
|
58
|
+
0: 10, // Camp
|
|
59
|
+
1: 50, // Hamlet
|
|
60
|
+
2: 200, // Village
|
|
61
|
+
3: 1000, // Town
|
|
62
|
+
4: 5000, // City
|
|
63
|
+
};
|
|
64
|
+
let cap = baseCap[tier];
|
|
65
|
+
// Barracks increases cap (housing)
|
|
66
|
+
cap += facilities.barracks * 50;
|
|
67
|
+
// Medical facility allows healthier population density
|
|
68
|
+
cap += facilities.medical * 25;
|
|
69
|
+
return cap;
|
|
70
|
+
}
|
|
71
|
+
// ── Settlement Registry ───────────────────────────────────────────────────────
|
|
72
|
+
/** Create a new settlement registry. */
|
|
73
|
+
export function createSettlementRegistry() {
|
|
74
|
+
return {
|
|
75
|
+
settlements: new Map(),
|
|
76
|
+
byPosition: new Map(),
|
|
77
|
+
byFaction: new Map(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Register a settlement in the registry. */
|
|
81
|
+
export function registerSettlement(registry, settlement) {
|
|
82
|
+
registry.settlements.set(settlement.settlementId, settlement);
|
|
83
|
+
const posKey = `${settlement.position.x},${settlement.position.y}`;
|
|
84
|
+
registry.byPosition.set(posKey, settlement.settlementId);
|
|
85
|
+
if (settlement.factionId !== undefined) {
|
|
86
|
+
let factionSet = registry.byFaction.get(settlement.factionId);
|
|
87
|
+
if (!factionSet) {
|
|
88
|
+
factionSet = new Set();
|
|
89
|
+
registry.byFaction.set(settlement.factionId, factionSet);
|
|
90
|
+
}
|
|
91
|
+
factionSet.add(settlement.settlementId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Remove a settlement from the registry. */
|
|
95
|
+
export function unregisterSettlement(registry, settlementId) {
|
|
96
|
+
const settlement = registry.settlements.get(settlementId);
|
|
97
|
+
if (!settlement)
|
|
98
|
+
return false;
|
|
99
|
+
registry.settlements.delete(settlementId);
|
|
100
|
+
const posKey = `${settlement.position.x},${settlement.position.y}`;
|
|
101
|
+
registry.byPosition.delete(posKey);
|
|
102
|
+
if (settlement.factionId !== undefined) {
|
|
103
|
+
const factionSet = registry.byFaction.get(settlement.factionId);
|
|
104
|
+
if (factionSet) {
|
|
105
|
+
factionSet.delete(settlementId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
/** Get settlement by position. */
|
|
111
|
+
export function getSettlementAtPosition(registry, x, y) {
|
|
112
|
+
const posKey = `${x},${y}`;
|
|
113
|
+
const settlementId = registry.byPosition.get(posKey);
|
|
114
|
+
if (settlementId) {
|
|
115
|
+
return registry.settlements.get(settlementId);
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
/** Get all settlements for a faction. */
|
|
120
|
+
export function getFactionSettlements(registry, factionId) {
|
|
121
|
+
const settlementIds = registry.byFaction.get(factionId);
|
|
122
|
+
if (!settlementIds)
|
|
123
|
+
return [];
|
|
124
|
+
return Array.from(settlementIds)
|
|
125
|
+
.map((id) => registry.settlements.get(id))
|
|
126
|
+
.filter((s) => s !== undefined);
|
|
127
|
+
}
|
|
128
|
+
/** Find nearest settlement to a position. */
|
|
129
|
+
export function findNearestSettlement(registry, x, y) {
|
|
130
|
+
let nearest;
|
|
131
|
+
let nearestDist = Infinity;
|
|
132
|
+
for (const settlement of registry.settlements.values()) {
|
|
133
|
+
const dx = settlement.position.x - x;
|
|
134
|
+
const dy = settlement.position.y - y;
|
|
135
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
136
|
+
if (dist < nearestDist) {
|
|
137
|
+
nearest = settlement;
|
|
138
|
+
nearestDist = dist;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!nearest)
|
|
142
|
+
return undefined;
|
|
143
|
+
return { settlement: nearest, distance: nearestDist };
|
|
144
|
+
}
|
|
145
|
+
// ── Construction Projects ──────────────────────────────────────────────────────
|
|
146
|
+
/** Facility upgrade costs and requirements. */
|
|
147
|
+
export const FACILITY_UPGRADE_COSTS = {
|
|
148
|
+
forge: {
|
|
149
|
+
0: { materials: {}, laborHours: 0 },
|
|
150
|
+
1: { materials: { stone: 50, wood: 100 }, laborHours: 100 },
|
|
151
|
+
2: { materials: { stone: 150, iron: 50, wood: 200 }, laborHours: 300 },
|
|
152
|
+
3: { materials: { stone: 400, iron: 150, steel: 50 }, laborHours: 800 },
|
|
153
|
+
4: { materials: { stone: 1000, steel: 200, mithril: 20 }, laborHours: 2000 },
|
|
154
|
+
},
|
|
155
|
+
medical: {
|
|
156
|
+
0: { materials: {}, laborHours: 0 },
|
|
157
|
+
1: { materials: { wood: 50, cloth: 30 }, laborHours: 80 },
|
|
158
|
+
2: { materials: { wood: 150, stone: 100, cloth: 100 }, laborHours: 250 },
|
|
159
|
+
3: { materials: { stone: 300, glass: 100, iron: 50 }, laborHours: 600 },
|
|
160
|
+
4: { materials: { stone: 800, glass: 300, steel: 100 }, laborHours: 1500 },
|
|
161
|
+
},
|
|
162
|
+
market: {
|
|
163
|
+
0: { materials: {}, laborHours: 0 },
|
|
164
|
+
1: { materials: { wood: 80 }, laborHours: 60 },
|
|
165
|
+
2: { materials: { wood: 200, stone: 100 }, laborHours: 200 },
|
|
166
|
+
3: { materials: { stone: 400, wood: 300, iron: 30 }, laborHours: 500 },
|
|
167
|
+
4: { materials: { stone: 1200, marble: 200, steel: 50 }, laborHours: 1200 },
|
|
168
|
+
},
|
|
169
|
+
barracks: {
|
|
170
|
+
0: { materials: {}, laborHours: 0 },
|
|
171
|
+
1: { materials: { wood: 100 }, laborHours: 80 },
|
|
172
|
+
2: { materials: { wood: 300, stone: 100 }, laborHours: 250 },
|
|
173
|
+
3: { materials: { stone: 500, wood: 200, iron: 50 }, laborHours: 600 },
|
|
174
|
+
4: { materials: { stone: 1500, steel: 200 }, laborHours: 1500 },
|
|
175
|
+
},
|
|
176
|
+
temple: {
|
|
177
|
+
0: { materials: {}, laborHours: 0 },
|
|
178
|
+
1: { materials: { wood: 60, cloth: 20 }, laborHours: 100 },
|
|
179
|
+
2: { materials: { stone: 200, wood: 100, cloth: 50 }, laborHours: 300 },
|
|
180
|
+
3: { materials: { stone: 600, glass: 100, gold: 10 }, laborHours: 800 },
|
|
181
|
+
4: { materials: { stone: 2000, marble: 500, gold: 100, crystal: 50 }, laborHours: 2500 },
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
/** Start a construction project. */
|
|
185
|
+
export function startConstructionProject(settlement, facility, targetLevel, tick) {
|
|
186
|
+
const currentLevel = settlement.facilities[facility];
|
|
187
|
+
if (targetLevel <= currentLevel) {
|
|
188
|
+
return { success: false, reason: "already_at_or_above_level" };
|
|
189
|
+
}
|
|
190
|
+
if (targetLevel > currentLevel + 1) {
|
|
191
|
+
return { success: false, reason: "must_upgrade_sequentially" };
|
|
192
|
+
}
|
|
193
|
+
// Check if project already exists for this facility
|
|
194
|
+
const existing = settlement.activeProjects.find((p) => p.targetFacility === facility);
|
|
195
|
+
if (existing) {
|
|
196
|
+
return { success: false, reason: "project_already_active" };
|
|
197
|
+
}
|
|
198
|
+
const costs = FACILITY_UPGRADE_COSTS[facility][targetLevel];
|
|
199
|
+
if (!costs) {
|
|
200
|
+
return { success: false, reason: "invalid_target_level" };
|
|
201
|
+
}
|
|
202
|
+
const project = {
|
|
203
|
+
projectId: `${settlement.settlementId}_${facility}_${targetLevel}_${tick}`,
|
|
204
|
+
targetFacility: facility,
|
|
205
|
+
targetLevel,
|
|
206
|
+
requiredResources: { ...costs.materials },
|
|
207
|
+
progress_Q: q(0),
|
|
208
|
+
contributors: [],
|
|
209
|
+
startedAtTick: tick,
|
|
210
|
+
};
|
|
211
|
+
settlement.activeProjects.push(project);
|
|
212
|
+
settlement.history.push({
|
|
213
|
+
tick,
|
|
214
|
+
type: "project_started",
|
|
215
|
+
description: `Started construction of ${facility} level ${targetLevel}`,
|
|
216
|
+
data: { facility, targetLevel, projectId: project.projectId },
|
|
217
|
+
});
|
|
218
|
+
return { success: true, project };
|
|
219
|
+
}
|
|
220
|
+
/** Contribute work to a construction project. */
|
|
221
|
+
export function contributeToProject(settlement, projectId, entityId, competenceQuality_Q, // From Phase 40 competence system
|
|
222
|
+
hoursWorked, tick) {
|
|
223
|
+
const project = settlement.activeProjects.find((p) => p.projectId === projectId);
|
|
224
|
+
if (!project) {
|
|
225
|
+
return { success: false, reason: "project_not_found" };
|
|
226
|
+
}
|
|
227
|
+
// Add contributor
|
|
228
|
+
if (!project.contributors.includes(entityId)) {
|
|
229
|
+
project.contributors.push(entityId);
|
|
230
|
+
}
|
|
231
|
+
// Calculate progress contribution
|
|
232
|
+
// Total required = laborHours from costs
|
|
233
|
+
const costs = FACILITY_UPGRADE_COSTS[project.targetFacility][project.targetLevel];
|
|
234
|
+
const totalLaborRequired = costs.laborHours * SCALE.Q;
|
|
235
|
+
// Progress = competenceQuality * hoursWorked / totalRequired
|
|
236
|
+
const contribution = mulDiv(competenceQuality_Q, Math.round(hoursWorked * SCALE.Q), totalLaborRequired);
|
|
237
|
+
project.progress_Q = clampQ((project.progress_Q + contribution), q(0), SCALE.Q);
|
|
238
|
+
// Check completion
|
|
239
|
+
if (project.progress_Q >= SCALE.Q) {
|
|
240
|
+
completeConstructionProject(settlement, project, tick);
|
|
241
|
+
return { success: true, completed: true };
|
|
242
|
+
}
|
|
243
|
+
return { success: true, completed: false };
|
|
244
|
+
}
|
|
245
|
+
/** Complete a construction project. */
|
|
246
|
+
function completeConstructionProject(settlement, project, tick) {
|
|
247
|
+
// Upgrade the facility
|
|
248
|
+
settlement.facilities[project.targetFacility] = project.targetLevel;
|
|
249
|
+
// Recalculate population cap
|
|
250
|
+
settlement.populationCap = calculatePopulationCap(settlement.tier, settlement.facilities);
|
|
251
|
+
// Remove from active projects
|
|
252
|
+
const idx = settlement.activeProjects.indexOf(project);
|
|
253
|
+
if (idx >= 0) {
|
|
254
|
+
settlement.activeProjects.splice(idx, 1);
|
|
255
|
+
}
|
|
256
|
+
// Record history
|
|
257
|
+
settlement.history.push({
|
|
258
|
+
tick,
|
|
259
|
+
type: "facility_upgraded",
|
|
260
|
+
description: `Completed ${project.targetFacility} level ${project.targetLevel}`,
|
|
261
|
+
entityIds: project.contributors,
|
|
262
|
+
data: { facility: project.targetFacility, level: project.targetLevel },
|
|
263
|
+
});
|
|
264
|
+
// Check for tier upgrade
|
|
265
|
+
checkTierUpgrade(settlement, tick);
|
|
266
|
+
}
|
|
267
|
+
/** Check if settlement qualifies for tier upgrade. */
|
|
268
|
+
function checkTierUpgrade(settlement, tick) {
|
|
269
|
+
const facilitySum = Object.values(settlement.facilities).reduce((a, b) => a + b, 0);
|
|
270
|
+
const requiredFacilities = (settlement.tier + 1) * 2;
|
|
271
|
+
if (facilitySum >= requiredFacilities && settlement.population >= settlement.populationCap * 0.8) {
|
|
272
|
+
const oldTier = settlement.tier;
|
|
273
|
+
settlement.tier = Math.min(4, (settlement.tier + 1));
|
|
274
|
+
if (settlement.tier > oldTier) {
|
|
275
|
+
settlement.populationCap = calculatePopulationCap(settlement.tier, settlement.facilities);
|
|
276
|
+
settlement.history.push({
|
|
277
|
+
tick,
|
|
278
|
+
type: "tier_upgraded",
|
|
279
|
+
description: `${settlement.name} has grown from ${SETTLEMENT_TIER_NAMES[oldTier]} to ${SETTLEMENT_TIER_NAMES[settlement.tier]}`,
|
|
280
|
+
data: { oldTier, newTier: settlement.tier },
|
|
281
|
+
});
|
|
282
|
+
// Add shared storage if newly qualified
|
|
283
|
+
if (settlement.tier >= 2 && !settlement.sharedStorage) {
|
|
284
|
+
settlement.sharedStorage = createInventory(-1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// ── Population Dynamics ───────────────────────────────────────────────────────
|
|
290
|
+
/** Update settlement population based on conditions. */
|
|
291
|
+
export function updateSettlementPopulation(settlement, tick) {
|
|
292
|
+
if (settlement.population >= settlement.populationCap) {
|
|
293
|
+
return { growth: 0, reason: "at_capacity" };
|
|
294
|
+
}
|
|
295
|
+
// Base growth rate
|
|
296
|
+
let growthChance = 0.001; // 0.1% per update
|
|
297
|
+
// Modifiers
|
|
298
|
+
if (settlement.foodSurplus_Q > q(0.5)) {
|
|
299
|
+
growthChance *= 1.5; // Food surplus bonus
|
|
300
|
+
}
|
|
301
|
+
else if (settlement.foodSurplus_Q < q(0.2)) {
|
|
302
|
+
growthChance *= 0.5; // Food shortage penalty
|
|
303
|
+
}
|
|
304
|
+
if (settlement.facilities.medical >= 2) {
|
|
305
|
+
growthChance *= 1.3; // Good medical facilities
|
|
306
|
+
}
|
|
307
|
+
if (settlement.safetyStatus.ticksSinceLastRaid < 100) {
|
|
308
|
+
growthChance *= 0.3; // Recent raid penalty
|
|
309
|
+
}
|
|
310
|
+
if (settlement.safetyStatus.hasDefenses) {
|
|
311
|
+
growthChance *= 1.2; // Defenses provide security
|
|
312
|
+
}
|
|
313
|
+
// Apply growth
|
|
314
|
+
const growthRoll = Math.random(); // In real system, use seeded RNG
|
|
315
|
+
if (growthRoll < growthChance) {
|
|
316
|
+
const growth = Math.max(1, Math.floor(settlement.population * 0.01));
|
|
317
|
+
const actualGrowth = Math.min(growth, settlement.populationCap - settlement.population);
|
|
318
|
+
if (actualGrowth > 0) {
|
|
319
|
+
settlement.population += actualGrowth;
|
|
320
|
+
settlement.history.push({
|
|
321
|
+
tick,
|
|
322
|
+
type: "population_changed",
|
|
323
|
+
description: `Population grew by ${actualGrowth} to ${settlement.population}`,
|
|
324
|
+
data: { change: actualGrowth, newPopulation: settlement.population },
|
|
325
|
+
});
|
|
326
|
+
return { growth: actualGrowth, reason: "natural_growth" };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return { growth: 0, reason: "no_growth" };
|
|
330
|
+
}
|
|
331
|
+
/** Get available services for a settlement. */
|
|
332
|
+
export function getAvailableServices(settlement) {
|
|
333
|
+
return {
|
|
334
|
+
repair: settlement.facilities.forge >= 1,
|
|
335
|
+
repairQualityBonus_Q: (settlement.facilities.forge * 500), // +5% per level
|
|
336
|
+
medicalCare: getMedicalCareLevel(settlement.facilities.medical),
|
|
337
|
+
training: settlement.facilities.barracks >= 1,
|
|
338
|
+
trainingBonus_Q: (settlement.facilities.barracks * 500),
|
|
339
|
+
market: settlement.facilities.market >= 1,
|
|
340
|
+
marketDiscount_Q: (settlement.facilities.market * 300), // -3% per level
|
|
341
|
+
questGeneration: settlement.tier >= 1,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function getMedicalCareLevel(level) {
|
|
345
|
+
switch (level) {
|
|
346
|
+
case 0: return "none";
|
|
347
|
+
case 1: return "basic";
|
|
348
|
+
case 2: return "skilled";
|
|
349
|
+
case 3: return "expert";
|
|
350
|
+
case 4: return "master";
|
|
351
|
+
default: return "none";
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// ── Settlement Defense ─────────────────────────────────────────────────────────
|
|
355
|
+
/** Record a raid/siege on a settlement. */
|
|
356
|
+
export function recordRaid(settlement, attackerFactionId, casualties, tick) {
|
|
357
|
+
settlement.safetyStatus.ticksSinceLastRaid = 0;
|
|
358
|
+
settlement.safetyStatus.recentCasualties = casualties;
|
|
359
|
+
settlement.history.push({
|
|
360
|
+
tick,
|
|
361
|
+
type: "raid",
|
|
362
|
+
description: `Raid by faction ${attackerFactionId}, ${casualties} casualties`,
|
|
363
|
+
data: { attackerFactionId, casualties },
|
|
364
|
+
});
|
|
365
|
+
// Population loss from casualties
|
|
366
|
+
if (casualties > 0) {
|
|
367
|
+
settlement.population = Math.max(0, settlement.population - casualties);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/** Update settlement defenses. */
|
|
371
|
+
export function updateDefenses(settlement, hasDefenses) {
|
|
372
|
+
settlement.safetyStatus.hasDefenses = hasDefenses;
|
|
373
|
+
}
|
|
374
|
+
// ── Serialization ─────────────────────────────────────────────────────────────
|
|
375
|
+
/** Serialize settlement to JSON-friendly format. */
|
|
376
|
+
export function serializeSettlement(settlement) {
|
|
377
|
+
return {
|
|
378
|
+
settlementId: settlement.settlementId,
|
|
379
|
+
name: settlement.name,
|
|
380
|
+
position: settlement.position,
|
|
381
|
+
tier: settlement.tier,
|
|
382
|
+
facilities: settlement.facilities,
|
|
383
|
+
population: settlement.population,
|
|
384
|
+
populationCap: settlement.populationCap,
|
|
385
|
+
factionId: settlement.factionId,
|
|
386
|
+
sharedStorage: settlement.sharedStorage ? undefined : undefined, // Simplified
|
|
387
|
+
activeProjects: settlement.activeProjects,
|
|
388
|
+
history: settlement.history,
|
|
389
|
+
safetyStatus: settlement.safetyStatus,
|
|
390
|
+
foodSurplus_Q: settlement.foodSurplus_Q,
|
|
391
|
+
foundedAtTick: settlement.foundedAtTick,
|
|
392
|
+
lastUpdateTick: settlement.lastUpdateTick,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/** Deserialize settlement. */
|
|
396
|
+
export function deserializeSettlement(data) {
|
|
397
|
+
const d = data;
|
|
398
|
+
return {
|
|
399
|
+
settlementId: d.settlementId ?? "",
|
|
400
|
+
name: d.name ?? "Unknown",
|
|
401
|
+
position: d.position ?? { x: 0, y: 0 },
|
|
402
|
+
tier: d.tier ?? 0,
|
|
403
|
+
facilities: d.facilities ?? {
|
|
404
|
+
forge: 0, medical: 0, market: 0, barracks: 0, temple: 0,
|
|
405
|
+
},
|
|
406
|
+
population: d.population ?? 0,
|
|
407
|
+
populationCap: d.populationCap ?? 0,
|
|
408
|
+
factionId: d.factionId,
|
|
409
|
+
sharedStorage: undefined,
|
|
410
|
+
activeProjects: Array.isArray(d.activeProjects) ? d.activeProjects : [],
|
|
411
|
+
history: Array.isArray(d.history) ? d.history : [],
|
|
412
|
+
safetyStatus: d.safetyStatus ?? {
|
|
413
|
+
ticksSinceLastRaid: 1000, hasDefenses: false, recentCasualties: 0,
|
|
414
|
+
},
|
|
415
|
+
foodSurplus_Q: d.foodSurplus_Q ?? q(0.5),
|
|
416
|
+
foundedAtTick: d.foundedAtTick ?? 0,
|
|
417
|
+
lastUpdateTick: d.lastUpdateTick ?? 0,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { I32, Q } from "../units.js";
|
|
2
|
+
export interface ActionState {
|
|
3
|
+
attackCooldownTicks: I32;
|
|
4
|
+
defenceCooldownTicks: I32;
|
|
5
|
+
grappleCooldownTicks: I32;
|
|
6
|
+
facingDirQ: {
|
|
7
|
+
x: I32;
|
|
8
|
+
y: I32;
|
|
9
|
+
z: I32;
|
|
10
|
+
};
|
|
11
|
+
weaponBindPartnerId: number;
|
|
12
|
+
weaponBindTicks: number;
|
|
13
|
+
swingMomentumQ: Q;
|
|
14
|
+
shootCooldownTicks: I32;
|
|
15
|
+
aimTicks: number;
|
|
16
|
+
aimTargetId: number;
|
|
17
|
+
roundsInMag?: number;
|
|
18
|
+
capabilityCooldowns?: Map<string, number>;
|
|
19
|
+
lastCellKey?: string;
|
|
20
|
+
staggerTicks?: number;
|
|
21
|
+
sustainedEmission?: {
|
|
22
|
+
sourceId: string;
|
|
23
|
+
effectId: string;
|
|
24
|
+
targetId?: number;
|
|
25
|
+
remainingTicks: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare const defaultAction: () => ActionState;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const defaultAction = () => ({
|
|
2
|
+
attackCooldownTicks: 0,
|
|
3
|
+
defenceCooldownTicks: 0,
|
|
4
|
+
grappleCooldownTicks: 0,
|
|
5
|
+
facingDirQ: { x: 10_000, y: 0, z: 0 },
|
|
6
|
+
weaponBindPartnerId: 0,
|
|
7
|
+
weaponBindTicks: 0,
|
|
8
|
+
swingMomentumQ: 0,
|
|
9
|
+
shootCooldownTicks: 0,
|
|
10
|
+
aimTicks: 0,
|
|
11
|
+
aimTargetId: 0,
|
|
12
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type Q } from "../units.js";
|
|
2
|
+
import type { IndividualAttributes } from "../types.js";
|
|
3
|
+
import type { Entity } from "./entity.js";
|
|
4
|
+
/**
|
|
5
|
+
* Life-stage classification derived from normalized age fraction.
|
|
6
|
+
* Species-agnostic: boundaries are proportional to lifespan, not absolute years.
|
|
7
|
+
*/
|
|
8
|
+
export type AgePhase = "infant" | "child" | "adolescent" | "young_adult" | "adult" | "elder" | "ancient";
|
|
9
|
+
/** Q-valued multipliers for each aging dimension. */
|
|
10
|
+
export interface AgeMultipliers {
|
|
11
|
+
/** Multiplier for peakForce_N, peakPower_W, continuousPower_W [Q]. */
|
|
12
|
+
muscularStrength_Q: Q;
|
|
13
|
+
/** Multiplier on reactionTime_s — > q(1.0) means slower reaction [Q]. */
|
|
14
|
+
reactionTime_Q: Q;
|
|
15
|
+
/** Multiplier for controlQuality, stability, fineControl [Q]. */
|
|
16
|
+
motorControl_Q: Q;
|
|
17
|
+
/** Multiplier for stature_m [Q]. */
|
|
18
|
+
stature_Q: Q;
|
|
19
|
+
/** Multiplier for logicalMathematical, spatial, bodilyKinesthetic, musical [Q]. */
|
|
20
|
+
cognitionFluid_Q: Q;
|
|
21
|
+
/** Multiplier for linguistic, interpersonal, intrapersonal [Q]. */
|
|
22
|
+
cognitionCrystal_Q: Q;
|
|
23
|
+
/** Multiplier for distressTolerance [Q]. */
|
|
24
|
+
distressTolerance_Q: Q;
|
|
25
|
+
}
|
|
26
|
+
/** Per-entity age accumulator stored on `entity.age`. */
|
|
27
|
+
export interface AgeState {
|
|
28
|
+
/** Elapsed seconds of this entity's life (from birth). */
|
|
29
|
+
ageSeconds: number;
|
|
30
|
+
}
|
|
31
|
+
/** Seconds in one year (non-leap). */
|
|
32
|
+
export declare const SECONDS_PER_YEAR: number;
|
|
33
|
+
/** Default lifespan for entities without a species override [years]. */
|
|
34
|
+
export declare const HUMAN_LIFESPAN_YEARS = 80;
|
|
35
|
+
/**
|
|
36
|
+
* Compute normalized age fraction [0..SCALE.Q] for a given age and lifespan.
|
|
37
|
+
*
|
|
38
|
+
* A 25-year-old human (lifespan 80) → q(0.3125).
|
|
39
|
+
* A 187-year-old elf (lifespan 600) → q(0.312) — effectively the same developmental stage.
|
|
40
|
+
*
|
|
41
|
+
* @param ageYears Current age in years.
|
|
42
|
+
* @param lifespanYears Expected lifespan (default: HUMAN_LIFESPAN_YEARS).
|
|
43
|
+
*/
|
|
44
|
+
export declare function computeAgeFrac(ageYears: number, lifespanYears?: number): Q;
|
|
45
|
+
/**
|
|
46
|
+
* Classify the entity's life stage from their normalized age fraction.
|
|
47
|
+
*
|
|
48
|
+
* Boundaries (ageFrac):
|
|
49
|
+
* infant 0–0.05 | child 0.05–0.15 | adolescent 0.15–0.22 |
|
|
50
|
+
* young_adult 0.22–0.38 | adult 0.38–0.62 | elder 0.62–0.88 | ancient 0.88+
|
|
51
|
+
*/
|
|
52
|
+
export declare function getAgePhase(ageYears: number, lifespanYears?: number): AgePhase;
|
|
53
|
+
/**
|
|
54
|
+
* Derive age-based attribute multipliers from normalized age and lifespan.
|
|
55
|
+
*
|
|
56
|
+
* All returned Q values except `reactionTime_Q` are in [0, SCALE.Q].
|
|
57
|
+
* `reactionTime_Q` may exceed SCALE.Q (values > q(1.0) indicate slower reaction
|
|
58
|
+
* than the archetype baseline).
|
|
59
|
+
*/
|
|
60
|
+
export declare function deriveAgeMultipliers(ageYears: number, lifespanYears?: number): AgeMultipliers;
|
|
61
|
+
/**
|
|
62
|
+
* Apply age multipliers to a base attribute set, returning a new object.
|
|
63
|
+
*
|
|
64
|
+
* The input `base` is treated as the archetype peak (typically from `generateIndividual`).
|
|
65
|
+
* The caller is responsible for caching the base and recomputing aged attributes when
|
|
66
|
+
* age advances (e.g. once per in-game month for campaign simulation).
|
|
67
|
+
*
|
|
68
|
+
* Attributes affected:
|
|
69
|
+
* - morphology.stature_m
|
|
70
|
+
* - performance.peakForce_N, peakPower_W, continuousPower_W
|
|
71
|
+
* - control.reactionTime_s, controlQuality, stability, fineControl
|
|
72
|
+
* - resilience.distressTolerance
|
|
73
|
+
* - cognition (if present): fluid dims + crystal dims scaled independently
|
|
74
|
+
*
|
|
75
|
+
* All Q outputs are clamped to [0, SCALE.Q]; reactionTime_s is clamped to ≥ 1.
|
|
76
|
+
*
|
|
77
|
+
* @param base Archetype-peak attributes (unmodified).
|
|
78
|
+
* @param ageYears Current age in years.
|
|
79
|
+
* @param lifespanYears Expected lifespan (default: HUMAN_LIFESPAN_YEARS).
|
|
80
|
+
*/
|
|
81
|
+
export declare function applyAgingToAttributes(base: IndividualAttributes, ageYears: number, lifespanYears?: number): IndividualAttributes;
|
|
82
|
+
/**
|
|
83
|
+
* Advance an entity's age by `elapsedSeconds`.
|
|
84
|
+
*
|
|
85
|
+
* Initializes `entity.age` if absent. Does NOT recompute attributes — the host
|
|
86
|
+
* should call `applyAgingToAttributes` when it needs current aged stats.
|
|
87
|
+
*
|
|
88
|
+
* Mutates: `entity.age`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function stepAging(entity: Entity, elapsedSeconds: number): void;
|
|
91
|
+
/**
|
|
92
|
+
* Convenience helper: return the current age in fractional years from entity.age.
|
|
93
|
+
* Returns 0 if `entity.age` is absent.
|
|
94
|
+
*/
|
|
95
|
+
export declare function entityAgeYears(entity: Entity): number;
|