@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.
Files changed (311) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/LICENSE +21 -0
  3. package/README.md +2199 -0
  4. package/STABLE_API.md +266 -0
  5. package/dist/src/anatomy/anatomy-compiler.d.ts +14 -0
  6. package/dist/src/anatomy/anatomy-compiler.js +277 -0
  7. package/dist/src/anatomy/anatomy-contracts.d.ts +94 -0
  8. package/dist/src/anatomy/anatomy-contracts.js +1 -0
  9. package/dist/src/anatomy/anatomy-helpers.d.ts +82 -0
  10. package/dist/src/anatomy/anatomy-helpers.js +233 -0
  11. package/dist/src/anatomy/anatomy-schema.d.ts +28 -0
  12. package/dist/src/anatomy/anatomy-schema.js +388 -0
  13. package/dist/src/anatomy/index.d.ts +4 -0
  14. package/dist/src/anatomy/index.js +4 -0
  15. package/dist/src/archetypes.d.ts +87 -0
  16. package/dist/src/archetypes.js +285 -0
  17. package/dist/src/arena.d.ts +173 -0
  18. package/dist/src/arena.js +695 -0
  19. package/dist/src/bridge/bridge-engine.d.ts +46 -0
  20. package/dist/src/bridge/bridge-engine.js +252 -0
  21. package/dist/src/bridge/index.d.ts +4 -0
  22. package/dist/src/bridge/index.js +5 -0
  23. package/dist/src/bridge/interpolation.d.ts +64 -0
  24. package/dist/src/bridge/interpolation.js +130 -0
  25. package/dist/src/bridge/mapping.d.ts +33 -0
  26. package/dist/src/bridge/mapping.js +54 -0
  27. package/dist/src/bridge/types.d.ts +94 -0
  28. package/dist/src/bridge/types.js +2 -0
  29. package/dist/src/campaign.d.ts +141 -0
  30. package/dist/src/campaign.js +235 -0
  31. package/dist/src/channels.d.ts +15 -0
  32. package/dist/src/channels.js +20 -0
  33. package/dist/src/chronicle.d.ts +124 -0
  34. package/dist/src/chronicle.js +232 -0
  35. package/dist/src/collective-activities.d.ts +154 -0
  36. package/dist/src/collective-activities.js +247 -0
  37. package/dist/src/competence/acoustic.d.ts +101 -0
  38. package/dist/src/competence/acoustic.js +242 -0
  39. package/dist/src/competence/catalogue.d.ts +30 -0
  40. package/dist/src/competence/catalogue.js +241 -0
  41. package/dist/src/competence/crafting.d.ts +35 -0
  42. package/dist/src/competence/crafting.js +88 -0
  43. package/dist/src/competence/engineering.d.ts +53 -0
  44. package/dist/src/competence/engineering.js +108 -0
  45. package/dist/src/competence/framework.d.ts +68 -0
  46. package/dist/src/competence/framework.js +694 -0
  47. package/dist/src/competence/index.d.ts +12 -0
  48. package/dist/src/competence/index.js +13 -0
  49. package/dist/src/competence/interspecies.d.ts +81 -0
  50. package/dist/src/competence/interspecies.js +108 -0
  51. package/dist/src/competence/language.d.ts +79 -0
  52. package/dist/src/competence/language.js +115 -0
  53. package/dist/src/competence/naturalist.d.ts +97 -0
  54. package/dist/src/competence/naturalist.js +187 -0
  55. package/dist/src/competence/navigation.d.ts +24 -0
  56. package/dist/src/competence/navigation.js +48 -0
  57. package/dist/src/competence/performance.d.ts +125 -0
  58. package/dist/src/competence/performance.js +210 -0
  59. package/dist/src/competence/teaching.d.ts +64 -0
  60. package/dist/src/competence/teaching.js +121 -0
  61. package/dist/src/competence/willpower.d.ts +74 -0
  62. package/dist/src/competence/willpower.js +114 -0
  63. package/dist/src/crafting/index.d.ts +55 -0
  64. package/dist/src/crafting/index.js +229 -0
  65. package/dist/src/crafting/manufacturing.d.ts +83 -0
  66. package/dist/src/crafting/manufacturing.js +165 -0
  67. package/dist/src/crafting/materials.d.ts +53 -0
  68. package/dist/src/crafting/materials.js +120 -0
  69. package/dist/src/crafting/recipes.d.ts +75 -0
  70. package/dist/src/crafting/recipes.js +233 -0
  71. package/dist/src/crafting/workshops.d.ts +61 -0
  72. package/dist/src/crafting/workshops.js +170 -0
  73. package/dist/src/debug.d.ts +86 -0
  74. package/dist/src/debug.js +76 -0
  75. package/dist/src/derive.d.ts +21 -0
  76. package/dist/src/derive.js +88 -0
  77. package/dist/src/describe.d.ts +29 -0
  78. package/dist/src/describe.js +276 -0
  79. package/dist/src/dialogue.d.ts +122 -0
  80. package/dist/src/dialogue.js +266 -0
  81. package/dist/src/dist.d.ts +20 -0
  82. package/dist/src/dist.js +39 -0
  83. package/dist/src/downtime.d.ts +89 -0
  84. package/dist/src/downtime.js +391 -0
  85. package/dist/src/economy.d.ts +116 -0
  86. package/dist/src/economy.js +182 -0
  87. package/dist/src/emotional-contagion.d.ts +142 -0
  88. package/dist/src/emotional-contagion.js +274 -0
  89. package/dist/src/equipment.d.ts +206 -0
  90. package/dist/src/equipment.js +598 -0
  91. package/dist/src/faction.d.ts +102 -0
  92. package/dist/src/faction.js +237 -0
  93. package/dist/src/generate.d.ts +35 -0
  94. package/dist/src/generate.js +166 -0
  95. package/dist/src/index.d.ts +42 -0
  96. package/dist/src/index.js +54 -0
  97. package/dist/src/inheritance.d.ts +69 -0
  98. package/dist/src/inheritance.js +136 -0
  99. package/dist/src/inventory.d.ts +194 -0
  100. package/dist/src/inventory.js +637 -0
  101. package/dist/src/item-durability.d.ts +69 -0
  102. package/dist/src/item-durability.js +308 -0
  103. package/dist/src/legend.d.ts +97 -0
  104. package/dist/src/legend.js +269 -0
  105. package/dist/src/lod.d.ts +9 -0
  106. package/dist/src/lod.js +84 -0
  107. package/dist/src/metrics.d.ts +51 -0
  108. package/dist/src/metrics.js +91 -0
  109. package/dist/src/model3d.d.ts +138 -0
  110. package/dist/src/model3d.js +214 -0
  111. package/dist/src/mythology.d.ts +101 -0
  112. package/dist/src/mythology.js +308 -0
  113. package/dist/src/narrative-render.d.ts +42 -0
  114. package/dist/src/narrative-render.js +194 -0
  115. package/dist/src/narrative-stress.d.ts +123 -0
  116. package/dist/src/narrative-stress.js +183 -0
  117. package/dist/src/narrative.d.ts +44 -0
  118. package/dist/src/narrative.js +257 -0
  119. package/dist/src/party.d.ts +70 -0
  120. package/dist/src/party.js +226 -0
  121. package/dist/src/polity.d.ts +262 -0
  122. package/dist/src/polity.js +398 -0
  123. package/dist/src/presets.d.ts +42 -0
  124. package/dist/src/presets.js +170 -0
  125. package/dist/src/progression.d.ts +170 -0
  126. package/dist/src/progression.js +256 -0
  127. package/dist/src/quest-generators.d.ts +76 -0
  128. package/dist/src/quest-generators.js +534 -0
  129. package/dist/src/quest.d.ts +239 -0
  130. package/dist/src/quest.js +520 -0
  131. package/dist/src/relationships-effects.d.ts +75 -0
  132. package/dist/src/relationships-effects.js +219 -0
  133. package/dist/src/relationships.d.ts +104 -0
  134. package/dist/src/relationships.js +347 -0
  135. package/dist/src/replay.d.ts +47 -0
  136. package/dist/src/replay.js +82 -0
  137. package/dist/src/rng.d.ts +9 -0
  138. package/dist/src/rng.js +37 -0
  139. package/dist/src/settlement-services.d.ts +67 -0
  140. package/dist/src/settlement-services.js +267 -0
  141. package/dist/src/settlement.d.ts +143 -0
  142. package/dist/src/settlement.js +419 -0
  143. package/dist/src/sim/action.d.ts +28 -0
  144. package/dist/src/sim/action.js +12 -0
  145. package/dist/src/sim/aging.d.ts +95 -0
  146. package/dist/src/sim/aging.js +243 -0
  147. package/dist/src/sim/ai/decide.d.ts +10 -0
  148. package/dist/src/sim/ai/decide.js +267 -0
  149. package/dist/src/sim/ai/perception.d.ts +12 -0
  150. package/dist/src/sim/ai/perception.js +54 -0
  151. package/dist/src/sim/ai/personality.d.ts +54 -0
  152. package/dist/src/sim/ai/personality.js +202 -0
  153. package/dist/src/sim/ai/presets.d.ts +2 -0
  154. package/dist/src/sim/ai/presets.js +28 -0
  155. package/dist/src/sim/ai/system.d.ts +6 -0
  156. package/dist/src/sim/ai/system.js +13 -0
  157. package/dist/src/sim/ai/targeting.d.ts +8 -0
  158. package/dist/src/sim/ai/targeting.js +42 -0
  159. package/dist/src/sim/ai/types.d.ts +14 -0
  160. package/dist/src/sim/ai/types.js +1 -0
  161. package/dist/src/sim/body.d.ts +9 -0
  162. package/dist/src/sim/body.js +32 -0
  163. package/dist/src/sim/bodyplan.d.ts +161 -0
  164. package/dist/src/sim/bodyplan.js +677 -0
  165. package/dist/src/sim/capability.d.ts +135 -0
  166. package/dist/src/sim/capability.js +8 -0
  167. package/dist/src/sim/combat.d.ts +21 -0
  168. package/dist/src/sim/combat.js +77 -0
  169. package/dist/src/sim/commandBuilders.d.ts +11 -0
  170. package/dist/src/sim/commandBuilders.js +39 -0
  171. package/dist/src/sim/commands.d.ts +71 -0
  172. package/dist/src/sim/commands.js +8 -0
  173. package/dist/src/sim/condition.d.ts +35 -0
  174. package/dist/src/sim/condition.js +21 -0
  175. package/dist/src/sim/cone.d.ts +40 -0
  176. package/dist/src/sim/cone.js +44 -0
  177. package/dist/src/sim/context.d.ts +68 -0
  178. package/dist/src/sim/context.js +1 -0
  179. package/dist/src/sim/density.d.ts +14 -0
  180. package/dist/src/sim/density.js +33 -0
  181. package/dist/src/sim/disease.d.ts +141 -0
  182. package/dist/src/sim/disease.js +353 -0
  183. package/dist/src/sim/entity.d.ts +251 -0
  184. package/dist/src/sim/entity.js +19 -0
  185. package/dist/src/sim/events.d.ts +25 -0
  186. package/dist/src/sim/events.js +5 -0
  187. package/dist/src/sim/explosion.d.ts +40 -0
  188. package/dist/src/sim/explosion.js +40 -0
  189. package/dist/src/sim/formation-unit.d.ts +138 -0
  190. package/dist/src/sim/formation-unit.js +197 -0
  191. package/dist/src/sim/formation.d.ts +12 -0
  192. package/dist/src/sim/formation.js +54 -0
  193. package/dist/src/sim/frontage.d.ts +30 -0
  194. package/dist/src/sim/frontage.js +84 -0
  195. package/dist/src/sim/grapple.d.ts +100 -0
  196. package/dist/src/sim/grapple.js +480 -0
  197. package/dist/src/sim/hazard.d.ts +104 -0
  198. package/dist/src/sim/hazard.js +201 -0
  199. package/dist/src/sim/hydrostatic.d.ts +58 -0
  200. package/dist/src/sim/hydrostatic.js +117 -0
  201. package/dist/src/sim/impairment.d.ts +20 -0
  202. package/dist/src/sim/impairment.js +162 -0
  203. package/dist/src/sim/indexing.d.ts +7 -0
  204. package/dist/src/sim/indexing.js +7 -0
  205. package/dist/src/sim/injury.d.ts +54 -0
  206. package/dist/src/sim/injury.js +66 -0
  207. package/dist/src/sim/intent.d.ts +26 -0
  208. package/dist/src/sim/intent.js +7 -0
  209. package/dist/src/sim/kernel.d.ts +45 -0
  210. package/dist/src/sim/kernel.js +1992 -0
  211. package/dist/src/sim/kinds.d.ts +64 -0
  212. package/dist/src/sim/kinds.js +56 -0
  213. package/dist/src/sim/knockback.d.ts +50 -0
  214. package/dist/src/sim/knockback.js +82 -0
  215. package/dist/src/sim/limb.d.ts +48 -0
  216. package/dist/src/sim/limb.js +78 -0
  217. package/dist/src/sim/medical.d.ts +32 -0
  218. package/dist/src/sim/medical.js +33 -0
  219. package/dist/src/sim/morale.d.ts +69 -0
  220. package/dist/src/sim/morale.js +92 -0
  221. package/dist/src/sim/mount.d.ts +150 -0
  222. package/dist/src/sim/mount.js +225 -0
  223. package/dist/src/sim/nutrition.d.ts +74 -0
  224. package/dist/src/sim/nutrition.js +168 -0
  225. package/dist/src/sim/occlusion.d.ts +8 -0
  226. package/dist/src/sim/occlusion.js +71 -0
  227. package/dist/src/sim/push.d.ts +11 -0
  228. package/dist/src/sim/push.js +79 -0
  229. package/dist/src/sim/ranged.d.ts +44 -0
  230. package/dist/src/sim/ranged.js +69 -0
  231. package/dist/src/sim/seeds.d.ts +3 -0
  232. package/dist/src/sim/seeds.js +16 -0
  233. package/dist/src/sim/sensory-extended.d.ts +103 -0
  234. package/dist/src/sim/sensory-extended.js +181 -0
  235. package/dist/src/sim/sensory.d.ts +38 -0
  236. package/dist/src/sim/sensory.js +109 -0
  237. package/dist/src/sim/skills.d.ts +70 -0
  238. package/dist/src/sim/skills.js +69 -0
  239. package/dist/src/sim/sleep.d.ts +107 -0
  240. package/dist/src/sim/sleep.js +215 -0
  241. package/dist/src/sim/spatial.d.ts +8 -0
  242. package/dist/src/sim/spatial.js +59 -0
  243. package/dist/src/sim/step/capability.d.ts +8 -0
  244. package/dist/src/sim/step/capability.js +77 -0
  245. package/dist/src/sim/step/concentration.d.ts +9 -0
  246. package/dist/src/sim/step/concentration.js +25 -0
  247. package/dist/src/sim/step/effects.d.ts +17 -0
  248. package/dist/src/sim/step/effects.js +96 -0
  249. package/dist/src/sim/step/energy.d.ts +3 -0
  250. package/dist/src/sim/step/energy.js +31 -0
  251. package/dist/src/sim/step/hazards.d.ts +4 -0
  252. package/dist/src/sim/step/hazards.js +19 -0
  253. package/dist/src/sim/step/injury.d.ts +10 -0
  254. package/dist/src/sim/step/injury.js +353 -0
  255. package/dist/src/sim/step/morale.d.ts +11 -0
  256. package/dist/src/sim/step/morale.js +130 -0
  257. package/dist/src/sim/step/movement.d.ts +5 -0
  258. package/dist/src/sim/step/movement.js +172 -0
  259. package/dist/src/sim/step/push.d.ts +11 -0
  260. package/dist/src/sim/step/push.js +79 -0
  261. package/dist/src/sim/step/substances.d.ts +3 -0
  262. package/dist/src/sim/step/substances.js +75 -0
  263. package/dist/src/sim/substance.d.ts +38 -0
  264. package/dist/src/sim/substance.js +57 -0
  265. package/dist/src/sim/systemic-toxicology.d.ts +109 -0
  266. package/dist/src/sim/systemic-toxicology.js +263 -0
  267. package/dist/src/sim/team.d.ts +9 -0
  268. package/dist/src/sim/team.js +37 -0
  269. package/dist/src/sim/tech.d.ts +36 -0
  270. package/dist/src/sim/tech.js +46 -0
  271. package/dist/src/sim/terrain.d.ts +121 -0
  272. package/dist/src/sim/terrain.js +141 -0
  273. package/dist/src/sim/testing.d.ts +13 -0
  274. package/dist/src/sim/testing.js +100 -0
  275. package/dist/src/sim/thermoregulation.d.ts +77 -0
  276. package/dist/src/sim/thermoregulation.js +161 -0
  277. package/dist/src/sim/tick.d.ts +3 -0
  278. package/dist/src/sim/tick.js +3 -0
  279. package/dist/src/sim/toxicology.d.ts +52 -0
  280. package/dist/src/sim/toxicology.js +104 -0
  281. package/dist/src/sim/trace.d.ts +141 -0
  282. package/dist/src/sim/trace.js +1 -0
  283. package/dist/src/sim/tuning.d.ts +16 -0
  284. package/dist/src/sim/tuning.js +42 -0
  285. package/dist/src/sim/vec3.d.ts +14 -0
  286. package/dist/src/sim/vec3.js +31 -0
  287. package/dist/src/sim/weapon_dynamics.d.ts +102 -0
  288. package/dist/src/sim/weapon_dynamics.js +142 -0
  289. package/dist/src/sim/weather.d.ts +95 -0
  290. package/dist/src/sim/weather.js +105 -0
  291. package/dist/src/sim/world.d.ts +52 -0
  292. package/dist/src/sim/world.js +1 -0
  293. package/dist/src/sim/wound-aging.d.ts +120 -0
  294. package/dist/src/sim/wound-aging.js +223 -0
  295. package/dist/src/species.d.ts +106 -0
  296. package/dist/src/species.js +664 -0
  297. package/dist/src/story-arcs.d.ts +17 -0
  298. package/dist/src/story-arcs.js +276 -0
  299. package/dist/src/tech-diffusion.d.ts +80 -0
  300. package/dist/src/tech-diffusion.js +185 -0
  301. package/dist/src/traits.d.ts +25 -0
  302. package/dist/src/traits.js +178 -0
  303. package/dist/src/types.d.ts +117 -0
  304. package/dist/src/types.js +1 -0
  305. package/dist/src/units.d.ts +41 -0
  306. package/dist/src/units.js +64 -0
  307. package/dist/src/weapons.d.ts +20 -0
  308. package/dist/src/weapons.js +824 -0
  309. package/dist/src/world-generation.d.ts +52 -0
  310. package/dist/src/world-generation.js +301 -0
  311. package/package.json +74 -0
