@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,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
|
+
}
|