@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,121 @@
|
|
|
1
|
+
// src/competence/teaching.ts — Phase 37: Teaching and Skill Transfer
|
|
2
|
+
//
|
|
3
|
+
// Interpersonal intelligence governs social reading and teaching effectiveness.
|
|
4
|
+
// Extends Phase 21 progression system with interpersonal-based skill transfer.
|
|
5
|
+
//
|
|
6
|
+
// No kernel import — pure resolution module.
|
|
7
|
+
import { SCALE, q, clampQ, qMul, mulDiv } from "../units.js";
|
|
8
|
+
import { getSkill } from "../sim/skills.js";
|
|
9
|
+
import { makeRng } from "../rng.js";
|
|
10
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
11
|
+
/** Base XP per hour of quality teaching. */
|
|
12
|
+
const BASE_XP_RATE = 10; // 10 XP per hour at full quality
|
|
13
|
+
/** Base fatigue cost per hour of teaching. */
|
|
14
|
+
const BASE_FATIGUE_PER_HOUR = 500; // 500 J per hour
|
|
15
|
+
/** Maximum XP gain per session. */
|
|
16
|
+
const MAX_XP_PER_SESSION = 100;
|
|
17
|
+
/** Fatigue multiplier for extended sessions. */
|
|
18
|
+
const FATIGUE_MULTIPLIER = 1.5;
|
|
19
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a teaching session and compute XP gain.
|
|
22
|
+
*
|
|
23
|
+
* Formula:
|
|
24
|
+
* xpGained = hours × BASE_XP_RATE × interpersonal(teacher) × learningRate(learner)
|
|
25
|
+
* teacherFatigueJ = hours × BASE_FATIGUE_PER_HOUR × FATIGUE_MULTIPLIER
|
|
26
|
+
*
|
|
27
|
+
* @param teacher - The entity providing instruction.
|
|
28
|
+
* @param learner - The entity receiving instruction.
|
|
29
|
+
* @param spec - Teaching specification.
|
|
30
|
+
* @returns Teaching outcome with XP gained and teacher fatigue.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveTeaching(teacher, learner, spec) {
|
|
33
|
+
// Get interpersonal skill (teacher's ability to convey knowledge)
|
|
34
|
+
const interpersonal = spec.teacherInterpersonal_Q
|
|
35
|
+
?? (teacher.attributes.cognition?.interpersonal ?? q(0.50));
|
|
36
|
+
// Learning rate based on learner's interpersonal (receptivity) and natural aptitude
|
|
37
|
+
// Phase 37: interpersonal affects both teaching AND learning
|
|
38
|
+
const learnerInterpersonal = spec.learnerLearningRate_Q
|
|
39
|
+
?? (learner.attributes.cognition?.interpersonal ?? q(0.50));
|
|
40
|
+
// Teaching quality: teacher's interpersonal skill
|
|
41
|
+
const teachingQuality_Q = clampQ(interpersonal, q(0.20), SCALE.Q);
|
|
42
|
+
// Learning rate: learner's interpersonal (social learning) + existing skill level
|
|
43
|
+
const existingSkill = getSkill(learner.skills, spec.domain);
|
|
44
|
+
const skillLevelBonus = clampQ(mulDiv(existingSkill.energyTransferMul, q(0.30), SCALE.Q), q(0), q(0.30));
|
|
45
|
+
const learningRate_Q = clampQ((qMul(learnerInterpersonal, q(0.70)) + skillLevelBonus), q(0.20), SCALE.Q);
|
|
46
|
+
// Calculate XP: hours × base rate × teaching quality × learning rate
|
|
47
|
+
const rawXp = spec.hours * BASE_XP_RATE;
|
|
48
|
+
const qualityMul = mulDiv(teachingQuality_Q, learningRate_Q, SCALE.Q);
|
|
49
|
+
const xpGained = Math.min(MAX_XP_PER_SESSION, Math.round(rawXp * qualityMul / SCALE.Q));
|
|
50
|
+
// Teacher fatigue scales with hours and effort (inversely with skill)
|
|
51
|
+
// Better teachers expend less energy for same result
|
|
52
|
+
const effortMul = clampQ((SCALE.Q - mulDiv(interpersonal, q(0.50), SCALE.Q)), q(0.50), SCALE.Q);
|
|
53
|
+
const baseFatigue = spec.hours * BASE_FATIGUE_PER_HOUR;
|
|
54
|
+
const teacherFatigueJ = Math.round(baseFatigue * FATIGUE_MULTIPLIER * effortMul / SCALE.Q);
|
|
55
|
+
return { xpGained, teacherFatigueJ, teachingQuality_Q };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Compute maximum effective teaching hours based on teacher stamina.
|
|
59
|
+
*/
|
|
60
|
+
export function computeMaxTeachingHours(teacher, availableEnergyJ) {
|
|
61
|
+
const interpersonal = (teacher.attributes.cognition?.interpersonal ?? q(0.50));
|
|
62
|
+
// More skilled teachers use energy more efficiently
|
|
63
|
+
const effortMul = clampQ((SCALE.Q - mulDiv(interpersonal, q(0.50), SCALE.Q)), q(0.50), SCALE.Q);
|
|
64
|
+
const fatiguePerHour = BASE_FATIGUE_PER_HOUR * FATIGUE_MULTIPLIER * effortMul / SCALE.Q;
|
|
65
|
+
return Math.floor(availableEnergyJ / Math.max(1, fatiguePerHour));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if teacher is qualified to teach a skill.
|
|
69
|
+
* Teacher should have at least journeyman level (skill delta showing improvement).
|
|
70
|
+
*/
|
|
71
|
+
export function isQualifiedTeacher(teacher, domain, minimumProficiency = q(0.30)) {
|
|
72
|
+
const skill = getSkill(teacher.skills, domain);
|
|
73
|
+
// Check if teacher has made meaningful progress in this skill
|
|
74
|
+
// Negative energyTransferMul means they've learned (more efficient)
|
|
75
|
+
// A good teacher should have negative energyTransferMul (below baseline)
|
|
76
|
+
// proficiency = 1.0 + (energyTransferMul / SCALE.Q), where negative mul = better
|
|
77
|
+
const proficiency = clampQ((SCALE.Q + skill.energyTransferMul), q(0), SCALE.Q);
|
|
78
|
+
// Default skill has energyTransferMul = 0, so proficiency = SCALE.Q (1.0)
|
|
79
|
+
// We need to check if they've actually improved (negative mul)
|
|
80
|
+
const hasActualSkill = skill.energyTransferMul < 0;
|
|
81
|
+
return hasActualSkill && proficiency >= minimumProficiency;
|
|
82
|
+
}
|
|
83
|
+
// ── Deception detection (Phase 37 extension) ──────────────────────────────────
|
|
84
|
+
/**
|
|
85
|
+
* Compute probability of detecting deception.
|
|
86
|
+
*
|
|
87
|
+
* Formula:
|
|
88
|
+
* P_detect = clamp(
|
|
89
|
+
* attentionDepth × 0.50 + interpersonal × 0.50 − plausibility,
|
|
90
|
+
* 0, 1)
|
|
91
|
+
*
|
|
92
|
+
* @param detector - The entity attempting to detect deception.
|
|
93
|
+
* @param plausibility_Q - Plausibility of the deception (0–1).
|
|
94
|
+
* @returns Detection probability (0–1).
|
|
95
|
+
*/
|
|
96
|
+
export function computeDeceptionDetectionProbability(detector, plausibility_Q) {
|
|
97
|
+
// Attention depth contribution (perception skill)
|
|
98
|
+
const attentionDepth = detector.attributes.perception?.attentionDepth ?? 4;
|
|
99
|
+
const attentionContrib = clampQ(Math.trunc((attentionDepth * SCALE.Q) / 10), q(0), SCALE.Q);
|
|
100
|
+
// Interpersonal contribution (social intuition)
|
|
101
|
+
const interpersonal = (detector.attributes.cognition?.interpersonal ?? q(0.50));
|
|
102
|
+
// Combined detection capability: 50% attention + 50% interpersonal
|
|
103
|
+
const detectionCap = clampQ((mulDiv(attentionContrib, q(0.50), SCALE.Q) +
|
|
104
|
+
mulDiv(interpersonal, q(0.50), SCALE.Q)), q(0), SCALE.Q);
|
|
105
|
+
// Subtract plausibility (better lies are harder to detect)
|
|
106
|
+
const pDetect = clampQ((detectionCap - plausibility_Q), q(0), SCALE.Q);
|
|
107
|
+
return pDetect;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve deception detection.
|
|
111
|
+
* @returns True if deception was detected.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveDeceptionDetection(detector, plausibility_Q, seed) {
|
|
114
|
+
const pDetect = computeDeceptionDetectionProbability(detector, plausibility_Q);
|
|
115
|
+
const rng = makeRng(seed, SCALE.Q);
|
|
116
|
+
const roll = rng.q01();
|
|
117
|
+
const detected = roll < pDetect;
|
|
118
|
+
// Confidence based on detection probability (higher = more certain)
|
|
119
|
+
const confidence_Q = clampQ((pDetect + mulDiv(rng.q01(), q(0.20), SCALE.Q)), q(0.50), SCALE.Q);
|
|
120
|
+
return { detected, confidence_Q };
|
|
121
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
import type { Entity } from "../sim/entity.js";
|
|
3
|
+
/**
|
|
4
|
+
* Willpower state analogous to physical energy reserve.
|
|
5
|
+
*/
|
|
6
|
+
export interface WillpowerState {
|
|
7
|
+
/** Current willpower in joules (same unit as energy_J). */
|
|
8
|
+
current_J: number;
|
|
9
|
+
/** Maximum willpower capacity based on intrapersonal intelligence. */
|
|
10
|
+
max_J: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Outcome of a willpower operation.
|
|
14
|
+
*/
|
|
15
|
+
export interface WillpowerOutcome {
|
|
16
|
+
/** True if operation succeeded (sufficient willpower). */
|
|
17
|
+
success: boolean;
|
|
18
|
+
/** Remaining willpower after operation. */
|
|
19
|
+
remaining_J: number;
|
|
20
|
+
/** True if willpower was depleted by this operation. */
|
|
21
|
+
depleted: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compute maximum willpower for an entity based on intrapersonal intelligence.
|
|
25
|
+
*
|
|
26
|
+
* Formula: max_J = intrapersonal × SCALE_WILLPOWER_J
|
|
27
|
+
*
|
|
28
|
+
* @param entity - The entity to compute willpower for.
|
|
29
|
+
* @returns Maximum willpower capacity in joules.
|
|
30
|
+
*/
|
|
31
|
+
export declare function computeMaxWillpower(entity: Entity): number;
|
|
32
|
+
/**
|
|
33
|
+
* Initialize willpower state for an entity.
|
|
34
|
+
* Call this when first adding willpower to an entity.
|
|
35
|
+
*/
|
|
36
|
+
export declare function initializeWillpower(entity: Entity): WillpowerState;
|
|
37
|
+
/**
|
|
38
|
+
* Deduct willpower for a cognitive operation.
|
|
39
|
+
*
|
|
40
|
+
* @param willpower - Current willpower state (mutated).
|
|
41
|
+
* @param cost_J - Cost of the operation in joules.
|
|
42
|
+
* @returns Outcome indicating success/failure and depletion status.
|
|
43
|
+
*/
|
|
44
|
+
export declare function deductWillpower(willpower: WillpowerState, cost_J: number): WillpowerOutcome;
|
|
45
|
+
/**
|
|
46
|
+
* Replenish willpower during rest.
|
|
47
|
+
*
|
|
48
|
+
* Formula: replenish = max_J × REPLENISH_RATE_PER_HOUR × hours
|
|
49
|
+
*
|
|
50
|
+
* @param willpower - Current willpower state (mutated).
|
|
51
|
+
* @param hours - Hours of rest.
|
|
52
|
+
* @returns Amount actually replenished.
|
|
53
|
+
*/
|
|
54
|
+
export declare function replenishWillpower(willpower: WillpowerState, hours: number): number;
|
|
55
|
+
/**
|
|
56
|
+
* Step willpower for concentration aura (Phase 12B integration).
|
|
57
|
+
* Called per tick when entity has active concentration.
|
|
58
|
+
*
|
|
59
|
+
* @param willpower - Current willpower state (mutated).
|
|
60
|
+
* @returns True if concentration can be maintained.
|
|
61
|
+
*/
|
|
62
|
+
export declare function stepConcentrationWillpower(willpower: WillpowerState): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Check if entity has sufficient willpower for an operation.
|
|
65
|
+
*/
|
|
66
|
+
export declare function hasSufficientWillpower(willpower: WillpowerState, cost_J: number): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Get willpower ratio (0–1) for UI/AI decisions.
|
|
69
|
+
*/
|
|
70
|
+
export declare function getWillpowerRatio(willpower: WillpowerState): Q;
|
|
71
|
+
/**
|
|
72
|
+
* Force willpower state update (for downtime healing, cheats, etc).
|
|
73
|
+
*/
|
|
74
|
+
export declare function setWillpower(willpower: WillpowerState, value_J: number): void;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// src/competence/willpower.ts — Phase 38: Willpower Reserve (Intrapersonal Intelligence)
|
|
2
|
+
//
|
|
3
|
+
// Intrapersonal intelligence governs internal management: willpower, sustained focus,
|
|
4
|
+
// emotional self-regulation, and mental stamina for cognitively demanding tasks.
|
|
5
|
+
//
|
|
6
|
+
// Analogous to Phase 2B's reserveEnergy_J for physical stamina.
|
|
7
|
+
// No kernel import — pure resolution module.
|
|
8
|
+
import { SCALE, q, clampQ } from "../units.js";
|
|
9
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
10
|
+
/** Scale factor: max willpower = intrapersonal × SCALE_WILLPOWER_J. */
|
|
11
|
+
const SCALE_WILLPOWER_J = 50_000; // 50kJ max at intrapersonal q(1.0)
|
|
12
|
+
/** Base replenishment rate: q(0.10) of max per hour of rest. */
|
|
13
|
+
const REPLENISH_RATE_PER_HOUR = q(0.10);
|
|
14
|
+
/** Concentration aura drain per tick (from Phase 12B). */
|
|
15
|
+
const CONCENTRATION_DRAIN_PER_TICK = 100; // 100 J per tick
|
|
16
|
+
/** Minimum willpower required to maintain concentration. */
|
|
17
|
+
const MIN_WILLPOWER_FOR_CONCENTRATION = 50;
|
|
18
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Compute maximum willpower for an entity based on intrapersonal intelligence.
|
|
21
|
+
*
|
|
22
|
+
* Formula: max_J = intrapersonal × SCALE_WILLPOWER_J
|
|
23
|
+
*
|
|
24
|
+
* @param entity - The entity to compute willpower for.
|
|
25
|
+
* @returns Maximum willpower capacity in joules.
|
|
26
|
+
*/
|
|
27
|
+
export function computeMaxWillpower(entity) {
|
|
28
|
+
const intrapersonal = (entity.attributes.cognition?.intrapersonal ?? q(0.55));
|
|
29
|
+
return Math.round((intrapersonal * SCALE_WILLPOWER_J) / SCALE.Q);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize willpower state for an entity.
|
|
33
|
+
* Call this when first adding willpower to an entity.
|
|
34
|
+
*/
|
|
35
|
+
export function initializeWillpower(entity) {
|
|
36
|
+
const max_J = computeMaxWillpower(entity);
|
|
37
|
+
return {
|
|
38
|
+
current_J: max_J, // Start at full
|
|
39
|
+
max_J,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Deduct willpower for a cognitive operation.
|
|
44
|
+
*
|
|
45
|
+
* @param willpower - Current willpower state (mutated).
|
|
46
|
+
* @param cost_J - Cost of the operation in joules.
|
|
47
|
+
* @returns Outcome indicating success/failure and depletion status.
|
|
48
|
+
*/
|
|
49
|
+
export function deductWillpower(willpower, cost_J) {
|
|
50
|
+
if (willpower.current_J < cost_J) {
|
|
51
|
+
// Insufficient willpower - operation fails
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
remaining_J: willpower.current_J,
|
|
55
|
+
depleted: willpower.current_J < MIN_WILLPOWER_FOR_CONCENTRATION,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
willpower.current_J -= cost_J;
|
|
59
|
+
const depleted = willpower.current_J < MIN_WILLPOWER_FOR_CONCENTRATION;
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
remaining_J: willpower.current_J,
|
|
63
|
+
depleted,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Replenish willpower during rest.
|
|
68
|
+
*
|
|
69
|
+
* Formula: replenish = max_J × REPLENISH_RATE_PER_HOUR × hours
|
|
70
|
+
*
|
|
71
|
+
* @param willpower - Current willpower state (mutated).
|
|
72
|
+
* @param hours - Hours of rest.
|
|
73
|
+
* @returns Amount actually replenished.
|
|
74
|
+
*/
|
|
75
|
+
export function replenishWillpower(willpower, hours) {
|
|
76
|
+
const replenishAmount = Math.round((willpower.max_J * REPLENISH_RATE_PER_HOUR * hours) / SCALE.Q);
|
|
77
|
+
const before = willpower.current_J;
|
|
78
|
+
willpower.current_J = Math.min(willpower.max_J, willpower.current_J + replenishAmount);
|
|
79
|
+
return willpower.current_J - before;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Step willpower for concentration aura (Phase 12B integration).
|
|
83
|
+
* Called per tick when entity has active concentration.
|
|
84
|
+
*
|
|
85
|
+
* @param willpower - Current willpower state (mutated).
|
|
86
|
+
* @returns True if concentration can be maintained.
|
|
87
|
+
*/
|
|
88
|
+
export function stepConcentrationWillpower(willpower) {
|
|
89
|
+
if (willpower.current_J < CONCENTRATION_DRAIN_PER_TICK) {
|
|
90
|
+
// Insufficient willpower - aura collapses
|
|
91
|
+
willpower.current_J = Math.max(0, willpower.current_J);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
willpower.current_J -= CONCENTRATION_DRAIN_PER_TICK;
|
|
95
|
+
return willpower.current_J >= MIN_WILLPOWER_FOR_CONCENTRATION;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if entity has sufficient willpower for an operation.
|
|
99
|
+
*/
|
|
100
|
+
export function hasSufficientWillpower(willpower, cost_J) {
|
|
101
|
+
return willpower.current_J >= cost_J;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get willpower ratio (0–1) for UI/AI decisions.
|
|
105
|
+
*/
|
|
106
|
+
export function getWillpowerRatio(willpower) {
|
|
107
|
+
return clampQ(Math.round((willpower.current_J * SCALE.Q) / Math.max(1, willpower.max_J)), q(0), SCALE.Q);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Force willpower state update (for downtime healing, cheats, etc).
|
|
111
|
+
*/
|
|
112
|
+
export function setWillpower(willpower, value_J) {
|
|
113
|
+
willpower.current_J = clampQ(value_J, 0, willpower.max_J);
|
|
114
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
import type { Entity } from "../sim/entity.js";
|
|
3
|
+
import type { Inventory } from "../inventory.js";
|
|
4
|
+
import type { Recipe } from "./recipes.js";
|
|
5
|
+
import type { Material } from "./materials.js";
|
|
6
|
+
import type { WorkshopInstance } from "./workshops.js";
|
|
7
|
+
import type { ProductionLine } from "./manufacturing.js";
|
|
8
|
+
import { getRecipeById, getAllRecipes, type FeasibilityResult, type RecipeResolutionResult } from "./recipes.js";
|
|
9
|
+
import { getMaterialTypeById, createMaterialItem, type MaterialPropertyModifier } from "./materials.js";
|
|
10
|
+
import { getWorkshopBonus, validateWorkshopRequirements, createWorkshop, upgradeWorkshop, type WorkshopBonus } from "./workshops.js";
|
|
11
|
+
import { estimateBatchCompletionTime, isProductionLineComplete } from "./manufacturing.js";
|
|
12
|
+
/**
|
|
13
|
+
* Craft a single item using a recipe, entity, inventory, and workshop.
|
|
14
|
+
* Returns resolution result with success, quality, time, and consumed ingredients.
|
|
15
|
+
*/
|
|
16
|
+
export declare function craftItem(recipeId: string, entity: Entity, inventory: Inventory, workshop: WorkshopInstance, worldSeed: number, tick: number, salt: number): RecipeResolutionResult;
|
|
17
|
+
/**
|
|
18
|
+
* Start batch manufacturing of items.
|
|
19
|
+
* Creates a production line and returns its ID.
|
|
20
|
+
*/
|
|
21
|
+
export declare function startManufacturing(recipeId: string, quantity: number, workshop: WorkshopInstance, workers: Entity[], worldSeed: number, tick: number, salt: number): {
|
|
22
|
+
success: boolean;
|
|
23
|
+
lineId?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Advance manufacturing for a production line.
|
|
28
|
+
* Returns items completed in this step.
|
|
29
|
+
*/
|
|
30
|
+
export declare function advanceManufacturing(lineId: string, deltaTime_s: number, workers: Entity[], workshop: WorkshopInstance, worldSeed: number, tick: number, salt: number): {
|
|
31
|
+
itemsCompleted: number;
|
|
32
|
+
totalProduced: number;
|
|
33
|
+
progress_Q: Q;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Get all recipes that can be crafted by an entity with given inventory and workshop.
|
|
37
|
+
* Returns filtered list of recipes.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getAvailableRecipes(entity: Entity, inventory: Inventory, workshop: WorkshopInstance): Recipe[];
|
|
40
|
+
/**
|
|
41
|
+
* Get material properties for a crafted item.
|
|
42
|
+
* Applies material property modifiers to base item stats.
|
|
43
|
+
*/
|
|
44
|
+
export declare function applyMaterialProperties(baseItem: any, // Placeholder: Item type
|
|
45
|
+
material: Material): MaterialPropertyModifier;
|
|
46
|
+
/**
|
|
47
|
+
* Integrate crafting result into inventory.
|
|
48
|
+
* Consumes ingredients and adds crafted item.
|
|
49
|
+
* Returns success and error if any.
|
|
50
|
+
*/
|
|
51
|
+
export declare function integrateCraftingIntoInventory(inventory: Inventory, result: RecipeResolutionResult, instanceId: string): {
|
|
52
|
+
success: boolean;
|
|
53
|
+
error?: string;
|
|
54
|
+
};
|
|
55
|
+
export { type Recipe, type FeasibilityResult, type RecipeResolutionResult, type Material, type MaterialPropertyModifier, type WorkshopInstance, type WorkshopBonus, type ProductionLine, getRecipeById, getAllRecipes, getMaterialTypeById, createMaterialItem, getWorkshopBonus, validateWorkshopRequirements, createWorkshop, upgradeWorkshop, estimateBatchCompletionTime, isProductionLineComplete, };
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// src/crafting/index.ts — Phase 61: Crafting Ecosystem Main API
|
|
2
|
+
//
|
|
3
|
+
// Integration layer for crafting, materials, workshops, and manufacturing.
|
|
4
|
+
// Provides high‑level functions for game systems to interact with crafting.
|
|
5
|
+
import { q, SCALE, qMul } from "../units.js";
|
|
6
|
+
import { eventSeed, hashString } from "../sim/seeds.js";
|
|
7
|
+
import { consumeItemsByTemplateId, addItemToInventory } from "../inventory.js";
|
|
8
|
+
import { validateRecipeFeasibility, resolveRecipe, getRecipeById, getAllRecipes, } from "./recipes.js";
|
|
9
|
+
import { getMaterialTypeById, calculateMaterialEffect, createMaterialItem, } from "./materials.js";
|
|
10
|
+
import { getWorkshopBonus, validateWorkshopRequirements, createWorkshop, upgradeWorkshop, } from "./workshops.js";
|
|
11
|
+
import { setupProductionLine, advanceProduction, estimateBatchCompletionTime, isProductionLineComplete, } from "./manufacturing.js";
|
|
12
|
+
// ── Main Crafting API ─────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Craft a single item using a recipe, entity, inventory, and workshop.
|
|
15
|
+
* Returns resolution result with success, quality, time, and consumed ingredients.
|
|
16
|
+
*/
|
|
17
|
+
export function craftItem(recipeId, entity, inventory, workshop, worldSeed, tick, salt) {
|
|
18
|
+
const recipe = getRecipeById(recipeId);
|
|
19
|
+
if (!recipe) {
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
outputItemId: recipeId,
|
|
23
|
+
outputQuantity: 0,
|
|
24
|
+
quality_Q: q(0),
|
|
25
|
+
timeTaken_s: 0,
|
|
26
|
+
consumedIngredients: [],
|
|
27
|
+
descriptor: "ruined",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// Convert workshop tools map to format expected by recipes
|
|
31
|
+
const toolQualities = new Map();
|
|
32
|
+
for (const [category, quality] of workshop.availableTools.entries()) {
|
|
33
|
+
toolQualities.set(category, quality);
|
|
34
|
+
}
|
|
35
|
+
// Validate feasibility
|
|
36
|
+
const feasibility = validateRecipeFeasibility(recipe, inventory, entity, toolQualities);
|
|
37
|
+
if (!feasibility.feasible) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
outputItemId: recipe.outputItemId,
|
|
41
|
+
outputQuantity: 0,
|
|
42
|
+
quality_Q: q(0),
|
|
43
|
+
timeTaken_s: recipe.baseTime_s,
|
|
44
|
+
consumedIngredients: [],
|
|
45
|
+
descriptor: "ruined",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Apply workshop bonuses
|
|
49
|
+
const workshopBonus = getWorkshopBonus(workshop, recipe);
|
|
50
|
+
// Resolve recipe (workshop bonuses not yet integrated into resolveRecipe)
|
|
51
|
+
const result = resolveRecipe(recipe, entity, inventory, toolQualities, worldSeed, tick, entity.id, salt);
|
|
52
|
+
// Adjust time with workshop time reduction
|
|
53
|
+
result.timeTaken_s = Math.round(result.timeTaken_s * SCALE.Q / workshopBonus.timeReduction_Q);
|
|
54
|
+
// Adjust quality with workshop quality bonus
|
|
55
|
+
result.quality_Q = clampQ(qMul(result.quality_Q, workshopBonus.qualityBonus_Q), q(0), SCALE.Q);
|
|
56
|
+
// Update descriptor based on new quality
|
|
57
|
+
result.descriptor = qualityToDescriptor(result.quality_Q);
|
|
58
|
+
// If crafting succeeded, integrate into inventory
|
|
59
|
+
if (result.success) {
|
|
60
|
+
const instanceSeed = eventSeed(worldSeed, tick, entity.id, hashString(recipe.id), salt + 0xCAFE);
|
|
61
|
+
const instanceId = `craft_${recipeId}_${instanceSeed}`;
|
|
62
|
+
const integration = integrateCraftingIntoInventory(inventory, result, instanceId);
|
|
63
|
+
if (!integration.success) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
outputItemId: recipe.outputItemId,
|
|
67
|
+
outputQuantity: 0,
|
|
68
|
+
quality_Q: q(0),
|
|
69
|
+
timeTaken_s: result.timeTaken_s,
|
|
70
|
+
consumedIngredients: [],
|
|
71
|
+
descriptor: "ruined",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Start batch manufacturing of items.
|
|
79
|
+
* Creates a production line and returns its ID.
|
|
80
|
+
*/
|
|
81
|
+
export function startManufacturing(recipeId, quantity, workshop, workers, worldSeed, tick, salt) {
|
|
82
|
+
const recipe = getRecipeById(recipeId);
|
|
83
|
+
if (!recipe) {
|
|
84
|
+
return { success: false, error: `Recipe ${recipeId} not found` };
|
|
85
|
+
}
|
|
86
|
+
// Validate workshop requirements
|
|
87
|
+
const workshopReq = validateWorkshopRequirements(workshop, recipe);
|
|
88
|
+
if (workshopReq.facilityLevelInsufficient || workshopReq.missingTools.length > 0) {
|
|
89
|
+
return { success: false, error: "Workshop insufficient for recipe" };
|
|
90
|
+
}
|
|
91
|
+
// Validate worker skills (at least one worker meets skill requirements)
|
|
92
|
+
let skilledWorkerExists = false;
|
|
93
|
+
for (const worker of workers) {
|
|
94
|
+
let meetsAll = true;
|
|
95
|
+
for (const skillReq of recipe.skillRequirements) {
|
|
96
|
+
const skill = worker.attributes.cognition?.[skillReq.skillType] ?? q(0);
|
|
97
|
+
if (skill < skillReq.minLevel_Q) {
|
|
98
|
+
meetsAll = false;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (meetsAll) {
|
|
103
|
+
skilledWorkerExists = true;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!skilledWorkerExists) {
|
|
108
|
+
return { success: false, error: "No worker meets skill requirements" };
|
|
109
|
+
}
|
|
110
|
+
const primaryWorkerId = workers.length > 0 ? workers[0].id : 0;
|
|
111
|
+
const lineSeed = eventSeed(worldSeed, tick, primaryWorkerId, hashString(recipe.id), salt);
|
|
112
|
+
const orderId = `order_${worldSeed}_${tick}_${recipe.id}_${salt}`;
|
|
113
|
+
// Create manufacturing order
|
|
114
|
+
const order = {
|
|
115
|
+
orderId,
|
|
116
|
+
recipeId,
|
|
117
|
+
quantity,
|
|
118
|
+
workshop,
|
|
119
|
+
};
|
|
120
|
+
// Setup production line
|
|
121
|
+
const line = setupProductionLine(order, workers, lineSeed);
|
|
122
|
+
// TODO: store production line in persistent state
|
|
123
|
+
return { success: true, lineId: line.lineId };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Advance manufacturing for a production line.
|
|
127
|
+
* Returns items completed in this step.
|
|
128
|
+
*/
|
|
129
|
+
export function advanceManufacturing(lineId, deltaTime_s, workers, workshop, worldSeed, tick, salt) {
|
|
130
|
+
// Compute deterministic seed from lineId
|
|
131
|
+
let lineIdHash = 0;
|
|
132
|
+
for (let i = 0; i < lineId.length; i++)
|
|
133
|
+
lineIdHash += lineId.charCodeAt(i);
|
|
134
|
+
const seed = eventSeed(worldSeed, tick, lineIdHash, 0, salt);
|
|
135
|
+
// TODO: retrieve production line by lineId
|
|
136
|
+
const line = {
|
|
137
|
+
lineId,
|
|
138
|
+
recipeId: "recipe_shortsword",
|
|
139
|
+
batchSize: 10,
|
|
140
|
+
itemsProduced: 0,
|
|
141
|
+
progress_Q: q(0),
|
|
142
|
+
assignedWorkers: workers.map(w => w.id),
|
|
143
|
+
priority: 1,
|
|
144
|
+
qualityRange: { min_Q: q(0.30), max_Q: q(0.90), avg_Q: q(0.60) },
|
|
145
|
+
};
|
|
146
|
+
const result = advanceProduction(line, deltaTime_s, workers, workshop, seed);
|
|
147
|
+
// TODO: update stored line
|
|
148
|
+
return {
|
|
149
|
+
itemsCompleted: result.itemsCompleted,
|
|
150
|
+
totalProduced: result.totalItemsProduced,
|
|
151
|
+
progress_Q: result.progress_Q,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// ── Query API ─────────────────────────────────────────────────────────────────
|
|
155
|
+
/**
|
|
156
|
+
* Get all recipes that can be crafted by an entity with given inventory and workshop.
|
|
157
|
+
* Returns filtered list of recipes.
|
|
158
|
+
*/
|
|
159
|
+
export function getAvailableRecipes(entity, inventory, workshop) {
|
|
160
|
+
const allRecipes = getAllRecipes();
|
|
161
|
+
const toolQualities = new Map();
|
|
162
|
+
for (const [category, quality] of workshop.availableTools.entries()) {
|
|
163
|
+
toolQualities.set(category, quality);
|
|
164
|
+
}
|
|
165
|
+
return allRecipes.filter(recipe => {
|
|
166
|
+
const feasibility = validateRecipeFeasibility(recipe, inventory, entity, toolQualities);
|
|
167
|
+
return feasibility.feasible;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get material properties for a crafted item.
|
|
172
|
+
* Applies material property modifiers to base item stats.
|
|
173
|
+
*/
|
|
174
|
+
export function applyMaterialProperties(baseItem, // Placeholder: Item type
|
|
175
|
+
material) {
|
|
176
|
+
return calculateMaterialEffect(baseItem, material);
|
|
177
|
+
}
|
|
178
|
+
// ── Integration with Existing Systems ─────────────────────────────────────────
|
|
179
|
+
/**
|
|
180
|
+
* Integrate crafting result into inventory.
|
|
181
|
+
* Consumes ingredients and adds crafted item.
|
|
182
|
+
* Returns success and error if any.
|
|
183
|
+
*/
|
|
184
|
+
export function integrateCraftingIntoInventory(inventory, result, instanceId) {
|
|
185
|
+
if (!result.success) {
|
|
186
|
+
return { success: false, error: "Crafting failed" };
|
|
187
|
+
}
|
|
188
|
+
// Consume ingredients
|
|
189
|
+
for (const ing of result.consumedIngredients) {
|
|
190
|
+
const ok = consumeItemsByTemplateId(inventory, ing.itemId, ing.quantity);
|
|
191
|
+
if (!ok) {
|
|
192
|
+
return { success: false, error: `Insufficient ${ing.itemId} after validation` };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Create item instance
|
|
196
|
+
const itemInstance = {
|
|
197
|
+
instanceId,
|
|
198
|
+
templateId: result.outputItemId,
|
|
199
|
+
quantity: result.outputQuantity,
|
|
200
|
+
durability_Q: result.quality_Q, // Use quality as durability placeholder
|
|
201
|
+
modifications: [],
|
|
202
|
+
containerPath: [],
|
|
203
|
+
};
|
|
204
|
+
// Add to inventory
|
|
205
|
+
const addResult = addItemToInventory(inventory, itemInstance);
|
|
206
|
+
if (!addResult.success) {
|
|
207
|
+
return { success: false, error: "No container capacity for crafted item" };
|
|
208
|
+
}
|
|
209
|
+
return { success: true };
|
|
210
|
+
}
|
|
211
|
+
// ── Utility Functions ─────────────────────────────────────────────────────────
|
|
212
|
+
function clampQ(value, min, max) {
|
|
213
|
+
return Math.max(min, Math.min(max, value));
|
|
214
|
+
}
|
|
215
|
+
function qualityToDescriptor(quality_Q) {
|
|
216
|
+
if (quality_Q >= q(0.85))
|
|
217
|
+
return "masterwork";
|
|
218
|
+
if (quality_Q >= q(0.65))
|
|
219
|
+
return "fine";
|
|
220
|
+
if (quality_Q >= q(0.40))
|
|
221
|
+
return "adequate";
|
|
222
|
+
if (quality_Q >= q(0.20))
|
|
223
|
+
return "poor";
|
|
224
|
+
return "ruined";
|
|
225
|
+
}
|
|
226
|
+
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
227
|
+
export {
|
|
228
|
+
// Functions
|
|
229
|
+
getRecipeById, getAllRecipes, getMaterialTypeById, createMaterialItem, getWorkshopBonus, validateWorkshopRequirements, createWorkshop, upgradeWorkshop, estimateBatchCompletionTime, isProductionLineComplete, };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Q } from "../units.js";
|
|
2
|
+
import type { Entity } from "../sim/entity.js";
|
|
3
|
+
import type { Recipe } from "./recipes.js";
|
|
4
|
+
import type { WorkshopInstance } from "./workshops.js";
|
|
5
|
+
/** Production line state for batch manufacturing. */
|
|
6
|
+
export interface ProductionLine {
|
|
7
|
+
lineId: string;
|
|
8
|
+
recipeId: string;
|
|
9
|
+
batchSize: number;
|
|
10
|
+
itemsProduced: number;
|
|
11
|
+
progress_Q: Q;
|
|
12
|
+
assignedWorkers: number[];
|
|
13
|
+
priority: number;
|
|
14
|
+
qualityRange: {
|
|
15
|
+
min_Q: Q;
|
|
16
|
+
max_Q: Q;
|
|
17
|
+
avg_Q: Q;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Manufacturing order for starting batch production. */
|
|
21
|
+
export interface ManufacturingOrder {
|
|
22
|
+
orderId: string;
|
|
23
|
+
recipeId: string;
|
|
24
|
+
quantity: number;
|
|
25
|
+
workshop: WorkshopInstance;
|
|
26
|
+
deadlineTick?: number;
|
|
27
|
+
}
|
|
28
|
+
/** Result of advancing production. */
|
|
29
|
+
export interface ProductionAdvanceResult {
|
|
30
|
+
itemsCompleted: number;
|
|
31
|
+
totalItemsProduced: number;
|
|
32
|
+
progress_Q: Q;
|
|
33
|
+
qualityRange: {
|
|
34
|
+
min_Q: Q;
|
|
35
|
+
max_Q: Q;
|
|
36
|
+
avg_Q: Q;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Assembly step for multi-stage crafting. */
|
|
40
|
+
export interface AssemblyStep {
|
|
41
|
+
stepId: string;
|
|
42
|
+
description: string;
|
|
43
|
+
requiredSkill: string;
|
|
44
|
+
timeFraction: Q;
|
|
45
|
+
toolRequirements: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Initialize a production line for batch manufacturing.
|
|
49
|
+
*/
|
|
50
|
+
export declare function setupProductionLine(order: ManufacturingOrder, workers: Entity[], seed: number): ProductionLine;
|
|
51
|
+
/**
|
|
52
|
+
* Advance production line by deltaTime seconds.
|
|
53
|
+
* Progress accumulates based on number of workers and their skills.
|
|
54
|
+
* Returns new items completed and updated progress.
|
|
55
|
+
*/
|
|
56
|
+
export declare function advanceProduction(productionLine: ProductionLine, deltaTime_s: number, workers: Entity[], // Must match assignedWorkers IDs
|
|
57
|
+
workshop: WorkshopInstance, seed: number): ProductionAdvanceResult;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate predicted quality range for a batch based on workers, materials, workshop.
|
|
60
|
+
* Returns min, max, and average expected quality.
|
|
61
|
+
*/
|
|
62
|
+
export declare function calculateBatchQualityRange(workers: Entity[], workshop: WorkshopInstance, seed: number): {
|
|
63
|
+
min_Q: Q;
|
|
64
|
+
max_Q: Q;
|
|
65
|
+
avg_Q: Q;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Create assembly steps for a complex recipe.
|
|
69
|
+
*/
|
|
70
|
+
export declare function createAssemblySteps(recipe: Recipe): AssemblyStep[];
|
|
71
|
+
/**
|
|
72
|
+
* Advance a single assembly step.
|
|
73
|
+
*/
|
|
74
|
+
export declare function advanceAssemblyStep(step: AssemblyStep, worker: Entity, deltaTime_s: number, availableTools: Map<string, Q>): {
|
|
75
|
+
progress_Q: Q;
|
|
76
|
+
completed: boolean;
|
|
77
|
+
};
|
|
78
|
+
/** Estimate time to complete a batch given workers and workshop. */
|
|
79
|
+
export declare function estimateBatchCompletionTime(batchSize: number, workers: Entity[], workshop: WorkshopInstance): number;
|
|
80
|
+
/** Check if production line is complete. */
|
|
81
|
+
export declare function isProductionLineComplete(line: ProductionLine): boolean;
|
|
82
|
+
/** Get progress percentage (0–1). */
|
|
83
|
+
export declare function getProductionLineProgress(line: ProductionLine): Q;
|