@@ -0,0 +1,94 @@
1
+ import type { Vec3 } from "../sim/vec3.js";
2
+ import type { Q } from "../units.js";
3
+ import type { AnimationHints, PoseModifier, GrapplePoseConstraint } from "../model3d.js";
4
+ import type { MotionVector, ConditionSample } from "../debug.js";
5
+ /**
6
+ * Maps a simulation segment ID to a renderer bone name, with optional offsets.
7
+ */
8
+ export interface SegmentMapping {
9
+ segmentId: string;
10
+ boneName: string;
11
+ /** Optional offset in fixed-point metres (SCALE.m). */
12
+ positionOffset?: Vec3;
13
+ /** Optional rotation offset (Euler angles) in fixed-point degrees? Not used yet. */
14
+ rotationOffset?: Vec3;
15
+ }
16
+ /**
17
+ * Complete mapping for a body plan.
18
+ */
19
+ export interface BodyPlanMapping {
20
+ /** Matches BodyPlan.id */
21
+ bodyPlanId: string;
22
+ segments: SegmentMapping[];
23
+ }
24
+ /**
25
+ * Bridge configuration supplied by the host renderer.
26
+ */
27
+ export interface BridgeConfig {
28
+ mappings: BodyPlanMapping[];
29
+ /** Allow extrapolation when render time is ahead of the latest simulation tick. Default false. */
30
+ extrapolationAllowed?: boolean;
31
+ /** Bone name to use for unmapped segments (default "root"). */
32
+ defaultBoneName?: string;
33
+ }
34
+ /**
35
+ * Per‑segment pose data after mapping to bone names.
36
+ */
37
+ export interface MappedPoseModifier {
38
+ segmentId: string;
39
+ boneName: string;
40
+ impairmentQ: Q;
41
+ structuralQ: Q;
42
+ surfaceQ: Q;
43
+ }
44
+ /**
45
+ * Fully interpolated state for a single entity at a specific render time.
46
+ */
47
+ export interface InterpolatedState {
48
+ entityId: number;
49
+ teamId: number;
50
+ position_m: Vec3;
51
+ velocity_mps: Vec3;
52
+ facing: Vec3;
53
+ animation: AnimationHints;
54
+ poseModifiers: MappedPoseModifier[];
55
+ grapple: GrapplePoseConstraint;
56
+ condition: {
57
+ shockQ: Q;
58
+ fearQ: Q;
59
+ consciousness: Q;
60
+ fluidLoss: Q;
61
+ dead: boolean;
62
+ };
63
+ /** Interpolation factor t ∈ [0, SCALE.Q] between fromTick and toTick. */
64
+ interpolationFactor: Q;
65
+ fromTick: number;
66
+ toTick: number;
67
+ }
68
+ /**
69
+ * Aggregated snapshot for one entity at one tick.
70
+ */
71
+ export interface TickSnapshot {
72
+ entityId: number;
73
+ teamId: number;
74
+ tick: number;
75
+ position_m: Vec3;
76
+ velocity_mps: Vec3;
77
+ facing: Vec3;
78
+ animation: AnimationHints;
79
+ poseModifiers: PoseModifier[];
80
+ grapple: GrapplePoseConstraint;
81
+ condition: {
82
+ shockQ: Q;
83
+ fearQ: Q;
84
+ consciousness: Q;
85
+ fluidLoss: Q;
86
+ dead: boolean;
87
+ };
88
+ }
89
+ export interface BridgeUpdate {
90
+ snapshots: TickSnapshot[];
91
+ motion?: MotionVector[];
92
+ condition?: ConditionSample[];
93
+ }
94
+ export type InterpolationMode = "lerp" | "extrapolate" | "hold";
@@ -0,0 +1,2 @@
1
+ // src/bridge/types.ts — Bridge core interfaces
2
+ export {};
@@ -0,0 +1,141 @@
1
+ import type { Q } from "./units.js";
2
+ import type { Polity } from "./polity.js";
3
+ import type { Entity } from "./sim/entity.js";
4
+ import type { DowntimeConfig, EntityRecoveryReport } from "./downtime.js";
5
+ /**
6
+ * A named region in the campaign world.
7
+ *
8
+ * `travelCost` maps destination locationId → travel time in seconds.
9
+ * Only direct routes need to be specified; multi-hop routing is the host's responsibility.
10
+ */
11
+ export interface Location {
12
+ id: string;
13
+ name: string;
14
+ /** Phase 29 integration: ambient temperature for thermoregulation during travel/rest. */
15
+ ambientTemp_Q?: Q;
16
+ /** Altitude above sea level in real metres (not fixed-point). Used for environmental reference. */
17
+ elevation_m: number;
18
+ travelCost: Map<string, number>;
19
+ }
20
+ /**
21
+ * Full persistent state of an ongoing campaign.
22
+ *
23
+ * Designed to be serialised and deserialised between play sessions.
24
+ * Maps survive round-trip via `serialiseCampaign` / `deserialiseCampaign`.
25
+ */
26
+ export interface CampaignState {
27
+ id: string;
28
+ epoch: string;
29
+ worldTime_s: number;
30
+ entities: Map<number, Entity>;
31
+ locations: Map<string, Location>;
32
+ /** Current location of each entity, keyed by entityId. */
33
+ entityLocations: Map<number, string>;
34
+ /**
35
+ * Campaign-level item stockpiles per entity (arrows, rations, bandages, etc.).
36
+ * Separate from combat loadout (`entity.loadout`), which represents equipped gear.
37
+ * Map<entityId, Map<itemId, count>>
38
+ */
39
+ entityInventories: Map<number, Map<string, number>>;
40
+ log: Array<{
41
+ worldTime_s: number;
42
+ text: string;
43
+ }>;
44
+ /**
45
+ * Polities active in this campaign (Phase 61).
46
+ * Absent until the first call to `addPolity`.
47
+ */
48
+ polities?: Map<string, Polity>;
49
+ }
50
+ /**
51
+ * Create a new campaign with a set of starting entities.
52
+ *
53
+ * Entities are deep-cloned into the registry; the originals are not retained.
54
+ * worldTime_s starts at 0.
55
+ *
56
+ * @param epoch ISO timestamp string (defaults to current system time if omitted).
57
+ */
58
+ export declare function createCampaign(id: string, entities: Entity[], epoch?: string): CampaignState;
59
+ /** Register or update a location in the campaign registry. */
60
+ export declare function addLocation(campaign: CampaignState, location: Location): void;
61
+ /** Get the current locationId for an entity, or undefined if not placed. */
62
+ export declare function getEntityLocation(campaign: CampaignState, entityId: number): string | undefined;
63
+ /**
64
+ * Merge updated entity states from a completed encounter back into the registry.
65
+ *
66
+ * All entities in `worldEntities` are deep-cloned into the campaign registry.
67
+ * Entities not present in the world are left unchanged.
68
+ */
69
+ export declare function mergeEntityState(campaign: CampaignState, worldEntities: Entity[]): void;
70
+ /**
71
+ * Advance the campaign clock by `delta_s` seconds and apply wound recovery.
72
+ *
73
+ * Delegates to `stepDowntime` for all registered entities. If
74
+ * `opts.downtimeConfig` is supplied, it is used directly. Otherwise a
75
+ * default config is built: rest=true, careLevel="none" for every entity
76
+ * (natural clotting only — no treatment resources consumed).
77
+ *
78
+ * After the simulation, each entity's `injury` in the registry is replaced
79
+ * with the final simulated injury state (`report.finalInjury`), so the
80
+ * campaign registry always reflects the current physical condition.
81
+ *
82
+ * @returns Recovery reports for all entities that were processed.
83
+ */
84
+ export declare function stepCampaignTime(campaign: CampaignState, delta_s: number, opts?: {
85
+ downtimeConfig?: DowntimeConfig;
86
+ }): EntityRecoveryReport[];
87
+ /**
88
+ * Move an entity to a new location.
89
+ *
90
+ * Looks up the travel time between the entity's current location and
91
+ * `toLocationId` from the destination location's `travelCost` map (or from
92
+ * the source location's map if no current location is set, assumes 0 travel
93
+ * time for first placement).
94
+ *
95
+ * Advances `campaign.worldTime_s` by the travel time.
96
+ *
97
+ * @returns Travel time in seconds, or -1 if the destination is unknown or
98
+ * no travel route exists between current and destination.
99
+ */
100
+ export declare function travel(campaign: CampaignState, entityId: number, toLocationId: string): number;
101
+ /**
102
+ * Debit `count` units of `itemId` from the entity's campaign inventory.
103
+ *
104
+ * Returns `true` if the debit succeeded (sufficient stock available).
105
+ * Returns `false` if the entity does not have enough of the item.
106
+ * Stock is never reduced below 0.
107
+ */
108
+ export declare function debitInventory(campaign: CampaignState, entityId: number, itemId: string, count: number): boolean;
109
+ /**
110
+ * Credit `count` units of `itemId` to the entity's campaign inventory.
111
+ * Creates the inventory entry if it does not yet exist.
112
+ */
113
+ export declare function creditInventory(campaign: CampaignState, entityId: number, itemId: string, count: number): void;
114
+ /**
115
+ * Get the current count of `itemId` in the entity's campaign inventory.
116
+ * Returns 0 if the entity has no inventory or no entry for the item.
117
+ */
118
+ export declare function getInventoryCount(campaign: CampaignState, entityId: number, itemId: string): number;
119
+ /**
120
+ * Register a Polity in the campaign.
121
+ *
122
+ * The polity is stored by reference (not cloned); the caller owns the object.
123
+ * Initialises `campaign.polities` on first use.
124
+ */
125
+ export declare function addPolity(campaign: CampaignState, polity: Polity): void;
126
+ /**
127
+ * Retrieve a Polity by id, or undefined if not registered.
128
+ */
129
+ export declare function getPolity(campaign: CampaignState, polityId: string): Polity | undefined;
130
+ /**
131
+ * Serialise a CampaignState to a JSON string.
132
+ *
133
+ * Handles all nested Map fields (entities, locations, entityLocations,
134
+ * entityInventories, entity.armourState, entity.skills, location.travelCost)
135
+ * using the `__ananke_map__` marker pattern (same as src/replay.ts).
136
+ */
137
+ export declare function serialiseCampaign(campaign: CampaignState): string;
138
+ /**
139
+ * Deserialise a CampaignState from a JSON string produced by `serialiseCampaign`.
140
+ */
141
+ export declare function deserialiseCampaign(json: string): CampaignState;
@@ -0,0 +1,235 @@
1
+ // src/campaign.ts — Phase 22: Campaign & World State
2
+ //
3
+ // Persistence layer for campaigns that span multiple encounters.
4
+ // Tracks world time, entity state, location, and campaign inventory
5
+ // between sessions. Delegates wound recovery to Phase 19 stepDowntime.
6
+ //
7
+ // No kernel import — this is a pure data-management and bookkeeping module.
8
+ import { stepDowntime } from "./downtime.js";
9
+ // ── Map-aware serialisation helpers ──────────────────────────────────────────
10
+ // (Same pattern as src/replay.ts; duplicated to avoid a cross-module dependency.)
11
+ const MAP_MARKER = "__ananke_map__";
12
+ function mapReplacer(_key, value) {
13
+ if (value instanceof Map) {
14
+ return { [MAP_MARKER]: true, entries: [...value.entries()] };
15
+ }
16
+ return value;
17
+ }
18
+ function mapReviver(_key, value) {
19
+ if (value !== null &&
20
+ typeof value === "object" &&
21
+ value[MAP_MARKER] === true) {
22
+ return new Map(value.entries);
23
+ }
24
+ return value;
25
+ }
26
+ // ── Factory ───────────────────────────────────────────────────────────────────
27
+ /**
28
+ * Create a new campaign with a set of starting entities.
29
+ *
30
+ * Entities are deep-cloned into the registry; the originals are not retained.
31
+ * worldTime_s starts at 0.
32
+ *
33
+ * @param epoch ISO timestamp string (defaults to current system time if omitted).
34
+ */
35
+ export function createCampaign(id, entities, epoch) {
36
+ const entityMap = new Map();
37
+ for (const e of entities)
38
+ entityMap.set(e.id, structuredClone(e));
39
+ return {
40
+ id,
41
+ epoch: epoch ?? new Date().toISOString(),
42
+ worldTime_s: 0,
43
+ entities: entityMap,
44
+ locations: new Map(),
45
+ entityLocations: new Map(),
46
+ entityInventories: new Map(),
47
+ log: [],
48
+ };
49
+ }
50
+ // ── Location management ───────────────────────────────────────────────────────
51
+ /** Register or update a location in the campaign registry. */
52
+ export function addLocation(campaign, location) {
53
+ campaign.locations.set(location.id, location);
54
+ }
55
+ /** Get the current locationId for an entity, or undefined if not placed. */
56
+ export function getEntityLocation(campaign, entityId) {
57
+ return campaign.entityLocations.get(entityId);
58
+ }
59
+ // ── Entity registry ───────────────────────────────────────────────────────────
60
+ /**
61
+ * Merge updated entity states from a completed encounter back into the registry.
62
+ *
63
+ * All entities in `worldEntities` are deep-cloned into the campaign registry.
64
+ * Entities not present in the world are left unchanged.
65
+ */
66
+ export function mergeEntityState(campaign, worldEntities) {
67
+ for (const e of worldEntities) {
68
+ campaign.entities.set(e.id, structuredClone(e));
69
+ }
70
+ }
71
+ // ── Time advancement ──────────────────────────────────────────────────────────
72
+ /**
73
+ * Advance the campaign clock by `delta_s` seconds and apply wound recovery.
74
+ *
75
+ * Delegates to `stepDowntime` for all registered entities. If
76
+ * `opts.downtimeConfig` is supplied, it is used directly. Otherwise a
77
+ * default config is built: rest=true, careLevel="none" for every entity
78
+ * (natural clotting only — no treatment resources consumed).
79
+ *
80
+ * After the simulation, each entity's `injury` in the registry is replaced
81
+ * with the final simulated injury state (`report.finalInjury`), so the
82
+ * campaign registry always reflects the current physical condition.
83
+ *
84
+ * @returns Recovery reports for all entities that were processed.
85
+ */
86
+ export function stepCampaignTime(campaign, delta_s, opts) {
87
+ campaign.worldTime_s += delta_s;
88
+ // Build a minimal WorldState from campaign entities
89
+ const world = {
90
+ tick: 0,
91
+ seed: 1,
92
+ entities: [...campaign.entities.values()],
93
+ };
94
+ let config;
95
+ if (opts?.downtimeConfig) {
96
+ config = opts.downtimeConfig;
97
+ }
98
+ else {
99
+ // Default: rest with no active treatment for all entities
100
+ const treatments = new Map();
101
+ for (const id of campaign.entities.keys()) {
102
+ treatments.set(id, { careLevel: "none" });
103
+ }
104
+ config = { treatments, rest: true };
105
+ }
106
+ const reports = stepDowntime(world, delta_s, config);
107
+ // Apply healed injury states back to the campaign registry
108
+ for (const report of reports) {
109
+ const entity = campaign.entities.get(report.entityId);
110
+ if (!entity)
111
+ continue;
112
+ if (report.finalInjury) {
113
+ entity.injury = report.finalInjury;
114
+ }
115
+ if (report.died) {
116
+ campaign.log.push({
117
+ worldTime_s: campaign.worldTime_s,
118
+ text: `Entity ${report.entityId} died during recovery.`,
119
+ });
120
+ }
121
+ }
122
+ return reports;
123
+ }
124
+ // ── Travel ────────────────────────────────────────────────────────────────────
125
+ /**
126
+ * Move an entity to a new location.
127
+ *
128
+ * Looks up the travel time between the entity's current location and
129
+ * `toLocationId` from the destination location's `travelCost` map (or from
130
+ * the source location's map if no current location is set, assumes 0 travel
131
+ * time for first placement).
132
+ *
133
+ * Advances `campaign.worldTime_s` by the travel time.
134
+ *
135
+ * @returns Travel time in seconds, or -1 if the destination is unknown or
136
+ * no travel route exists between current and destination.
137
+ */
138
+ export function travel(campaign, entityId, toLocationId) {
139
+ const dest = campaign.locations.get(toLocationId);
140
+ if (!dest)
141
+ return -1;
142
+ const currentLoc = campaign.entityLocations.get(entityId);
143
+ let travelTime = 0;
144
+ if (currentLoc !== undefined && currentLoc !== toLocationId) {
145
+ const src = campaign.locations.get(currentLoc);
146
+ // Look up travel time from source first, then from destination (bidirectional)
147
+ const cost = src?.travelCost.get(toLocationId) ?? dest.travelCost.get(currentLoc);
148
+ if (cost === undefined)
149
+ return -1; // no route
150
+ travelTime = cost;
151
+ }
152
+ campaign.entityLocations.set(entityId, toLocationId);
153
+ campaign.worldTime_s += travelTime;
154
+ campaign.log.push({
155
+ worldTime_s: campaign.worldTime_s,
156
+ text: `Entity ${entityId} travelled to ${dest.name} (${travelTime}s).`,
157
+ });
158
+ return travelTime;
159
+ }
160
+ // ── Inventory ─────────────────────────────────────────────────────────────────
161
+ /**
162
+ * Debit `count` units of `itemId` from the entity's campaign inventory.
163
+ *
164
+ * Returns `true` if the debit succeeded (sufficient stock available).
165
+ * Returns `false` if the entity does not have enough of the item.
166
+ * Stock is never reduced below 0.
167
+ */
168
+ export function debitInventory(campaign, entityId, itemId, count) {
169
+ const inv = campaign.entityInventories.get(entityId);
170
+ const current = inv?.get(itemId) ?? 0;
171
+ if (current < count)
172
+ return false;
173
+ const updatedInv = inv ?? new Map();
174
+ updatedInv.set(itemId, current - count);
175
+ campaign.entityInventories.set(entityId, updatedInv);
176
+ campaign.log.push({
177
+ worldTime_s: campaign.worldTime_s,
178
+ text: `Entity ${entityId} used ${count}× ${itemId} (${current - count} remaining).`,
179
+ });
180
+ return true;
181
+ }
182
+ /**
183
+ * Credit `count` units of `itemId` to the entity's campaign inventory.
184
+ * Creates the inventory entry if it does not yet exist.
185
+ */
186
+ export function creditInventory(campaign, entityId, itemId, count) {
187
+ let inv = campaign.entityInventories.get(entityId);
188
+ if (!inv) {
189
+ inv = new Map();
190
+ campaign.entityInventories.set(entityId, inv);
191
+ }
192
+ inv.set(itemId, (inv.get(itemId) ?? 0) + count);
193
+ }
194
+ /**
195
+ * Get the current count of `itemId` in the entity's campaign inventory.
196
+ * Returns 0 if the entity has no inventory or no entry for the item.
197
+ */
198
+ export function getInventoryCount(campaign, entityId, itemId) {
199
+ return campaign.entityInventories.get(entityId)?.get(itemId) ?? 0;
200
+ }
201
+ // ── Polity integration (Phase 61) ────────────────────────────────────────────
202
+ /**
203
+ * Register a Polity in the campaign.
204
+ *
205
+ * The polity is stored by reference (not cloned); the caller owns the object.
206
+ * Initialises `campaign.polities` on first use.
207
+ */
208
+ export function addPolity(campaign, polity) {
209
+ if (!campaign.polities)
210
+ campaign.polities = new Map();
211
+ campaign.polities.set(polity.id, polity);
212
+ }
213
+ /**
214
+ * Retrieve a Polity by id, or undefined if not registered.
215
+ */
216
+ export function getPolity(campaign, polityId) {
217
+ return campaign.polities?.get(polityId);
218
+ }
219
+ // ── Serialisation ─────────────────────────────────────────────────────────────
220
+ /**
221
+ * Serialise a CampaignState to a JSON string.
222
+ *
223
+ * Handles all nested Map fields (entities, locations, entityLocations,
224
+ * entityInventories, entity.armourState, entity.skills, location.travelCost)
225
+ * using the `__ananke_map__` marker pattern (same as src/replay.ts).
226
+ */
227
+ export function serialiseCampaign(campaign) {
228
+ return JSON.stringify(campaign, mapReplacer);
229
+ }
230
+ /**
231
+ * Deserialise a CampaignState from a JSON string produced by `serialiseCampaign`.
232
+ */
233
+ export function deserialiseCampaign(json) {
234
+ return JSON.parse(json, mapReviver);
235
+ }
@@ -0,0 +1,15 @@
1
+ export declare enum DamageChannel {
2
+ Kinetic = 0,
3
+ Thermal = 1,
4
+ Electrical = 2,
5
+ Chemical = 3,
6
+ Radiation = 4,
7
+ Corrosive = 5,
8
+ Suffocation = 6,
9
+ ControlDisruption = 7,
10
+ /** Phase 11C: directed energy — plasma, laser, sonic. Armour resists via reflectivity. */
11
+ Energy = 8
12
+ }
13
+ export type ChannelMask = number;
14
+ export declare const channelMask: (...chs: DamageChannel[]) => ChannelMask;
15
+ export declare const hasChannel: (mask: ChannelMask, ch: DamageChannel) => boolean;
@@ -0,0 +1,20 @@
1
+ export var DamageChannel;
2
+ (function (DamageChannel) {
3
+ DamageChannel[DamageChannel["Kinetic"] = 0] = "Kinetic";
4
+ DamageChannel[DamageChannel["Thermal"] = 1] = "Thermal";
5
+ DamageChannel[DamageChannel["Electrical"] = 2] = "Electrical";
6
+ DamageChannel[DamageChannel["Chemical"] = 3] = "Chemical";
7
+ DamageChannel[DamageChannel["Radiation"] = 4] = "Radiation";
8
+ DamageChannel[DamageChannel["Corrosive"] = 5] = "Corrosive";
9
+ DamageChannel[DamageChannel["Suffocation"] = 6] = "Suffocation";
10
+ DamageChannel[DamageChannel["ControlDisruption"] = 7] = "ControlDisruption";
11
+ /** Phase 11C: directed energy — plasma, laser, sonic. Armour resists via reflectivity. */
12
+ DamageChannel[DamageChannel["Energy"] = 8] = "Energy";
13
+ })(DamageChannel || (DamageChannel = {}));
14
+ export const channelMask = (...chs) => {
15
+ let m = 0;
16
+ for (const c of chs)
17
+ m |= (1 << c);
18
+ return m;
19
+ };
20
+ export const hasChannel = (mask, ch) => (mask & (1 << ch)) !== 0;
@@ -0,0 +1,124 @@
1
+ /** A single entry in a chronicle. */
2
+ export interface ChronicleEntry {
3
+ entryId: string;
4
+ tick: number;
5
+ /** Significance score 0-100 for filtering/summarizing. */
6
+ significance: number;
7
+ eventType: ChronicleEventType;
8
+ /** Entity IDs involved in the event. */
9
+ actors: number[];
10
+ /** Template key for rendering. */
11
+ template: string;
12
+ /** Variables for template substitution. */
13
+ variables: Record<string, string | number>;
14
+ /** Rendered prose (generated on demand or at creation). */
15
+ rendered?: string | undefined;
16
+ /** Settlement ID if event occurred at a settlement. */
17
+ settlementId?: string | undefined;
18
+ /** Quest ID if event relates to a quest. */
19
+ questId?: string | undefined;
20
+ }
21
+ /** Types of events that can be recorded in a chronicle. */
22
+ export type ChronicleEventType = "entity_death" | "entity_birth" | "relationship_formed" | "relationship_broken" | "relationship_betrayal" | "quest_completed" | "quest_failed" | "quest_accepted" | "settlement_founded" | "settlement_upgraded" | "settlement_raided" | "settlement_destroyed" | "facility_completed" | "masterwork_crafted" | "first_contact" | "combat_victory" | "combat_defeat" | "rank_promotion" | "legendary_deed" | "tragic_event";
23
+ /** Chronicle scope determines visibility and ownership. */
24
+ export type ChronicleScope = "world" | "faction" | "settlement" | "entity";
25
+ /** A collection of chronicle entries forming a narrative history. */
26
+ export interface Chronicle {
27
+ chronicleId: string;
28
+ scope: ChronicleScope;
29
+ /** Owner ID for entity/faction/settlement-specific chronicles. */
30
+ ownerId?: number | string | undefined;
31
+ entries: ChronicleEntry[];
32
+ /** When the chronicle was created. */
33
+ createdAtTick: number;
34
+ /** Last entry tick. */
35
+ lastEntryTick: number;
36
+ /** Detected story arcs in this chronicle. */
37
+ detectedArcs: StoryArc[];
38
+ }
39
+ /** A detected story arc (pattern across multiple events). */
40
+ export interface StoryArc {
41
+ arcId: string;
42
+ arcType: StoryArcType;
43
+ /** Entry IDs that form this arc. */
44
+ entryIds: string[];
45
+ /** Primary actors in the arc. */
46
+ primaryActors: number[];
47
+ /** When the arc began. */
48
+ startTick: number;
49
+ /** When the arc ended (undefined if ongoing). */
50
+ endTick?: number | undefined;
51
+ /** Arc significance score. */
52
+ significance: number;
53
+ /** Human-readable arc description. */
54
+ description: string;
55
+ }
56
+ export type StoryArcType = "rise_of_hero" | "tragic_fall" | "rivalry" | "great_migration" | "settlement_growth" | "fallen_settlement" | "legendary_craftsman" | "notorious_villain" | "unlikely_friendship" | "betrayal_and_redemption";
57
+ /** Base significance scores by event type. */
58
+ export declare const SIGNIFICANCE_SCORES: Record<ChronicleEventType, number>;
59
+ /** Modifiers for significance calculation. */
60
+ export interface SignificanceContext {
61
+ /** Entity was of high reputation/fame. */
62
+ actorWasFamous?: boolean;
63
+ /** Event was a first (first death in world, etc.). */
64
+ wasFirst?: boolean;
65
+ /** Multiple entities involved. */
66
+ involvedMultipleParties?: boolean;
67
+ /** Outcome was unexpected (underdog victory). */
68
+ unexpectedOutcome?: boolean;
69
+ /** Event had lasting consequences. */
70
+ lastingConsequences?: boolean;
71
+ }
72
+ /** Calculate significance score for an event. */
73
+ export declare function calculateSignificance(eventType: ChronicleEventType, context?: SignificanceContext): number;
74
+ /** Check if an event is significant enough to record. */
75
+ export declare function isSignificant(eventType: ChronicleEventType, threshold?: number, context?: SignificanceContext): boolean;
76
+ /** Create a new chronicle. */
77
+ export declare function createChronicle(chronicleId: string, scope: ChronicleScope, tick: number, ownerId?: number | string): Chronicle;
78
+ /** Add an entry to a chronicle. */
79
+ export declare function addChronicleEntry(chronicle: Chronicle, entry: Omit<ChronicleEntry, "entryId">, seed?: number): ChronicleEntry;
80
+ /** Get entries above a significance threshold. */
81
+ export declare function getSignificantEntries(chronicle: Chronicle, minSignificance?: number): ChronicleEntry[];
82
+ /** Get entries within a tick range. */
83
+ export declare function getEntriesInRange(chronicle: Chronicle, startTick: number, endTick: number): ChronicleEntry[];
84
+ /** Get entries involving a specific entity. */
85
+ export declare function getEntriesForEntity(chronicle: Chronicle, entityId: number): ChronicleEntry[];
86
+ /** Get entries of a specific type. */
87
+ export declare function getEntriesByType(chronicle: Chronicle, eventType: ChronicleEventType): ChronicleEntry[];
88
+ /** Global chronicle registry. */
89
+ export interface ChronicleRegistry {
90
+ /** World-level chronicle (all significant events). */
91
+ worldChronicle: Chronicle;
92
+ /** Per-entity chronicles. */
93
+ entityChronicles: Map<number, Chronicle>;
94
+ /** Per-faction chronicles. */
95
+ factionChronicles: Map<number, Chronicle>;
96
+ /** Per-settlement chronicles. */
97
+ settlementChronicles: Map<string, Chronicle>;
98
+ }
99
+ /** Create a new chronicle registry. */
100
+ export declare function createChronicleRegistry(tick: number): ChronicleRegistry;
101
+ /** Record an event to appropriate chronicles. */
102
+ export declare function recordEvent(registry: ChronicleRegistry, entry: Omit<ChronicleEntry, "entryId">, options?: {
103
+ entityIds?: number[];
104
+ factionIds?: number[];
105
+ settlementId?: string;
106
+ }): void;
107
+ export type SummaryLevel = "full" | "chapter" | "synopsis";
108
+ export interface ChronicleSummary {
109
+ level: SummaryLevel;
110
+ totalEntries: number;
111
+ includedEntries: number;
112
+ summaryText: string;
113
+ keyEvents: ChronicleEntry[];
114
+ }
115
+ /** Summarize a chronicle at different granularities. */
116
+ export declare function summarizeChronicle(chronicle: Chronicle, level?: SummaryLevel): ChronicleSummary;
117
+ /** Serialize chronicle to JSON-friendly format. */
118
+ export declare function serializeChronicle(chronicle: Chronicle): unknown;
119
+ /** Deserialize chronicle. */
120
+ export declare function deserializeChronicle(data: unknown): Chronicle;
121
+ /** Serialize chronicle registry. */
122
+ export declare function serializeChronicleRegistry(registry: ChronicleRegistry): unknown;
123
+ /** Deserialize chronicle registry. */
124
+ export declare function deserializeChronicleRegistry(data: unknown): ChronicleRegistry;