@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,69 @@
|
|
|
1
|
+
import type { Q } from "./units.js";
|
|
2
|
+
import type { ItemInstance } from "./inventory.js";
|
|
3
|
+
/** Durability loss from parrying a heavy strike. */
|
|
4
|
+
export declare const DURABILITY_LOSS_PARRY: Q;
|
|
5
|
+
/** Durability loss from blocking with a weapon (scales with impact energy). */
|
|
6
|
+
export declare const DURABILITY_LOSS_BLOCK_BASE: Q;
|
|
7
|
+
/** Durability loss from armour absorbing damage (scales with absorbed energy). */
|
|
8
|
+
export declare const DURABILITY_LOSS_ARMOUR_BASE: Q;
|
|
9
|
+
/** Durability loss from normal weapon use (per strike). */
|
|
10
|
+
export declare const DURABILITY_LOSS_STRIKE: Q;
|
|
11
|
+
/** Threshold below which an item is considered "damaged" (visual/functional indicator). */
|
|
12
|
+
export declare const DAMAGED_THRESHOLD_Q: Q;
|
|
13
|
+
/** Threshold below which an item is "broken" (non-functional). */
|
|
14
|
+
export declare const BROKEN_THRESHOLD_Q: Q;
|
|
15
|
+
/** Check if item has durability tracking. */
|
|
16
|
+
export declare function hasDurability(item: ItemInstance): boolean;
|
|
17
|
+
/** Get current durability (returns 1.0 if item has no durability tracking). */
|
|
18
|
+
export declare function getDurability(item: ItemInstance): Q;
|
|
19
|
+
/** Check if item is damaged (below 50% durability). */
|
|
20
|
+
export declare function isDamaged(item: ItemInstance): boolean;
|
|
21
|
+
/** Check if item is broken (below 10% durability, non-functional). */
|
|
22
|
+
export declare function isBroken(item: ItemInstance): boolean;
|
|
23
|
+
/** Get durability-based effectiveness multiplier for weapons. */
|
|
24
|
+
export declare function getWeaponEffectiveness(item: ItemInstance): Q;
|
|
25
|
+
/** Get armour protection multiplier based on durability. */
|
|
26
|
+
export declare function getArmourProtection(item: ItemInstance): Q;
|
|
27
|
+
/** Apply durability loss to an item. */
|
|
28
|
+
export declare function applyDurabilityLoss(item: ItemInstance, loss_Q: Q): void;
|
|
29
|
+
/** Record wear from parrying an attack. */
|
|
30
|
+
export declare function recordParryWear(item: ItemInstance, impactEnergy_J: number): void;
|
|
31
|
+
/** Record wear from blocking with a weapon/shield. */
|
|
32
|
+
export declare function recordBlockWear(item: ItemInstance, absorbedEnergy_J: number): void;
|
|
33
|
+
/** Record wear from armour absorbing damage. */
|
|
34
|
+
export declare function recordArmourWear(item: ItemInstance, absorbedEnergy_J: number): void;
|
|
35
|
+
/** Record wear from normal weapon strike (hitting armour, flesh, etc.). */
|
|
36
|
+
export declare function recordStrikeWear(item: ItemInstance, hitArmour: boolean): void;
|
|
37
|
+
export interface RepairResult {
|
|
38
|
+
success: boolean;
|
|
39
|
+
durabilityRestored_Q: Q;
|
|
40
|
+
qualityLevel_Q: Q;
|
|
41
|
+
repairCost: RepairCost;
|
|
42
|
+
narrative: string;
|
|
43
|
+
}
|
|
44
|
+
export interface RepairCost {
|
|
45
|
+
/** Time required in seconds. */
|
|
46
|
+
time_s: number;
|
|
47
|
+
/** Material units consumed. */
|
|
48
|
+
materials: number;
|
|
49
|
+
/** Currency cost if using a repair service. */
|
|
50
|
+
currency?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Calculate repair parameters based on crafter skill and item condition. */
|
|
53
|
+
export declare function calculateRepairNeed(item: ItemInstance): {
|
|
54
|
+
missingDurability_Q: Q;
|
|
55
|
+
baseTime_s: number;
|
|
56
|
+
baseMaterials: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a repair attempt.
|
|
60
|
+
* @param item The item to repair
|
|
61
|
+
* @param crafterCognitionQ The crafter's cognition.logicalMath (for technical skill)
|
|
62
|
+
* @param toolQuality_Q Quality of tools being used (0-1)
|
|
63
|
+
* @param seed RNG seed for determinism
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveRepair(item: ItemInstance, crafterCognitionQ: Q, toolQuality_Q: Q, seed: number): RepairResult;
|
|
66
|
+
/** Quick repair with no skill check (field repair). */
|
|
67
|
+
export declare function fieldRepair(item: ItemInstance): RepairResult;
|
|
68
|
+
/** Calculate item value based on durability and modifications. */
|
|
69
|
+
export declare function calculateItemValue(baseValue: number, item: ItemInstance): number;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// src/item-durability.ts — Phase 43: Item Durability & Repair
|
|
2
|
+
//
|
|
3
|
+
// Wear tracking for weapons and armour, with repair mechanics integrated
|
|
4
|
+
// with the competence system.
|
|
5
|
+
import { q, clampQ, SCALE, mulDiv } from "./units.js";
|
|
6
|
+
import { applyItemMod } from "./inventory.js";
|
|
7
|
+
// ── Durability Constants ──────────────────────────────────────────────────────
|
|
8
|
+
/** Durability loss from parrying a heavy strike. */
|
|
9
|
+
export const DURABILITY_LOSS_PARRY = q(0.02);
|
|
10
|
+
/** Durability loss from blocking with a weapon (scales with impact energy). */
|
|
11
|
+
export const DURABILITY_LOSS_BLOCK_BASE = q(0.03);
|
|
12
|
+
/** Durability loss from armour absorbing damage (scales with absorbed energy). */
|
|
13
|
+
export const DURABILITY_LOSS_ARMOUR_BASE = q(0.04);
|
|
14
|
+
/** Durability loss from normal weapon use (per strike). */
|
|
15
|
+
export const DURABILITY_LOSS_STRIKE = q(0.005);
|
|
16
|
+
/** Threshold below which an item is considered "damaged" (visual/functional indicator). */
|
|
17
|
+
export const DAMAGED_THRESHOLD_Q = q(0.50);
|
|
18
|
+
/** Threshold below which an item is "broken" (non-functional). */
|
|
19
|
+
export const BROKEN_THRESHOLD_Q = q(0.10);
|
|
20
|
+
// ── Durability Queries ─────────────────────────────────────────────────────────
|
|
21
|
+
/** Check if item has durability tracking. */
|
|
22
|
+
export function hasDurability(item) {
|
|
23
|
+
return item.durability_Q !== undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Get current durability (returns 1.0 if item has no durability tracking). */
|
|
26
|
+
export function getDurability(item) {
|
|
27
|
+
return item.durability_Q ?? SCALE.Q;
|
|
28
|
+
}
|
|
29
|
+
/** Check if item is damaged (below 50% durability). */
|
|
30
|
+
export function isDamaged(item) {
|
|
31
|
+
if (!hasDurability(item))
|
|
32
|
+
return false;
|
|
33
|
+
return (item.durability_Q ?? SCALE.Q) < DAMAGED_THRESHOLD_Q;
|
|
34
|
+
}
|
|
35
|
+
/** Check if item is broken (below 10% durability, non-functional). */
|
|
36
|
+
export function isBroken(item) {
|
|
37
|
+
if (!hasDurability(item))
|
|
38
|
+
return false;
|
|
39
|
+
return (item.durability_Q ?? SCALE.Q) < BROKEN_THRESHOLD_Q;
|
|
40
|
+
}
|
|
41
|
+
/** Get durability-based effectiveness multiplier for weapons. */
|
|
42
|
+
export function getWeaponEffectiveness(item) {
|
|
43
|
+
if (!hasDurability(item))
|
|
44
|
+
return SCALE.Q;
|
|
45
|
+
const d = item.durability_Q ?? SCALE.Q;
|
|
46
|
+
// Broken weapons deal minimal damage (10%)
|
|
47
|
+
if (d < BROKEN_THRESHOLD_Q) {
|
|
48
|
+
return q(0.10);
|
|
49
|
+
}
|
|
50
|
+
// Damaged weapons deal reduced damage (50-100%)
|
|
51
|
+
if (d < DAMAGED_THRESHOLD_Q) {
|
|
52
|
+
// Linear interpolation from 50% at broken threshold to 100% at damaged threshold
|
|
53
|
+
const t = (d - BROKEN_THRESHOLD_Q) / (DAMAGED_THRESHOLD_Q - BROKEN_THRESHOLD_Q);
|
|
54
|
+
return Math.round(q(0.50) + t * q(0.50));
|
|
55
|
+
}
|
|
56
|
+
// Pristine to damaged: full effectiveness
|
|
57
|
+
return SCALE.Q;
|
|
58
|
+
}
|
|
59
|
+
/** Get armour protection multiplier based on durability. */
|
|
60
|
+
export function getArmourProtection(item) {
|
|
61
|
+
if (!hasDurability(item))
|
|
62
|
+
return SCALE.Q;
|
|
63
|
+
const d = item.durability_Q ?? SCALE.Q;
|
|
64
|
+
// Broken armour provides no protection
|
|
65
|
+
if (d < BROKEN_THRESHOLD_Q) {
|
|
66
|
+
return q(0);
|
|
67
|
+
}
|
|
68
|
+
// Damaged armour provides partial protection
|
|
69
|
+
if (d < DAMAGED_THRESHOLD_Q) {
|
|
70
|
+
// Linear from 25% at broken to 100% at damaged threshold
|
|
71
|
+
const t = (d - BROKEN_THRESHOLD_Q) / (DAMAGED_THRESHOLD_Q - BROKEN_THRESHOLD_Q);
|
|
72
|
+
return Math.round(q(0.25) + t * q(0.75));
|
|
73
|
+
}
|
|
74
|
+
return SCALE.Q;
|
|
75
|
+
}
|
|
76
|
+
// ── Durability Modifications ───────────────────────────────────────────────────
|
|
77
|
+
/** Apply durability loss to an item. */
|
|
78
|
+
export function applyDurabilityLoss(item, loss_Q) {
|
|
79
|
+
if (!hasDurability(item))
|
|
80
|
+
return;
|
|
81
|
+
item.durability_Q = Math.max(0, (item.durability_Q ?? SCALE.Q) - loss_Q);
|
|
82
|
+
// Auto-apply "damaged" mod when crossing threshold
|
|
83
|
+
if (item.durability_Q < DAMAGED_THRESHOLD_Q && item.durability_Q >= BROKEN_THRESHOLD_Q) {
|
|
84
|
+
if (!item.modifications?.some((m) => m.type === "damaged")) {
|
|
85
|
+
applyItemMod(item, {
|
|
86
|
+
type: "damaged",
|
|
87
|
+
name: "Damaged",
|
|
88
|
+
statMultipliers: {
|
|
89
|
+
damageMul: q(0.80),
|
|
90
|
+
valueMul: q(0.50),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Record wear from parrying an attack. */
|
|
97
|
+
export function recordParryWear(item, impactEnergy_J) {
|
|
98
|
+
if (!hasDurability(item))
|
|
99
|
+
return;
|
|
100
|
+
// Scale durability loss by impact energy (larger impacts = more wear)
|
|
101
|
+
// Base: ~100J impact causes base loss, scale linearly
|
|
102
|
+
const energyScale = Math.min(3.0, impactEnergy_J / 100);
|
|
103
|
+
const loss = Math.round(DURABILITY_LOSS_PARRY * energyScale);
|
|
104
|
+
applyDurabilityLoss(item, loss);
|
|
105
|
+
}
|
|
106
|
+
/** Record wear from blocking with a weapon/shield. */
|
|
107
|
+
export function recordBlockWear(item, absorbedEnergy_J) {
|
|
108
|
+
if (!hasDurability(item))
|
|
109
|
+
return;
|
|
110
|
+
// Blocking causes more wear than parrying
|
|
111
|
+
const energyScale = Math.min(4.0, absorbedEnergy_J / 80);
|
|
112
|
+
const loss = Math.round(DURABILITY_LOSS_BLOCK_BASE * energyScale);
|
|
113
|
+
applyDurabilityLoss(item, loss);
|
|
114
|
+
}
|
|
115
|
+
/** Record wear from armour absorbing damage. */
|
|
116
|
+
export function recordArmourWear(item, absorbedEnergy_J) {
|
|
117
|
+
if (!hasDurability(item))
|
|
118
|
+
return;
|
|
119
|
+
// Armour takes significant wear from absorption
|
|
120
|
+
const energyScale = Math.min(5.0, absorbedEnergy_J / 50);
|
|
121
|
+
const loss = Math.round(DURABILITY_LOSS_ARMOUR_BASE * energyScale);
|
|
122
|
+
applyDurabilityLoss(item, loss);
|
|
123
|
+
}
|
|
124
|
+
/** Record wear from normal weapon strike (hitting armour, flesh, etc.). */
|
|
125
|
+
export function recordStrikeWear(item, hitArmour) {
|
|
126
|
+
if (!hasDurability(item))
|
|
127
|
+
return;
|
|
128
|
+
// Striking armour causes more wear than striking flesh
|
|
129
|
+
const multiplier = hitArmour ? 2.0 : 1.0;
|
|
130
|
+
const loss = Math.round(DURABILITY_LOSS_STRIKE * multiplier);
|
|
131
|
+
applyDurabilityLoss(item, loss);
|
|
132
|
+
}
|
|
133
|
+
/** Calculate repair parameters based on crafter skill and item condition. */
|
|
134
|
+
export function calculateRepairNeed(item) {
|
|
135
|
+
if (!hasDurability(item)) {
|
|
136
|
+
return { missingDurability_Q: q(0), baseTime_s: 0, baseMaterials: 0 };
|
|
137
|
+
}
|
|
138
|
+
const current = item.durability_Q ?? SCALE.Q;
|
|
139
|
+
const missing = (SCALE.Q - current);
|
|
140
|
+
// Base repair time scales with damage severity
|
|
141
|
+
// Minor repair: 5 minutes, Major repair: 30 minutes, Restoration: 2 hours
|
|
142
|
+
let baseTime_s = 300; // 5 minutes
|
|
143
|
+
if (current < BROKEN_THRESHOLD_Q) {
|
|
144
|
+
baseTime_s = 7200; // 2 hours for broken items
|
|
145
|
+
}
|
|
146
|
+
else if (current < DAMAGED_THRESHOLD_Q) {
|
|
147
|
+
baseTime_s = 1800; // 30 minutes for damaged items
|
|
148
|
+
}
|
|
149
|
+
// Materials scale with missing durability
|
|
150
|
+
const baseMaterials = Math.ceil(missing / q(0.10));
|
|
151
|
+
return {
|
|
152
|
+
missingDurability_Q: missing,
|
|
153
|
+
baseTime_s,
|
|
154
|
+
baseMaterials,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Resolve a repair attempt.
|
|
159
|
+
* @param item The item to repair
|
|
160
|
+
* @param crafterCognitionQ The crafter's cognition.logicalMath (for technical skill)
|
|
161
|
+
* @param toolQuality_Q Quality of tools being used (0-1)
|
|
162
|
+
* @param seed RNG seed for determinism
|
|
163
|
+
*/
|
|
164
|
+
export function resolveRepair(item, crafterCognitionQ, toolQuality_Q, seed) {
|
|
165
|
+
if (!hasDurability(item)) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
durabilityRestored_Q: q(0),
|
|
169
|
+
qualityLevel_Q: q(0),
|
|
170
|
+
repairCost: { time_s: 0, materials: 0 },
|
|
171
|
+
narrative: "This item cannot be repaired.",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const need = calculateRepairNeed(item);
|
|
175
|
+
if (need.missingDurability_Q <= 0) {
|
|
176
|
+
return {
|
|
177
|
+
success: true,
|
|
178
|
+
durabilityRestored_Q: q(0),
|
|
179
|
+
qualityLevel_Q: SCALE.Q,
|
|
180
|
+
repairCost: { time_s: 0, materials: 0 },
|
|
181
|
+
narrative: "Item is already in perfect condition.",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Quality of repair depends on skill and tools
|
|
185
|
+
// Base quality from skill (logicalMath represents technical competence)
|
|
186
|
+
const skillBonus = mulDiv(crafterCognitionQ, q(0.30), SCALE.Q);
|
|
187
|
+
const toolBonus = mulDiv(toolQuality_Q, q(0.20), SCALE.Q);
|
|
188
|
+
// Deterministic quality roll using seed
|
|
189
|
+
const qualityLevel_Q = clampQ((q(0.50) + skillBonus + toolBonus), q(0.30), SCALE.Q);
|
|
190
|
+
// Calculate actual durability restored
|
|
191
|
+
// Poor quality repairs restore less and may reduce max durability
|
|
192
|
+
const restoreRatio = 0.5 + (qualityLevel_Q / SCALE.Q) * 0.5;
|
|
193
|
+
const durabilityRestored_Q = Math.round(need.missingDurability_Q * restoreRatio);
|
|
194
|
+
// Calculate actual costs (better skill = more efficient)
|
|
195
|
+
const efficiencyMul = q(0.70) + mulDiv(crafterCognitionQ, q(0.30), SCALE.Q);
|
|
196
|
+
const time_s = Math.round(need.baseTime_s * (SCALE.Q / efficiencyMul));
|
|
197
|
+
const materials = Math.max(1, Math.round(need.baseMaterials * (SCALE.Q / efficiencyMul)));
|
|
198
|
+
// Apply repair
|
|
199
|
+
const newDurability = Math.min(SCALE.Q, (item.durability_Q ?? 0) + durabilityRestored_Q);
|
|
200
|
+
item.durability_Q = newDurability;
|
|
201
|
+
// Remove "damaged" mod if repaired above threshold
|
|
202
|
+
if (item.durability_Q >= DAMAGED_THRESHOLD_Q && item.modifications) {
|
|
203
|
+
const damagedIdx = item.modifications.findIndex((m) => m.type === "damaged");
|
|
204
|
+
if (damagedIdx >= 0) {
|
|
205
|
+
item.modifications.splice(damagedIdx, 1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Add "masterwork" mod for exceptional repairs
|
|
209
|
+
if (qualityLevel_Q > q(0.90) && !item.modifications?.some((m) => m.type === "masterwork")) {
|
|
210
|
+
applyItemMod(item, {
|
|
211
|
+
type: "masterwork",
|
|
212
|
+
name: "Masterwork",
|
|
213
|
+
statMultipliers: {
|
|
214
|
+
durabilityMul: q(1.10),
|
|
215
|
+
valueMul: q(1.25),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Generate narrative
|
|
220
|
+
let narrative = "Repair completed.";
|
|
221
|
+
if (qualityLevel_Q > q(0.85)) {
|
|
222
|
+
narrative = "Excellent repair. The item looks as good as new.";
|
|
223
|
+
}
|
|
224
|
+
else if (qualityLevel_Q > q(0.60)) {
|
|
225
|
+
narrative = "Good repair. The item is serviceable again.";
|
|
226
|
+
}
|
|
227
|
+
else if (qualityLevel_Q > q(0.40)) {
|
|
228
|
+
narrative = "Basic repair. The item functions but shows wear.";
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
narrative = "Poor repair. The item works but may not hold up long.";
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
durabilityRestored_Q,
|
|
236
|
+
qualityLevel_Q,
|
|
237
|
+
repairCost: { time_s, materials },
|
|
238
|
+
narrative,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** Quick repair with no skill check (field repair). */
|
|
242
|
+
export function fieldRepair(item) {
|
|
243
|
+
if (!hasDurability(item)) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
durabilityRestored_Q: q(0),
|
|
247
|
+
qualityLevel_Q: q(0),
|
|
248
|
+
repairCost: { time_s: 0, materials: 0 },
|
|
249
|
+
narrative: "Cannot field repair this item.",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const current = item.durability_Q ?? q(0);
|
|
253
|
+
const maxRestore = mulDiv(SCALE.Q - current, q(0.30), SCALE.Q); // Max 30% restore in field
|
|
254
|
+
if (maxRestore <= 0) {
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
durabilityRestored_Q: q(0),
|
|
258
|
+
qualityLevel_Q: SCALE.Q,
|
|
259
|
+
repairCost: { time_s: 0, materials: 0 },
|
|
260
|
+
narrative: "No repair needed.",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
item.durability_Q = (current + maxRestore);
|
|
264
|
+
// Field repairs add temporary "damaged" mod to indicate suboptimal fix
|
|
265
|
+
if (!item.modifications?.some((m) => m.type === "damaged")) {
|
|
266
|
+
applyItemMod(item, {
|
|
267
|
+
type: "damaged",
|
|
268
|
+
name: "Field Repaired",
|
|
269
|
+
statMultipliers: {
|
|
270
|
+
valueMul: q(0.80),
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
success: true,
|
|
276
|
+
durabilityRestored_Q: maxRestore,
|
|
277
|
+
qualityLevel_Q: q(0.40),
|
|
278
|
+
repairCost: { time_s: 300, materials: 1 }, // 5 minutes, minimal materials
|
|
279
|
+
narrative: "Field repair complete. Temporary fix, needs proper repair soon.",
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// ── Value Calculation ─────────────────────────────────────────────────────────
|
|
283
|
+
/** Calculate item value based on durability and modifications. */
|
|
284
|
+
export function calculateItemValue(baseValue, item) {
|
|
285
|
+
let multiplier = 1.0;
|
|
286
|
+
// Durability factor
|
|
287
|
+
if (hasDurability(item)) {
|
|
288
|
+
const d = item.durability_Q ?? SCALE.Q;
|
|
289
|
+
if (d < BROKEN_THRESHOLD_Q) {
|
|
290
|
+
multiplier *= 0.1; // 10% for broken
|
|
291
|
+
}
|
|
292
|
+
else if (d < DAMAGED_THRESHOLD_Q) {
|
|
293
|
+
multiplier *= 0.5; // 50% for damaged
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
multiplier *= d / SCALE.Q; // Linear for good condition
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Modification factors
|
|
300
|
+
if (item.modifications) {
|
|
301
|
+
for (const mod of item.modifications) {
|
|
302
|
+
if (mod.statMultipliers?.valueMul) {
|
|
303
|
+
multiplier *= mod.statMultipliers.valueMul / SCALE.Q;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return Math.round(baseValue * multiplier);
|
|
308
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Q } from "./units.js";
|
|
2
|
+
import type { Chronicle, StoryArcType } from "./chronicle.js";
|
|
3
|
+
/** How an entity is perceived in living memory. */
|
|
4
|
+
export type LegendReputation = "legendary" | "heroic" | "notorious" | "forgotten";
|
|
5
|
+
/** A legend crystallised from chronicle entries. */
|
|
6
|
+
export interface Legend {
|
|
7
|
+
legendId: string;
|
|
8
|
+
/** Entity the legend is about. */
|
|
9
|
+
subjectId: number;
|
|
10
|
+
/** Display name frozen at legend creation. */
|
|
11
|
+
subjectName: string;
|
|
12
|
+
reputation: LegendReputation;
|
|
13
|
+
/**
|
|
14
|
+
* How widely known this legend is (0..1).
|
|
15
|
+
* q(1.0) = universally known; q(0.10) = forgotten by most.
|
|
16
|
+
* Decays over time via stepLegendFame.
|
|
17
|
+
*/
|
|
18
|
+
fame_Q: Q;
|
|
19
|
+
/**
|
|
20
|
+
* Thematic tags derived from source events/arcs.
|
|
21
|
+
* Examples: "warrior", "craftsman", "hero", "villain", "legendary_deed".
|
|
22
|
+
*/
|
|
23
|
+
tags: string[];
|
|
24
|
+
/** Chronicle entry IDs that built this legend. */
|
|
25
|
+
sourceEntryIds: string[];
|
|
26
|
+
/** Story arc types present in source chronicle. */
|
|
27
|
+
sourceArcTypes: StoryArcType[];
|
|
28
|
+
/** Tick when this legend was created. */
|
|
29
|
+
createdAtTick: number;
|
|
30
|
+
/** Optional lore text for display. */
|
|
31
|
+
lore?: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Modifiers that a legend applies to NPC interactions. */
|
|
34
|
+
export interface LegendEffect {
|
|
35
|
+
/** Bonus to persuasion probability (heroic / legendary). */
|
|
36
|
+
persuasionBonus_Q: Q;
|
|
37
|
+
/** Bonus to intimidation probability (notorious / legendary). */
|
|
38
|
+
intimidationBonus_Q: Q;
|
|
39
|
+
/** Fear delta applied to NPCs who know a notorious / legendary figure. */
|
|
40
|
+
fearBonus_Q: Q;
|
|
41
|
+
/** Ally morale bonus from fighting alongside / for a legend (heroic / legendary). */
|
|
42
|
+
moraleBonus_Q: Q;
|
|
43
|
+
}
|
|
44
|
+
/** Registry of all legends in a world. */
|
|
45
|
+
export interface LegendRegistry {
|
|
46
|
+
legends: Map<string, Legend>;
|
|
47
|
+
/** Index: subjectId → Set of legendIds */
|
|
48
|
+
bySubject: Map<number, Set<string>>;
|
|
49
|
+
}
|
|
50
|
+
/** Minimum significance score for a chronicle entry to contribute to a legend. */
|
|
51
|
+
export declare const LEGEND_MIN_SIGNIFICANCE = 60;
|
|
52
|
+
/**
|
|
53
|
+
* Promote chronicle entries above `minSignificance` into a Legend.
|
|
54
|
+
*
|
|
55
|
+
* Returns `undefined` if no qualifying entries exist.
|
|
56
|
+
* Considers only entries where `subjectId` appears in the actors array.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createLegendFromChronicle(chronicle: Chronicle, subjectId: number, subjectName: string, minSignificance?: number): Legend | undefined;
|
|
59
|
+
/** Create a new empty legend registry. */
|
|
60
|
+
export declare function createLegendRegistry(): LegendRegistry;
|
|
61
|
+
/** Register a legend in the registry. Overwrites any existing legend with the same ID. */
|
|
62
|
+
export declare function registerLegend(registry: LegendRegistry, legend: Legend): void;
|
|
63
|
+
/** Get all legends about a specific entity. */
|
|
64
|
+
export declare function getLegendsBySubject(registry: LegendRegistry, subjectId: number): Legend[];
|
|
65
|
+
/** Derive NPC-behavior modifiers from a legend. */
|
|
66
|
+
export declare function getLegendEffect(legend: Legend): LegendEffect;
|
|
67
|
+
/**
|
|
68
|
+
* Determine whether an NPC "knows" a legend.
|
|
69
|
+
*
|
|
70
|
+
* Deterministic: same (legend, npcId, worldSeed, tick) → same result.
|
|
71
|
+
* Probability = `legend.fame_Q / SCALE.Q`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function npcKnowsLegend(legend: Legend, npcId: number, worldSeed: number, tick: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Aggregate legend effects for initiatorId, filtered by what targetId (NPC) knows.
|
|
76
|
+
*
|
|
77
|
+
* Called before dialogue resolution to get bonus Q values that shift
|
|
78
|
+
* persuasion/intimidation/fear probabilities.
|
|
79
|
+
*/
|
|
80
|
+
export declare function applyLegendToDialogueContext(initiatorId: number, targetId: number, registry: LegendRegistry, worldSeed: number, tick: number): {
|
|
81
|
+
persuasionBonus_Q: Q;
|
|
82
|
+
intimidationBonus_Q: Q;
|
|
83
|
+
fearBonus_Q: Q;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Decay fame on all registered legends over `deltaTicks` time.
|
|
87
|
+
*
|
|
88
|
+
* - "legendary" reputation has a hard floor at q(0.50).
|
|
89
|
+
* - All other reputations decay freely.
|
|
90
|
+
* - Legends whose fame_Q falls below FORGOTTEN_FAME_THRESHOLD are reclassified as "forgotten".
|
|
91
|
+
* - fame_Q never goes below 0.
|
|
92
|
+
*/
|
|
93
|
+
export declare function stepLegendFame(registry: LegendRegistry, deltaTicks: number): void;
|
|
94
|
+
/** Serialize legend registry to JSON-friendly object. */
|
|
95
|
+
export declare function serializeLegendRegistry(registry: LegendRegistry): unknown;
|
|
96
|
+
/** Deserialize legend registry. */
|
|
97
|
+
export declare function deserializeLegendRegistry(data: unknown): LegendRegistry;
|