@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,76 @@
1
+ import type { Quest, QuestObjective, QuestRewards } from "./quest.js";
2
+ import type { CompetenceDomain } from "./competence/catalogue.js";
3
+ /** Template for generating similar quests. */
4
+ export interface QuestTemplate {
5
+ templateId: string;
6
+ titlePattern: string;
7
+ descriptionPattern: string;
8
+ minTier: number;
9
+ maxTier: number;
10
+ baseReward: QuestRewards;
11
+ objectiveGenerators: ObjectiveGenerator[];
12
+ /** Weight for random selection. */
13
+ weight: number;
14
+ /** Required faction standing range [min, max]. */
15
+ factionRange?: [number, number];
16
+ }
17
+ /** Function that generates an objective from context. */
18
+ export type ObjectiveGenerator = (ctx: QuestContext) => QuestObjective | null;
19
+ /** Context for quest generation. */
20
+ export interface QuestContext {
21
+ giverId?: number;
22
+ targetEntityId?: number;
23
+ targetLocation?: {
24
+ x: number;
25
+ y: number;
26
+ };
27
+ settlementTier: number;
28
+ factionStanding: number;
29
+ availableCompetences: CompetenceDomain[];
30
+ seed: number;
31
+ }
32
+ /** All available quest templates. */
33
+ export declare const QUEST_TEMPLATES: QuestTemplate[];
34
+ /**
35
+ * Select a quest template based on context and random seed.
36
+ */
37
+ export declare function selectTemplate(ctx: QuestContext, templates?: QuestTemplate[]): QuestTemplate | null;
38
+ /**
39
+ * Generate a complete quest from a template and context.
40
+ */
41
+ export declare function generateQuest(ctx: QuestContext, template: QuestTemplate, questIndex?: number): Quest;
42
+ /**
43
+ * Generate multiple quests for a settlement/quest giver.
44
+ */
45
+ export declare function generateQuests(ctx: QuestContext, count: number): Quest[];
46
+ /**
47
+ * Build quest context from simulation state.
48
+ */
49
+ export interface WorldContext {
50
+ giverId?: number;
51
+ settlementTier: number;
52
+ factionId?: string;
53
+ factionStanding: number;
54
+ nearbyEntities: number[];
55
+ pointsOfInterest: {
56
+ x: number;
57
+ y: number;
58
+ type: string;
59
+ }[];
60
+ seed: number;
61
+ }
62
+ /**
63
+ * Create a QuestContext from world simulation state.
64
+ */
65
+ export declare function buildQuestContext(worldCtx: WorldContext): QuestContext;
66
+ /**
67
+ * Add optional bonus objectives to a quest.
68
+ */
69
+ export declare function addBonusObjective(quest: Quest, type: "time_limit" | "stealth" | "no_harm", bonusReward: Partial<QuestRewards>): void;
70
+ /**
71
+ * Create a chain of related quests.
72
+ */
73
+ export declare function generateQuestChain(baseCtx: QuestContext, stages: {
74
+ template: QuestTemplate;
75
+ requiresPrevious: boolean;
76
+ }[]): Quest[];
@@ -0,0 +1,534 @@
1
+ // src/quest-generators.ts — Phase 41: Procedural Quest Generation
2
+ //
3
+ // Generates quests from world state, faction standing, and entity capabilities.
4
+ import { q } from "./units.js";
5
+ // ── Template Library ──────────────────────────────────────────────────────────
6
+ const BOUNTY_TEMPLATE = {
7
+ templateId: "bounty",
8
+ titlePattern: "Bounty: {targetName}",
9
+ descriptionPattern: "Eliminate {targetName}, who has been causing trouble in the region.",
10
+ minTier: 1,
11
+ maxTier: 4,
12
+ baseReward: {
13
+ reputation: { faction: 5 },
14
+ xp: 50,
15
+ currency: 100,
16
+ },
17
+ objectiveGenerators: [
18
+ (ctx) => ({
19
+ objectiveId: "defeat_target",
20
+ description: `Defeat the target entity`,
21
+ type: "defeat_entity",
22
+ ...(ctx.targetEntityId && { target: { entityId: ctx.targetEntityId } }),
23
+ progress: 0,
24
+ state: "available",
25
+ hidden: false,
26
+ }),
27
+ ],
28
+ weight: 10,
29
+ factionRange: [-50, 100], // Most factions offer bounties
30
+ };
31
+ const DELIVERY_TEMPLATE = {
32
+ templateId: "delivery",
33
+ titlePattern: "Special Delivery",
34
+ descriptionPattern: "Deliver {itemName} to {destination}.",
35
+ minTier: 1,
36
+ maxTier: 4,
37
+ baseReward: {
38
+ reputation: { faction: 3 },
39
+ xp: 25,
40
+ currency: 50,
41
+ },
42
+ objectiveGenerators: [
43
+ (ctx) => ({
44
+ objectiveId: "collect_package",
45
+ description: "Collect the package from the quest giver",
46
+ type: "collect_item",
47
+ target: { itemId: "quest_package" },
48
+ count: 1,
49
+ progress: 0,
50
+ state: "available",
51
+ hidden: false,
52
+ }),
53
+ (ctx) => ({
54
+ objectiveId: "deliver_package",
55
+ description: "Deliver the package to the destination",
56
+ type: "deliver_item",
57
+ target: {
58
+ itemId: "quest_package",
59
+ location: ctx.targetLocation
60
+ ? { ...ctx.targetLocation, radius_m: 10 }
61
+ : { x: 0, y: 0, radius_m: 10 },
62
+ },
63
+ progress: 0,
64
+ state: "locked",
65
+ hidden: false,
66
+ requires: ["collect_package"],
67
+ }),
68
+ ],
69
+ weight: 15,
70
+ factionRange: [0, 100], // Need neutral or better standing
71
+ };
72
+ const CRAFTING_TEMPLATE = {
73
+ templateId: "crafting",
74
+ titlePattern: "Commission: {itemName}",
75
+ descriptionPattern: "Craft a {quality} {itemName} using your skills.",
76
+ minTier: 2,
77
+ maxTier: 4,
78
+ baseReward: {
79
+ reputation: { faction: 8 },
80
+ xp: 75,
81
+ currency: 200,
82
+ },
83
+ objectiveGenerators: [
84
+ (ctx) => {
85
+ const domain = ctx.availableCompetences.includes("bodilyKinesthetic")
86
+ ? "bodilyKinesthetic"
87
+ : ctx.availableCompetences[0] ?? "bodilyKinesthetic";
88
+ return {
89
+ objectiveId: "craft_item",
90
+ description: "Craft the requested item to specification",
91
+ type: "use_competence",
92
+ target: {
93
+ competence: {
94
+ domain,
95
+ minQuality_Q: q(0.70),
96
+ },
97
+ },
98
+ progress: 0,
99
+ state: "available",
100
+ hidden: false,
101
+ };
102
+ },
103
+ ],
104
+ weight: 8,
105
+ factionRange: [10, 100], // Need some trust for crafting quests
106
+ };
107
+ const INVESTIGATION_TEMPLATE = {
108
+ templateId: "investigation",
109
+ titlePattern: "Investigate {locationName}",
110
+ descriptionPattern: "Travel to {locationName} and report what you find.",
111
+ minTier: 2,
112
+ maxTier: 4,
113
+ baseReward: {
114
+ reputation: { faction: 10 },
115
+ xp: 60,
116
+ currency: 75,
117
+ },
118
+ objectiveGenerators: [
119
+ (ctx) => ({
120
+ objectiveId: "reach_site",
121
+ description: "Reach the investigation site",
122
+ type: "reach_location",
123
+ target: {
124
+ location: ctx.targetLocation
125
+ ? { ...ctx.targetLocation, radius_m: 20 }
126
+ : { x: 100, y: 100, radius_m: 20 },
127
+ },
128
+ progress: 0,
129
+ state: "available",
130
+ hidden: false,
131
+ }),
132
+ (ctx) => ({
133
+ objectiveId: "report_findings",
134
+ description: "Return and report your findings",
135
+ type: "dialogue_choice",
136
+ target: { dialogueChoice: "report_investigation" },
137
+ progress: 0,
138
+ state: "locked",
139
+ hidden: false,
140
+ requires: ["reach_site"],
141
+ }),
142
+ ],
143
+ weight: 12,
144
+ factionRange: [5, 100],
145
+ };
146
+ const ESCORT_TEMPLATE = {
147
+ templateId: "escort",
148
+ titlePattern: "Escort {npcName}",
149
+ descriptionPattern: "Safely escort {npcName} to {destination}.",
150
+ minTier: 2,
151
+ maxTier: 4,
152
+ baseReward: {
153
+ reputation: { faction: 15 },
154
+ xp: 100,
155
+ currency: 150,
156
+ },
157
+ objectiveGenerators: [
158
+ (ctx) => ({
159
+ objectiveId: "reach_destination",
160
+ description: "Escort the NPC to the destination safely",
161
+ type: "escort_entity",
162
+ target: {
163
+ ...(ctx.targetEntityId && { entityId: ctx.targetEntityId }),
164
+ location: ctx.targetLocation
165
+ ? { ...ctx.targetLocation, radius_m: 15 }
166
+ : { x: 200, y: 200, radius_m: 15 },
167
+ },
168
+ progress: 0,
169
+ state: "available",
170
+ hidden: false,
171
+ }),
172
+ ],
173
+ weight: 6,
174
+ factionRange: [20, 100], // Need good standing for escort quests
175
+ };
176
+ const COLLECTION_TEMPLATE = {
177
+ templateId: "collection",
178
+ titlePattern: "Gather {itemName}",
179
+ descriptionPattern: "Collect {count} {itemName}(s) for research/crafting.",
180
+ minTier: 1,
181
+ maxTier: 3,
182
+ baseReward: {
183
+ reputation: { faction: 3 },
184
+ xp: 40,
185
+ currency: 60,
186
+ },
187
+ objectiveGenerators: [
188
+ (ctx) => ({
189
+ objectiveId: "collect_items",
190
+ description: "Collect the requested items",
191
+ type: "collect_item",
192
+ target: { itemId: "herb_rare" },
193
+ count: 5,
194
+ progress: 0,
195
+ state: "available",
196
+ hidden: false,
197
+ }),
198
+ ],
199
+ weight: 14,
200
+ factionRange: [0, 100],
201
+ };
202
+ const WAIT_TEMPLATE = {
203
+ templateId: "wait",
204
+ titlePattern: "Stand Watch",
205
+ descriptionPattern: "Stand watch at {locationName} for {duration}.",
206
+ minTier: 1,
207
+ maxTier: 3,
208
+ baseReward: {
209
+ reputation: { faction: 2 },
210
+ xp: 20,
211
+ currency: 30,
212
+ },
213
+ objectiveGenerators: [
214
+ (ctx) => ({
215
+ objectiveId: "reach_post",
216
+ description: "Go to your assigned post",
217
+ type: "reach_location",
218
+ target: {
219
+ location: ctx.targetLocation
220
+ ? { ...ctx.targetLocation, radius_m: 10 }
221
+ : { x: 0, y: 0, radius_m: 10 },
222
+ },
223
+ progress: 0,
224
+ state: "available",
225
+ hidden: false,
226
+ }),
227
+ (ctx) => ({
228
+ objectiveId: "stand_watch",
229
+ description: "Stand watch for the required duration",
230
+ type: "wait_duration",
231
+ count: 300, // 5 minutes
232
+ progress: 0,
233
+ state: "locked",
234
+ hidden: false,
235
+ requires: ["reach_post"],
236
+ }),
237
+ ],
238
+ weight: 10,
239
+ factionRange: [0, 100],
240
+ };
241
+ const MULTISTAGE_TEMPLATE = {
242
+ templateId: "multistage",
243
+ titlePattern: "The {adjective} Contract",
244
+ descriptionPattern: "A complex mission with multiple stages.",
245
+ minTier: 3,
246
+ maxTier: 4,
247
+ baseReward: {
248
+ reputation: { faction: 25 },
249
+ xp: 200,
250
+ currency: 500,
251
+ items: [{ itemId: "rare_material", quantity: 1 }],
252
+ },
253
+ objectiveGenerators: [
254
+ (ctx) => ({
255
+ objectiveId: "stage1_recon",
256
+ description: "Scout the target location",
257
+ type: "reach_location",
258
+ target: {
259
+ location: ctx.targetLocation
260
+ ? { ...ctx.targetLocation, radius_m: 30 }
261
+ : { x: 150, y: 150, radius_m: 30 },
262
+ },
263
+ progress: 0,
264
+ state: "available",
265
+ hidden: false,
266
+ }),
267
+ (ctx) => ({
268
+ objectiveId: "stage2_prepare",
269
+ description: "Craft necessary equipment",
270
+ type: "use_competence",
271
+ target: {
272
+ competence: {
273
+ domain: ctx.availableCompetences[0] ?? "bodilyKinesthetic",
274
+ minQuality_Q: q(0.60),
275
+ },
276
+ },
277
+ progress: 0,
278
+ state: "locked",
279
+ hidden: false,
280
+ requires: ["stage1_recon"],
281
+ }),
282
+ (ctx) => ({
283
+ objectiveId: "stage3_execute",
284
+ description: "Complete the primary objective",
285
+ type: "defeat_entity",
286
+ ...(ctx.targetEntityId && { target: { entityId: ctx.targetEntityId } }),
287
+ count: 1,
288
+ progress: 0,
289
+ state: "locked",
290
+ hidden: false,
291
+ requires: ["stage2_prepare"],
292
+ }),
293
+ ],
294
+ weight: 5,
295
+ factionRange: [30, 100], // High trust required for complex quests
296
+ };
297
+ /** All available quest templates. */
298
+ export const QUEST_TEMPLATES = [
299
+ BOUNTY_TEMPLATE,
300
+ DELIVERY_TEMPLATE,
301
+ CRAFTING_TEMPLATE,
302
+ INVESTIGATION_TEMPLATE,
303
+ ESCORT_TEMPLATE,
304
+ COLLECTION_TEMPLATE,
305
+ WAIT_TEMPLATE,
306
+ MULTISTAGE_TEMPLATE,
307
+ ];
308
+ // ── Generation Functions ──────────────────────────────────────────────────────
309
+ /**
310
+ * Select a quest template based on context and random seed.
311
+ */
312
+ export function selectTemplate(ctx, templates = QUEST_TEMPLATES) {
313
+ // Filter by tier and faction standing
314
+ const eligible = templates.filter((t) => ctx.settlementTier >= t.minTier &&
315
+ ctx.settlementTier <= t.maxTier &&
316
+ (!t.factionRange ||
317
+ (ctx.factionStanding >= t.factionRange[0] &&
318
+ ctx.factionStanding <= t.factionRange[1])));
319
+ if (eligible.length === 0)
320
+ return null;
321
+ // Weighted random selection using deterministic seed
322
+ const totalWeight = eligible.reduce((sum, t) => sum + t.weight, 0);
323
+ let roll = (ctx.seed % totalWeight);
324
+ for (const template of eligible) {
325
+ roll -= template.weight;
326
+ if (roll < 0)
327
+ return template;
328
+ }
329
+ return eligible[eligible.length - 1] ?? null;
330
+ }
331
+ /**
332
+ * Generate a quest ID from template and context.
333
+ */
334
+ function generateQuestId(template, ctx, index) {
335
+ return `${template.templateId}_${ctx.giverId ?? "world"}_${index}_${ctx.seed}`;
336
+ }
337
+ /**
338
+ * Fill template strings with context values.
339
+ */
340
+ function fillTemplate(template, ctx) {
341
+ const replacements = {
342
+ "{targetName}": ctx.targetEntityId ? `Target-${ctx.targetEntityId}` : "the target",
343
+ "{itemName}": "item",
344
+ "{quality}": "fine",
345
+ "{destination}": ctx.targetLocation
346
+ ? `location (${ctx.targetLocation.x}, ${ctx.targetLocation.y})`
347
+ : "the destination",
348
+ "{locationName}": ctx.targetLocation
349
+ ? `the site at (${ctx.targetLocation.x}, ${ctx.targetLocation.y})`
350
+ : "the designated location",
351
+ "{npcName}": ctx.targetEntityId ? `NPC-${ctx.targetEntityId}` : "the client",
352
+ "{duration}": "5 minutes",
353
+ "{count}": "5",
354
+ "{adjective}": "Complex",
355
+ };
356
+ return template.replace(/\{[^}]+\}/g, (match) => replacements[match] ?? match);
357
+ }
358
+ /**
359
+ * Scale rewards based on settlement tier and faction standing.
360
+ */
361
+ function scaleRewards(base, tier, standing) {
362
+ const tierMul = 1 + (tier - 1) * 0.5; // Tier 1 = 1x, Tier 2 = 1.5x, etc.
363
+ const standingMul = 1 + standing / 200; // -100 to +100 maps to 0.5x to 1.5x
364
+ const result = {};
365
+ if (base.reputation) {
366
+ result.reputation = Object.fromEntries(Object.entries(base.reputation).map(([k, v]) => [k, Math.round(v * standingMul)]));
367
+ }
368
+ if (base.xp) {
369
+ result.xp = Math.round(base.xp * tierMul);
370
+ }
371
+ if (base.currency) {
372
+ result.currency = Math.round(base.currency * tierMul * standingMul);
373
+ }
374
+ if (base.items) {
375
+ result.items = base.items;
376
+ }
377
+ return result;
378
+ }
379
+ /**
380
+ * Generate a complete quest from a template and context.
381
+ */
382
+ export function generateQuest(ctx, template, questIndex = 0) {
383
+ // Generate objectives
384
+ const objectives = [];
385
+ for (const generator of template.objectiveGenerators) {
386
+ const obj = generator(ctx);
387
+ if (obj)
388
+ objectives.push(obj);
389
+ }
390
+ // Create quest
391
+ const quest = {
392
+ questId: generateQuestId(template, ctx, questIndex),
393
+ title: fillTemplate(template.titlePattern, ctx),
394
+ description: fillTemplate(template.descriptionPattern, ctx),
395
+ ...(ctx.giverId && { giverId: ctx.giverId }),
396
+ objectives,
397
+ state: "inactive",
398
+ priority: template.minTier * 10 + Math.floor(Math.random() * 10),
399
+ rewards: scaleRewards(template.baseReward, ctx.settlementTier, ctx.factionStanding),
400
+ };
401
+ return quest;
402
+ }
403
+ /**
404
+ * Generate multiple quests for a settlement/quest giver.
405
+ */
406
+ export function generateQuests(ctx, count) {
407
+ const quests = [];
408
+ const usedTemplates = new Set();
409
+ for (let i = 0; i < count; i++) {
410
+ // Avoid repeating templates
411
+ const availableTemplates = QUEST_TEMPLATES.filter((t) => !usedTemplates.has(t.templateId) || usedTemplates.size >= QUEST_TEMPLATES.length);
412
+ const template = selectTemplate(ctx, availableTemplates);
413
+ if (!template)
414
+ break;
415
+ usedTemplates.add(template.templateId);
416
+ const questCtx = {
417
+ ...ctx,
418
+ seed: ctx.seed + i, // Vary seed for each quest
419
+ };
420
+ quests.push(generateQuest(questCtx, template, i));
421
+ }
422
+ return quests;
423
+ }
424
+ /**
425
+ * Create a QuestContext from world simulation state.
426
+ */
427
+ export function buildQuestContext(worldCtx) {
428
+ // Select random target from available entities/locations
429
+ const targetEntityId = worldCtx.nearbyEntities.length > 0
430
+ ? worldCtx.nearbyEntities[worldCtx.seed % worldCtx.nearbyEntities.length]
431
+ : undefined;
432
+ const targetLocation = worldCtx.pointsOfInterest.length > 0
433
+ ? worldCtx.pointsOfInterest[worldCtx.seed % worldCtx.pointsOfInterest.length]
434
+ : undefined;
435
+ const ctx = {
436
+ settlementTier: worldCtx.settlementTier,
437
+ factionStanding: worldCtx.factionStanding,
438
+ availableCompetences: [
439
+ "bodilyKinesthetic",
440
+ "spatial",
441
+ "naturalist",
442
+ "linguistic",
443
+ "logicalMathematical",
444
+ ],
445
+ seed: worldCtx.seed,
446
+ };
447
+ if (worldCtx.giverId !== undefined)
448
+ ctx.giverId = worldCtx.giverId;
449
+ if (targetEntityId !== undefined)
450
+ ctx.targetEntityId = targetEntityId;
451
+ if (targetLocation !== undefined)
452
+ ctx.targetLocation = targetLocation;
453
+ return ctx;
454
+ }
455
+ // ── Quest Variations ───────────────────────────────────────────────────────────
456
+ /**
457
+ * Add optional bonus objectives to a quest.
458
+ */
459
+ export function addBonusObjective(quest, type, bonusReward) {
460
+ let objective;
461
+ switch (type) {
462
+ case "time_limit":
463
+ objective = {
464
+ objectiveId: "bonus_speed",
465
+ description: "Complete within time limit for bonus",
466
+ type: "wait_duration",
467
+ count: 3600, // 1 hour
468
+ progress: 0,
469
+ state: "available",
470
+ hidden: true,
471
+ };
472
+ break;
473
+ case "stealth":
474
+ objective = {
475
+ objectiveId: "bonus_stealth",
476
+ description: "Complete without being detected",
477
+ type: "dialogue_choice",
478
+ target: { dialogueChoice: "stealth_success" },
479
+ progress: 0,
480
+ state: "available",
481
+ hidden: true,
482
+ };
483
+ break;
484
+ case "no_harm":
485
+ objective = {
486
+ objectiveId: "bonus_pacifist",
487
+ description: "Complete without harming non-targets",
488
+ type: "dialogue_choice",
489
+ target: { dialogueChoice: "pacifist_success" },
490
+ progress: 0,
491
+ state: "available",
492
+ hidden: true,
493
+ };
494
+ break;
495
+ default:
496
+ return;
497
+ }
498
+ quest.objectives.push(objective);
499
+ // Merge bonus rewards
500
+ if (bonusReward.xp) {
501
+ quest.rewards ??= {};
502
+ quest.rewards.xp = (quest.rewards.xp ?? 0) + bonusReward.xp;
503
+ }
504
+ if (bonusReward.currency) {
505
+ quest.rewards ??= {};
506
+ quest.rewards.currency = (quest.rewards.currency ?? 0) + bonusReward.currency;
507
+ }
508
+ }
509
+ /**
510
+ * Create a chain of related quests.
511
+ */
512
+ export function generateQuestChain(baseCtx, stages) {
513
+ const chain = [];
514
+ let prevQuestId;
515
+ for (let i = 0; i < stages.length; i++) {
516
+ const stage = stages[i];
517
+ if (!stage)
518
+ continue;
519
+ const { template, requiresPrevious } = stage;
520
+ const ctx = {
521
+ ...baseCtx,
522
+ seed: baseCtx.seed + i * 1000,
523
+ };
524
+ const quest = generateQuest(ctx, template, i);
525
+ // Link to previous quest if required
526
+ if (requiresPrevious && prevQuestId) {
527
+ quest.description += ` (Requires completion of previous mission)`;
528
+ // The quest giver would only offer this if previous is complete
529
+ }
530
+ chain.push(quest);
531
+ prevQuestId = quest.questId;
532
+ }
533
+ return chain;
534
+ }