@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,694 @@
|
|
|
1
|
+
// src/competence/framework.ts — Phase 40: Non-Combat Competence Framework
|
|
2
|
+
//
|
|
3
|
+
// Unified competence resolution system that:
|
|
4
|
+
// 1. Routes to correct domain-specific resolver
|
|
5
|
+
// 2. Integrates with Phase 21 progression (XP gain)
|
|
6
|
+
// 3. Provides canonical CompetenceAction/CompetenceOutcome types
|
|
7
|
+
//
|
|
8
|
+
// No kernel import — pure resolution module.
|
|
9
|
+
import { SCALE, q, clampQ, mulDiv } from "../units.js";
|
|
10
|
+
import { makeRng } from "../rng.js";
|
|
11
|
+
import { getTaskById } from "./catalogue.js";
|
|
12
|
+
// Import domain-specific resolvers
|
|
13
|
+
import { resolveCrafting } from "./crafting.js";
|
|
14
|
+
import { resolveNavigation } from "./navigation.js";
|
|
15
|
+
import { resolveTracking, resolveForaging, resolveTaming, } from "./naturalist.js";
|
|
16
|
+
import { resolveSignal } from "./interspecies.js";
|
|
17
|
+
import { resolveTeaching } from "./teaching.js";
|
|
18
|
+
import { resolveEngineering } from "./engineering.js";
|
|
19
|
+
import { resolveFormationSignal } from "./acoustic.js";
|
|
20
|
+
import { resolvePerformance } from "./performance.js";
|
|
21
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
22
|
+
/** Base XP for successful task completion. */
|
|
23
|
+
const BASE_XP_SUCCESS = 10;
|
|
24
|
+
/** XP multiplier for outcome quality. */
|
|
25
|
+
const QUALITY_XP_MULTIPLIERS = {
|
|
26
|
+
exceptional: 2.0,
|
|
27
|
+
good: 1.5,
|
|
28
|
+
adequate: 1.0,
|
|
29
|
+
poor: 0.5,
|
|
30
|
+
failure: 0,
|
|
31
|
+
};
|
|
32
|
+
/** Difficulty XP bonus: harder tasks grant more XP. */
|
|
33
|
+
const MAX_DIFFICULTY_BONUS = 15;
|
|
34
|
+
/** Tool quality bonus lookup (default if tool not specified). */
|
|
35
|
+
const DEFAULT_TOOL_BONUS = q(1.0);
|
|
36
|
+
// ── Helper Functions ──────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Map domain-specific descriptors to canonical descriptors.
|
|
39
|
+
*/
|
|
40
|
+
function canonicalizeDescriptor(descriptor) {
|
|
41
|
+
switch (descriptor) {
|
|
42
|
+
case "exceptional":
|
|
43
|
+
case "masterwork":
|
|
44
|
+
return "exceptional";
|
|
45
|
+
case "good":
|
|
46
|
+
case "fine":
|
|
47
|
+
return "good";
|
|
48
|
+
case "adequate":
|
|
49
|
+
return "adequate";
|
|
50
|
+
case "poor":
|
|
51
|
+
return "poor";
|
|
52
|
+
case "failure":
|
|
53
|
+
case "ruined":
|
|
54
|
+
return "failure";
|
|
55
|
+
default:
|
|
56
|
+
return "adequate";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculate XP gain based on outcome.
|
|
61
|
+
*/
|
|
62
|
+
function calculateXP(descriptor, difficulty_Q) {
|
|
63
|
+
const qualityMul = QUALITY_XP_MULTIPLIERS[descriptor];
|
|
64
|
+
// No XP for failure, no difficulty bonus on failure
|
|
65
|
+
if (descriptor === "failure") {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
const difficultyBonus = Math.round((difficulty_Q / SCALE.Q) * MAX_DIFFICULTY_BONUS);
|
|
69
|
+
return Math.round(BASE_XP_SUCCESS * qualityMul) + difficultyBonus;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate narrative line for outcome.
|
|
73
|
+
*/
|
|
74
|
+
function generateNarrative(task, outcome, actor) {
|
|
75
|
+
const actorName = `Entity ${actor.id}`;
|
|
76
|
+
const taskName = task.description.toLowerCase();
|
|
77
|
+
switch (outcome.descriptor) {
|
|
78
|
+
case "exceptional":
|
|
79
|
+
return `${actorName} achieved an exceptional result ${taskName}, surpassing all expectations.`;
|
|
80
|
+
case "good":
|
|
81
|
+
return `${actorName} successfully completed ${taskName} with good quality.`;
|
|
82
|
+
case "adequate":
|
|
83
|
+
return `${actorName} managed ${taskName} adequately, meeting basic requirements.`;
|
|
84
|
+
case "poor":
|
|
85
|
+
return `${actorName} struggled with ${taskName}, producing a poor result.`;
|
|
86
|
+
case "failure":
|
|
87
|
+
return `${actorName} failed to complete ${taskName}.`;
|
|
88
|
+
default:
|
|
89
|
+
return `${actorName} attempted ${taskName}.`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the relevant cognitive attribute for a domain.
|
|
94
|
+
*/
|
|
95
|
+
function getDomainIntelligence(entity, domain) {
|
|
96
|
+
const cognition = entity.attributes.cognition;
|
|
97
|
+
if (!cognition)
|
|
98
|
+
return q(0.50);
|
|
99
|
+
switch (domain) {
|
|
100
|
+
case "linguistic":
|
|
101
|
+
return (cognition.linguistic ?? q(0.50));
|
|
102
|
+
case "logicalMathematical":
|
|
103
|
+
return (cognition.logicalMathematical ?? q(0.50));
|
|
104
|
+
case "spatial":
|
|
105
|
+
return (cognition.spatial ?? q(0.50));
|
|
106
|
+
case "bodilyKinesthetic":
|
|
107
|
+
return (cognition.bodilyKinesthetic ?? q(0.50));
|
|
108
|
+
case "musical":
|
|
109
|
+
return (cognition.musical ?? q(0.50));
|
|
110
|
+
case "interpersonal":
|
|
111
|
+
return (cognition.interpersonal ?? q(0.50));
|
|
112
|
+
case "intrapersonal":
|
|
113
|
+
return (cognition.intrapersonal ?? q(0.50));
|
|
114
|
+
case "naturalist":
|
|
115
|
+
return (cognition.naturalist ?? q(0.50));
|
|
116
|
+
case "interSpecies":
|
|
117
|
+
return (cognition.interSpecies ?? q(0.35));
|
|
118
|
+
default:
|
|
119
|
+
return q(0.50);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ── Domain Routers ────────────────────────────────────────────────────────────
|
|
123
|
+
/**
|
|
124
|
+
* Resolve bodily-kinesthetic tasks (crafting, physical skill).
|
|
125
|
+
*/
|
|
126
|
+
function resolveBodilyKinesthetic(actor, task, action) {
|
|
127
|
+
const seed = action.seed;
|
|
128
|
+
// Map task to crafting spec
|
|
129
|
+
const toolCategory = task.taskId.includes("sword")
|
|
130
|
+
? "forge"
|
|
131
|
+
: task.taskId.includes("surgery")
|
|
132
|
+
? "precision"
|
|
133
|
+
: undefined;
|
|
134
|
+
const spec = {
|
|
135
|
+
outputId: task.taskId,
|
|
136
|
+
materialQ: q(0.50),
|
|
137
|
+
baseTime_s: task.timeBase_s,
|
|
138
|
+
minBKQ: q(0.20),
|
|
139
|
+
...(toolCategory !== undefined && { toolCategory }),
|
|
140
|
+
};
|
|
141
|
+
const result = resolveCrafting(actor, spec, seed);
|
|
142
|
+
return {
|
|
143
|
+
quality_Q: result.quality_Q,
|
|
144
|
+
timeTaken_s: result.timeTaken_s,
|
|
145
|
+
success: result.descriptor !== "ruined",
|
|
146
|
+
descriptor: canonicalizeDescriptor(result.descriptor),
|
|
147
|
+
details: {
|
|
148
|
+
qualityMul: result.quality_Q,
|
|
149
|
+
latentFlaw: result.descriptor === "poor",
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolve spatial tasks (navigation, wayfinding).
|
|
155
|
+
*/
|
|
156
|
+
function resolveSpatial(actor, task, action) {
|
|
157
|
+
const terrain = action.terrain ?? "forest";
|
|
158
|
+
const hasMap = action.toolId === "map";
|
|
159
|
+
const spec = {
|
|
160
|
+
terrain: terrain,
|
|
161
|
+
visibility: "clear",
|
|
162
|
+
distance_m: 1000,
|
|
163
|
+
hasMap,
|
|
164
|
+
};
|
|
165
|
+
const result = resolveNavigation(actor, spec, action.seed);
|
|
166
|
+
// Determine descriptor based on efficiency
|
|
167
|
+
let descriptor;
|
|
168
|
+
if (result.routeEfficiency >= q(0.90))
|
|
169
|
+
descriptor = "exceptional";
|
|
170
|
+
else if (result.routeEfficiency >= q(0.75))
|
|
171
|
+
descriptor = "good";
|
|
172
|
+
else if (result.routeEfficiency >= q(0.50))
|
|
173
|
+
descriptor = "adequate";
|
|
174
|
+
else if (result.routeEfficiency >= q(0.30))
|
|
175
|
+
descriptor = "poor";
|
|
176
|
+
else
|
|
177
|
+
descriptor = "failure";
|
|
178
|
+
return {
|
|
179
|
+
quality_Q: result.routeEfficiency,
|
|
180
|
+
timeTaken_s: task.timeBase_s + result.timeLost_s,
|
|
181
|
+
success: result.routeEfficiency >= q(0.30),
|
|
182
|
+
descriptor,
|
|
183
|
+
details: { efficiency_Q: result.routeEfficiency },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Resolve naturalist tasks (tracking, foraging, taming).
|
|
188
|
+
*/
|
|
189
|
+
function resolveNaturalist(actor, task, action) {
|
|
190
|
+
const seed = action.seed;
|
|
191
|
+
if (task.taskId.includes("track")) {
|
|
192
|
+
const spec = {
|
|
193
|
+
trackAge_s: task.taskId.includes("fresh") ? 1800 : 86400,
|
|
194
|
+
terrain: "forest",
|
|
195
|
+
quarrySpecies: "human",
|
|
196
|
+
};
|
|
197
|
+
const result = resolveTracking(actor, spec, seed);
|
|
198
|
+
let descriptor;
|
|
199
|
+
if (result.confidence_Q >= q(0.80))
|
|
200
|
+
descriptor = "exceptional";
|
|
201
|
+
else if (result.confidence_Q >= q(0.60))
|
|
202
|
+
descriptor = "good";
|
|
203
|
+
else if (result.confidence_Q >= q(0.40))
|
|
204
|
+
descriptor = "adequate";
|
|
205
|
+
else if (result.confidence_Q >= q(0.20))
|
|
206
|
+
descriptor = "poor";
|
|
207
|
+
else
|
|
208
|
+
descriptor = "failure";
|
|
209
|
+
return {
|
|
210
|
+
quality_Q: result.confidence_Q,
|
|
211
|
+
timeTaken_s: task.timeBase_s,
|
|
212
|
+
success: result.confidence_Q >= q(0.40),
|
|
213
|
+
descriptor,
|
|
214
|
+
details: { confidence_Q: result.confidence_Q, trackRange_m: result.trackRange_m },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (task.taskId.includes("forage") || task.taskId.includes("identify")) {
|
|
218
|
+
const spec = {
|
|
219
|
+
searchHours: task.timeBase_s / 3600,
|
|
220
|
+
biome: "forest",
|
|
221
|
+
season: "summer",
|
|
222
|
+
};
|
|
223
|
+
const result = resolveForaging(actor, spec, seed);
|
|
224
|
+
let descriptor;
|
|
225
|
+
if (result.misidentified)
|
|
226
|
+
descriptor = "failure";
|
|
227
|
+
else if (result.herbQuality_Q >= q(0.80))
|
|
228
|
+
descriptor = "exceptional";
|
|
229
|
+
else if (result.herbQuality_Q >= q(0.60))
|
|
230
|
+
descriptor = "good";
|
|
231
|
+
else if (result.itemsFound >= 2)
|
|
232
|
+
descriptor = "adequate";
|
|
233
|
+
else
|
|
234
|
+
descriptor = "poor";
|
|
235
|
+
return {
|
|
236
|
+
quality_Q: result.herbQuality_Q,
|
|
237
|
+
timeTaken_s: task.timeBase_s,
|
|
238
|
+
success: !result.misidentified && result.itemsFound > 0,
|
|
239
|
+
descriptor,
|
|
240
|
+
details: { itemsFound: result.itemsFound, herbQuality_Q: result.herbQuality_Q },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (task.taskId.includes("tame")) {
|
|
244
|
+
const spec = {
|
|
245
|
+
animalSpecies: "horse",
|
|
246
|
+
animalFearQ: q(0.40),
|
|
247
|
+
effortFactor: q(1.0),
|
|
248
|
+
priorSuccesses: 0,
|
|
249
|
+
};
|
|
250
|
+
const result = resolveTaming(actor, spec, seed);
|
|
251
|
+
let descriptor;
|
|
252
|
+
if (result.attacked)
|
|
253
|
+
descriptor = "failure";
|
|
254
|
+
else if (result.trust_Q >= q(0.90))
|
|
255
|
+
descriptor = "exceptional";
|
|
256
|
+
else if (result.trust_Q >= q(0.60))
|
|
257
|
+
descriptor = "good";
|
|
258
|
+
else if (result.trust_Q >= q(0.30))
|
|
259
|
+
descriptor = "adequate";
|
|
260
|
+
else
|
|
261
|
+
descriptor = "poor";
|
|
262
|
+
return {
|
|
263
|
+
quality_Q: result.trust_Q,
|
|
264
|
+
timeTaken_s: task.timeBase_s,
|
|
265
|
+
success: !result.attacked && result.trust_Q >= q(0.30),
|
|
266
|
+
descriptor,
|
|
267
|
+
details: { trust_Q: result.trust_Q, attacked: result.attacked },
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
// Default fallback
|
|
271
|
+
return {
|
|
272
|
+
quality_Q: q(0.50),
|
|
273
|
+
timeTaken_s: task.timeBase_s,
|
|
274
|
+
success: true,
|
|
275
|
+
descriptor: "adequate",
|
|
276
|
+
details: {},
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Resolve inter-species tasks.
|
|
281
|
+
*/
|
|
282
|
+
function resolveInterSpecies(actor, task, action, world) {
|
|
283
|
+
const seed = action.seed;
|
|
284
|
+
const spec = {
|
|
285
|
+
targetSpecies: "unknown",
|
|
286
|
+
intent: task.taskId.includes("calm") ? "calm" : "ally",
|
|
287
|
+
targetFearQ: q(0.50),
|
|
288
|
+
};
|
|
289
|
+
const result = resolveSignal(actor, spec, seed);
|
|
290
|
+
let descriptor;
|
|
291
|
+
if (result.aggravated)
|
|
292
|
+
descriptor = "failure";
|
|
293
|
+
else if (result.comprehension_Q >= q(0.80))
|
|
294
|
+
descriptor = "exceptional";
|
|
295
|
+
else if (result.comprehension_Q >= q(0.60))
|
|
296
|
+
descriptor = "good";
|
|
297
|
+
else if (result.comprehension_Q >= q(0.40))
|
|
298
|
+
descriptor = "adequate";
|
|
299
|
+
else
|
|
300
|
+
descriptor = "poor";
|
|
301
|
+
return {
|
|
302
|
+
quality_Q: result.comprehension_Q,
|
|
303
|
+
timeTaken_s: task.timeBase_s,
|
|
304
|
+
success: result.comprehension_Q >= q(0.40) && !result.aggravated,
|
|
305
|
+
descriptor,
|
|
306
|
+
details: { comprehension_Q: result.comprehension_Q, aggravated: result.aggravated },
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Resolve linguistic tasks.
|
|
311
|
+
*/
|
|
312
|
+
function resolveLinguistic(actor, task, action, world) {
|
|
313
|
+
if (task.taskId.includes("command")) {
|
|
314
|
+
// Use formation signal for command tasks
|
|
315
|
+
const formationSignal = task.taskId.includes("formation")
|
|
316
|
+
? "advance"
|
|
317
|
+
: "hold";
|
|
318
|
+
// Need a listener - use self as placeholder or find in world
|
|
319
|
+
const listener = action.targetEntityId
|
|
320
|
+
? world.entities.find((e) => e.id === action.targetEntityId) ?? actor
|
|
321
|
+
: actor;
|
|
322
|
+
const result = resolveFormationSignal(actor, formationSignal, listener, 10);
|
|
323
|
+
let descriptor;
|
|
324
|
+
if (result.received && result.clarity_Q >= q(0.80))
|
|
325
|
+
descriptor = "exceptional";
|
|
326
|
+
else if (result.received && result.clarity_Q >= q(0.60))
|
|
327
|
+
descriptor = "good";
|
|
328
|
+
else if (result.received)
|
|
329
|
+
descriptor = "adequate";
|
|
330
|
+
else if (result.clarity_Q >= q(0.30))
|
|
331
|
+
descriptor = "poor";
|
|
332
|
+
else
|
|
333
|
+
descriptor = "failure";
|
|
334
|
+
return {
|
|
335
|
+
quality_Q: result.clarity_Q,
|
|
336
|
+
timeTaken_s: result.latency_ms,
|
|
337
|
+
success: result.received,
|
|
338
|
+
descriptor,
|
|
339
|
+
details: { clarity_Q: result.clarity_Q, latency_ms: result.latency_ms },
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
if (task.taskId.includes("negotiate")) {
|
|
343
|
+
// Negotiation uses interpersonal + linguistic
|
|
344
|
+
const intPersonal = getDomainIntelligence(actor, "interpersonal");
|
|
345
|
+
const intLinguistic = getDomainIntelligence(actor, "linguistic");
|
|
346
|
+
const combined = mulDiv(intPersonal, intLinguistic, SCALE.Q);
|
|
347
|
+
// Apply difficulty
|
|
348
|
+
const difficultyPenalty = mulDiv(task.difficulty_Q, q(0.30), SCALE.Q);
|
|
349
|
+
const effectiveSkill = clampQ((combined - difficultyPenalty), q(0), SCALE.Q);
|
|
350
|
+
const rng = makeRng(action.seed, SCALE.Q);
|
|
351
|
+
const roll = rng.q01();
|
|
352
|
+
const success = roll < effectiveSkill;
|
|
353
|
+
let descriptor;
|
|
354
|
+
if (effectiveSkill >= q(0.85) && success)
|
|
355
|
+
descriptor = "exceptional";
|
|
356
|
+
else if (effectiveSkill >= q(0.65) && success)
|
|
357
|
+
descriptor = "good";
|
|
358
|
+
else if (success)
|
|
359
|
+
descriptor = "adequate";
|
|
360
|
+
else if (effectiveSkill >= q(0.40))
|
|
361
|
+
descriptor = "poor";
|
|
362
|
+
else
|
|
363
|
+
descriptor = "failure";
|
|
364
|
+
return {
|
|
365
|
+
quality_Q: effectiveSkill,
|
|
366
|
+
timeTaken_s: task.timeBase_s,
|
|
367
|
+
success,
|
|
368
|
+
descriptor,
|
|
369
|
+
details: { effectiveSkill_Q: effectiveSkill },
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
// Default translation/command task - simplified
|
|
373
|
+
const linguistic = getDomainIntelligence(actor, "linguistic");
|
|
374
|
+
let descriptor;
|
|
375
|
+
if (linguistic >= q(0.80))
|
|
376
|
+
descriptor = "exceptional";
|
|
377
|
+
else if (linguistic >= q(0.65))
|
|
378
|
+
descriptor = "good";
|
|
379
|
+
else if (linguistic >= q(0.50))
|
|
380
|
+
descriptor = "adequate";
|
|
381
|
+
else if (linguistic >= q(0.30))
|
|
382
|
+
descriptor = "poor";
|
|
383
|
+
else
|
|
384
|
+
descriptor = "failure";
|
|
385
|
+
return {
|
|
386
|
+
quality_Q: linguistic,
|
|
387
|
+
timeTaken_s: task.timeBase_s,
|
|
388
|
+
success: linguistic >= q(0.50),
|
|
389
|
+
descriptor,
|
|
390
|
+
details: { linguistic_Q: linguistic },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Resolve interpersonal tasks.
|
|
395
|
+
*/
|
|
396
|
+
function resolveInterpersonal(actor, task, action, world) {
|
|
397
|
+
if (task.taskId.includes("teach")) {
|
|
398
|
+
// Find learner
|
|
399
|
+
const learner = action.targetEntityId
|
|
400
|
+
? world.entities.find((e) => e.id === action.targetEntityId)
|
|
401
|
+
: undefined;
|
|
402
|
+
if (!learner) {
|
|
403
|
+
return {
|
|
404
|
+
quality_Q: q(0),
|
|
405
|
+
timeTaken_s: task.timeBase_s,
|
|
406
|
+
success: false,
|
|
407
|
+
descriptor: "failure",
|
|
408
|
+
details: { error: "No learner found" },
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const spec = {
|
|
412
|
+
domain: "meleeCombat",
|
|
413
|
+
hours: task.timeBase_s / 3600,
|
|
414
|
+
};
|
|
415
|
+
const result = resolveTeaching(actor, learner, spec);
|
|
416
|
+
let descriptor;
|
|
417
|
+
if (result.xpGained >= 30)
|
|
418
|
+
descriptor = "exceptional";
|
|
419
|
+
else if (result.xpGained >= 20)
|
|
420
|
+
descriptor = "good";
|
|
421
|
+
else if (result.xpGained >= 10)
|
|
422
|
+
descriptor = "adequate";
|
|
423
|
+
else if (result.xpGained > 0)
|
|
424
|
+
descriptor = "poor";
|
|
425
|
+
else
|
|
426
|
+
descriptor = "failure";
|
|
427
|
+
return {
|
|
428
|
+
quality_Q: result.teachingQuality_Q,
|
|
429
|
+
timeTaken_s: task.timeBase_s,
|
|
430
|
+
success: result.xpGained > 0,
|
|
431
|
+
descriptor,
|
|
432
|
+
details: { xpGained: result.xpGained, teacherFatigueJ: result.teacherFatigueJ },
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (task.taskId.includes("rally")) {
|
|
436
|
+
// Rally uses interpersonal + willpower check
|
|
437
|
+
const interpersonal = getDomainIntelligence(actor, "interpersonal");
|
|
438
|
+
const rng = makeRng(action.seed, SCALE.Q);
|
|
439
|
+
const roll = rng.q01();
|
|
440
|
+
const success = roll < interpersonal;
|
|
441
|
+
let descriptor;
|
|
442
|
+
if (interpersonal >= q(0.80) && success)
|
|
443
|
+
descriptor = "exceptional";
|
|
444
|
+
else if (interpersonal >= q(0.60) && success)
|
|
445
|
+
descriptor = "good";
|
|
446
|
+
else if (success)
|
|
447
|
+
descriptor = "adequate";
|
|
448
|
+
else if (interpersonal >= q(0.40))
|
|
449
|
+
descriptor = "poor";
|
|
450
|
+
else
|
|
451
|
+
descriptor = "failure";
|
|
452
|
+
return {
|
|
453
|
+
quality_Q: interpersonal,
|
|
454
|
+
timeTaken_s: task.timeBase_s,
|
|
455
|
+
success,
|
|
456
|
+
descriptor,
|
|
457
|
+
details: { fearReduction_Q: mulDiv(interpersonal, q(0.30), SCALE.Q) },
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
// Detect deception - simplified
|
|
461
|
+
const interpersonal = getDomainIntelligence(actor, "interpersonal");
|
|
462
|
+
return {
|
|
463
|
+
quality_Q: interpersonal,
|
|
464
|
+
timeTaken_s: task.timeBase_s,
|
|
465
|
+
success: interpersonal >= q(0.50),
|
|
466
|
+
descriptor: interpersonal >= q(0.70) ? "good" : interpersonal >= q(0.40) ? "adequate" : "poor",
|
|
467
|
+
details: { interpersonal_Q: interpersonal },
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Resolve logical-mathematical tasks.
|
|
472
|
+
*/
|
|
473
|
+
function resolveLogicalMathematical(actor, task, action) {
|
|
474
|
+
const category = task.taskId.includes("fortification")
|
|
475
|
+
? "fortification"
|
|
476
|
+
: task.taskId.includes("siege")
|
|
477
|
+
? "mechanism"
|
|
478
|
+
: "weapon";
|
|
479
|
+
const spec = {
|
|
480
|
+
category: category,
|
|
481
|
+
complexity_Q: task.difficulty_Q,
|
|
482
|
+
timeBudget_h: task.timeBase_s / 3600,
|
|
483
|
+
};
|
|
484
|
+
const result = resolveEngineering(actor, spec, action.seed);
|
|
485
|
+
return {
|
|
486
|
+
quality_Q: result.qualityMul,
|
|
487
|
+
timeTaken_s: result.timeTaken_h * 3600,
|
|
488
|
+
success: result.descriptor !== "failure",
|
|
489
|
+
descriptor: result.descriptor,
|
|
490
|
+
details: { qualityMul: result.qualityMul, latentFlaw: result.latentFlaw },
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Resolve musical tasks.
|
|
495
|
+
*/
|
|
496
|
+
function resolveMusical(actor, task, action) {
|
|
497
|
+
if (task.taskId.includes("compose")) {
|
|
498
|
+
// Composition is simpler - just check musical skill vs difficulty
|
|
499
|
+
const musical = getDomainIntelligence(actor, "musical");
|
|
500
|
+
const rng = makeRng(action.seed, SCALE.Q);
|
|
501
|
+
const roll = rng.q01();
|
|
502
|
+
const difficultyPenalty = mulDiv(task.difficulty_Q, q(0.30), SCALE.Q);
|
|
503
|
+
const effectiveSkill = clampQ((musical - difficultyPenalty), q(0), SCALE.Q);
|
|
504
|
+
const success = roll < effectiveSkill;
|
|
505
|
+
let descriptor;
|
|
506
|
+
if (effectiveSkill >= q(0.85) && success)
|
|
507
|
+
descriptor = "exceptional";
|
|
508
|
+
else if (effectiveSkill >= q(0.65) && success)
|
|
509
|
+
descriptor = "good";
|
|
510
|
+
else if (success)
|
|
511
|
+
descriptor = "adequate";
|
|
512
|
+
else if (effectiveSkill >= q(0.40))
|
|
513
|
+
descriptor = "poor";
|
|
514
|
+
else
|
|
515
|
+
descriptor = "failure";
|
|
516
|
+
return {
|
|
517
|
+
quality_Q: effectiveSkill,
|
|
518
|
+
timeTaken_s: task.timeBase_s,
|
|
519
|
+
success,
|
|
520
|
+
descriptor,
|
|
521
|
+
details: { compositionQuality_Q: effectiveSkill },
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
if (task.taskId.includes("signal")) {
|
|
525
|
+
// Formation signaling - simplified without needing a listener
|
|
526
|
+
const musical = getDomainIntelligence(actor, "musical");
|
|
527
|
+
let descriptor;
|
|
528
|
+
if (musical >= q(0.80))
|
|
529
|
+
descriptor = "exceptional";
|
|
530
|
+
else if (musical >= q(0.60))
|
|
531
|
+
descriptor = "good";
|
|
532
|
+
else if (musical >= q(0.40))
|
|
533
|
+
descriptor = "adequate";
|
|
534
|
+
else if (musical >= q(0.25))
|
|
535
|
+
descriptor = "poor";
|
|
536
|
+
else
|
|
537
|
+
descriptor = "failure";
|
|
538
|
+
return {
|
|
539
|
+
quality_Q: musical,
|
|
540
|
+
timeTaken_s: task.timeBase_s,
|
|
541
|
+
success: musical >= q(0.40),
|
|
542
|
+
descriptor,
|
|
543
|
+
details: { signalClarity_Q: musical },
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
// Performance
|
|
547
|
+
const spec = {
|
|
548
|
+
performanceType: "rally",
|
|
549
|
+
duration_s: task.timeBase_s,
|
|
550
|
+
audienceCount: 5,
|
|
551
|
+
range_m: 50,
|
|
552
|
+
};
|
|
553
|
+
const result = resolvePerformance(actor, spec);
|
|
554
|
+
return {
|
|
555
|
+
quality_Q: mulDiv(result.fearDecayBonus_Q, SCALE.Q, q(0.020)),
|
|
556
|
+
timeTaken_s: task.timeBase_s,
|
|
557
|
+
success: result.descriptor !== "poor",
|
|
558
|
+
descriptor: result.descriptor,
|
|
559
|
+
details: { fearDecayBonus_Q: result.fearDecayBonus_Q, willpowerDrained: result.willpowerDrained_J },
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Resolve intrapersonal tasks.
|
|
564
|
+
*/
|
|
565
|
+
function resolveIntrapersonal(actor, task, action) {
|
|
566
|
+
const intrapersonal = getDomainIntelligence(actor, "intrapersonal");
|
|
567
|
+
if (task.taskId.includes("meditate")) {
|
|
568
|
+
// Meditation restores willpower
|
|
569
|
+
const willpowerRestored = Math.round((intrapersonal / SCALE.Q) * 5000 * (task.timeBase_s / 1800));
|
|
570
|
+
let descriptor;
|
|
571
|
+
if (intrapersonal >= q(0.80))
|
|
572
|
+
descriptor = "exceptional";
|
|
573
|
+
else if (intrapersonal >= q(0.60))
|
|
574
|
+
descriptor = "good";
|
|
575
|
+
else if (intrapersonal >= q(0.40))
|
|
576
|
+
descriptor = "adequate";
|
|
577
|
+
else
|
|
578
|
+
descriptor = "poor";
|
|
579
|
+
return {
|
|
580
|
+
quality_Q: intrapersonal,
|
|
581
|
+
timeTaken_s: task.timeBase_s,
|
|
582
|
+
success: true,
|
|
583
|
+
descriptor,
|
|
584
|
+
details: { willpowerRestored },
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
// Resist temptation
|
|
588
|
+
const rng = makeRng(action.seed, SCALE.Q);
|
|
589
|
+
const roll = rng.q01();
|
|
590
|
+
const difficultyPenalty = mulDiv(task.difficulty_Q, q(0.30), SCALE.Q);
|
|
591
|
+
const effectiveWillpower = clampQ((intrapersonal - difficultyPenalty), q(0), SCALE.Q);
|
|
592
|
+
const success = roll < effectiveWillpower;
|
|
593
|
+
let descriptor;
|
|
594
|
+
if (success && effectiveWillpower >= q(0.70))
|
|
595
|
+
descriptor = "exceptional";
|
|
596
|
+
else if (success)
|
|
597
|
+
descriptor = "good";
|
|
598
|
+
else if (effectiveWillpower >= q(0.40))
|
|
599
|
+
descriptor = "adequate";
|
|
600
|
+
else
|
|
601
|
+
descriptor = "failure";
|
|
602
|
+
return {
|
|
603
|
+
quality_Q: effectiveWillpower,
|
|
604
|
+
timeTaken_s: task.timeBase_s,
|
|
605
|
+
success,
|
|
606
|
+
descriptor,
|
|
607
|
+
details: { resistanceStrength_Q: effectiveWillpower },
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
// ── Main Router ───────────────────────────────────────────────────────────────
|
|
611
|
+
/**
|
|
612
|
+
* Resolve a competence action.
|
|
613
|
+
*
|
|
614
|
+
* This is the main entry point for the Phase 40 competence framework.
|
|
615
|
+
* It routes to the appropriate domain-specific resolver and computes
|
|
616
|
+
* XP gain for Phase 21 integration.
|
|
617
|
+
*
|
|
618
|
+
* @param actor - The entity performing the action.
|
|
619
|
+
* @param action - The competence action specification.
|
|
620
|
+
* @param world - The world state (for finding targets, etc.).
|
|
621
|
+
* @returns Competence outcome with quality, XP, and narrative.
|
|
622
|
+
*/
|
|
623
|
+
export function resolveCompetence(actor, action, world) {
|
|
624
|
+
// Look up task
|
|
625
|
+
const task = getTaskById(action.taskId);
|
|
626
|
+
if (!task) {
|
|
627
|
+
return {
|
|
628
|
+
domain: action.domain,
|
|
629
|
+
quality_Q: q(0),
|
|
630
|
+
timeTaken_s: 0,
|
|
631
|
+
success: false,
|
|
632
|
+
descriptor: "failure",
|
|
633
|
+
xpGained: 0,
|
|
634
|
+
narrativeLine: `Unknown task: ${action.taskId}`,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
// Route to domain-specific resolver
|
|
638
|
+
let result;
|
|
639
|
+
switch (task.domain) {
|
|
640
|
+
case "bodilyKinesthetic":
|
|
641
|
+
result = resolveBodilyKinesthetic(actor, task, action);
|
|
642
|
+
break;
|
|
643
|
+
case "spatial":
|
|
644
|
+
result = resolveSpatial(actor, task, action);
|
|
645
|
+
break;
|
|
646
|
+
case "naturalist":
|
|
647
|
+
result = resolveNaturalist(actor, task, action);
|
|
648
|
+
break;
|
|
649
|
+
case "interSpecies":
|
|
650
|
+
result = resolveInterSpecies(actor, task, action, world);
|
|
651
|
+
break;
|
|
652
|
+
case "linguistic":
|
|
653
|
+
result = resolveLinguistic(actor, task, action, world);
|
|
654
|
+
break;
|
|
655
|
+
case "interpersonal":
|
|
656
|
+
result = resolveInterpersonal(actor, task, action, world);
|
|
657
|
+
break;
|
|
658
|
+
case "logicalMathematical":
|
|
659
|
+
result = resolveLogicalMathematical(actor, task, action);
|
|
660
|
+
break;
|
|
661
|
+
case "musical":
|
|
662
|
+
result = resolveMusical(actor, task, action);
|
|
663
|
+
break;
|
|
664
|
+
case "intrapersonal":
|
|
665
|
+
result = resolveIntrapersonal(actor, task, action);
|
|
666
|
+
break;
|
|
667
|
+
default:
|
|
668
|
+
result = {
|
|
669
|
+
quality_Q: q(0.50),
|
|
670
|
+
timeTaken_s: task.timeBase_s,
|
|
671
|
+
success: true,
|
|
672
|
+
descriptor: "adequate",
|
|
673
|
+
details: {},
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
// Calculate XP
|
|
677
|
+
const xpGained = calculateXP(result.descriptor, task.difficulty_Q);
|
|
678
|
+
// Generate narrative if requested
|
|
679
|
+
const narrativeLine = action.narrative
|
|
680
|
+
? generateNarrative(task, { ...result, domain: action.domain, xpGained }, actor)
|
|
681
|
+
: undefined;
|
|
682
|
+
return {
|
|
683
|
+
domain: action.domain,
|
|
684
|
+
quality_Q: result.quality_Q,
|
|
685
|
+
timeTaken_s: result.timeTaken_s,
|
|
686
|
+
success: result.success,
|
|
687
|
+
descriptor: result.descriptor,
|
|
688
|
+
xpGained,
|
|
689
|
+
narrativeLine,
|
|
690
|
+
details: result.details,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
// ── Utility Exports ───────────────────────────────────────────────────────────
|
|
694
|
+
export { getDomainIntelligence, canonicalizeDescriptor, calculateXP };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./crafting.js";
|
|
2
|
+
export * from "./navigation.js";
|
|
3
|
+
export * from "./naturalist.js";
|
|
4
|
+
export * from "./interspecies.js";
|
|
5
|
+
export * from "./language.js";
|
|
6
|
+
export * from "./teaching.js";
|
|
7
|
+
export * from "./willpower.js";
|
|
8
|
+
export * from "./engineering.js";
|
|
9
|
+
export * from "./acoustic.js";
|
|
10
|
+
export * from "./performance.js";
|
|
11
|
+
export * from "./catalogue.js";
|
|
12
|
+
export * from "./framework.js";
|