@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,637 @@
|
|
|
1
|
+
// src/inventory.ts — Phase 43: Deep Inventory & Encumbrance
|
|
2
|
+
//
|
|
3
|
+
// Container-based inventory system with nested storage, weight tracking,
|
|
4
|
+
// and encumbrance penalties derived from physical carrying capacity.
|
|
5
|
+
import { q, qMul, clampQ, SCALE, mulDiv } from "./units.js";
|
|
6
|
+
// Category thresholds and penalties
|
|
7
|
+
export const ENCUMBRANCE_CATEGORIES = [
|
|
8
|
+
{
|
|
9
|
+
category: "unencumbered",
|
|
10
|
+
maxRatio: 0.30,
|
|
11
|
+
penalties: {
|
|
12
|
+
fineControlPenalty_Q: q(0),
|
|
13
|
+
dodgeParryLatencyMul: q(1.0),
|
|
14
|
+
speedMul: q(1.0),
|
|
15
|
+
noSprint: false,
|
|
16
|
+
noMove: false,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
category: "light",
|
|
21
|
+
maxRatio: 0.50,
|
|
22
|
+
penalties: {
|
|
23
|
+
fineControlPenalty_Q: q(0.05),
|
|
24
|
+
dodgeParryLatencyMul: q(1.0),
|
|
25
|
+
speedMul: q(0.95),
|
|
26
|
+
noSprint: false,
|
|
27
|
+
noMove: false,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
category: "medium",
|
|
32
|
+
maxRatio: 0.75,
|
|
33
|
+
penalties: {
|
|
34
|
+
fineControlPenalty_Q: q(0.10),
|
|
35
|
+
dodgeParryLatencyMul: q(1.20),
|
|
36
|
+
speedMul: q(0.90),
|
|
37
|
+
noSprint: false,
|
|
38
|
+
noMove: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
category: "heavy",
|
|
43
|
+
maxRatio: 1.00,
|
|
44
|
+
penalties: {
|
|
45
|
+
fineControlPenalty_Q: q(0.15),
|
|
46
|
+
dodgeParryLatencyMul: q(1.30),
|
|
47
|
+
speedMul: q(0.75),
|
|
48
|
+
noSprint: true,
|
|
49
|
+
noMove: false,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
category: "overloaded",
|
|
54
|
+
maxRatio: Infinity,
|
|
55
|
+
penalties: {
|
|
56
|
+
fineControlPenalty_Q: q(0.30),
|
|
57
|
+
dodgeParryLatencyMul: q(1.50),
|
|
58
|
+
speedMul: q(0),
|
|
59
|
+
noSprint: true,
|
|
60
|
+
noMove: true,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
// ── Inventory Creation ────────────────────────────────────────────────────────
|
|
65
|
+
/** Create an empty inventory for an entity. */
|
|
66
|
+
export function createInventory(ownerId) {
|
|
67
|
+
return {
|
|
68
|
+
ownerId,
|
|
69
|
+
containers: [],
|
|
70
|
+
equipped: {
|
|
71
|
+
containers: new Map(),
|
|
72
|
+
},
|
|
73
|
+
encumbrance_Kg: 0,
|
|
74
|
+
maxEncumbrance_Kg: 0,
|
|
75
|
+
currency: 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Create a new container. */
|
|
79
|
+
export function createContainer(containerId, name, capacity_Kg, emptyMass_kg, volume_L) {
|
|
80
|
+
return {
|
|
81
|
+
containerId,
|
|
82
|
+
name,
|
|
83
|
+
capacity_Kg,
|
|
84
|
+
volume_L,
|
|
85
|
+
items: [],
|
|
86
|
+
isEquipped: false,
|
|
87
|
+
emptyMass_kg,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// ── Max Encumbrance Calculation ───────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Calculate maximum encumbrance based on physical strength.
|
|
93
|
+
* Stronger characters can carry more absolute weight.
|
|
94
|
+
*/
|
|
95
|
+
export function calculateMaxEncumbrance_Kg(attributes, capacityFactor = q(0.5)) {
|
|
96
|
+
// Base: peakForce_N relates to lifting/carrying capacity
|
|
97
|
+
// A typical human (1800N peak force) can carry ~50kg at 50% factor
|
|
98
|
+
const peakForce_N = attributes.performance.peakForce_N;
|
|
99
|
+
// Convert force to mass: F = mg → m = F/g
|
|
100
|
+
// Using BigInt for precision: result in kg (scaled by SCALE.kg)
|
|
101
|
+
const numerator = BigInt(peakForce_N) * BigInt(capacityFactor) * BigInt(SCALE.kg);
|
|
102
|
+
const denom = BigInt(SCALE.N) * BigInt(SCALE.Q) * 9810n; // g ≈ 9.81 m/s²
|
|
103
|
+
const maxKg = Number(numerator / denom);
|
|
104
|
+
return Math.max(1, maxKg); // At least 1kg capacity
|
|
105
|
+
}
|
|
106
|
+
// ── Weight Calculations ───────────────────────────────────────────────────────
|
|
107
|
+
/** Calculate total weight of items in a container (including container itself). */
|
|
108
|
+
export function calculateContainerWeight(container) {
|
|
109
|
+
let total = container.emptyMass_kg;
|
|
110
|
+
for (const item of container.items) {
|
|
111
|
+
total += getItemInstanceMass(item);
|
|
112
|
+
}
|
|
113
|
+
return total;
|
|
114
|
+
}
|
|
115
|
+
/** Get mass of a single item instance (including modifications). */
|
|
116
|
+
export function getItemInstanceMass(item) {
|
|
117
|
+
// Base mass would come from template lookup; here we use a placeholder
|
|
118
|
+
// In practice, this would reference the equipment catalogue
|
|
119
|
+
let mass = item.quantity; // Simplified: assume 1kg per quantity unit
|
|
120
|
+
// Apply weight modifications
|
|
121
|
+
if (item.modifications) {
|
|
122
|
+
for (const mod of item.modifications) {
|
|
123
|
+
if (mod.statMultipliers?.weightMul) {
|
|
124
|
+
mass = mulDiv(mass * SCALE.Q, mod.statMultipliers.weightMul, SCALE.Q);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return mass;
|
|
129
|
+
}
|
|
130
|
+
/** Calculate total encumbrance for an inventory. */
|
|
131
|
+
export function calculateTotalEncumbrance(inventory) {
|
|
132
|
+
let total = 0;
|
|
133
|
+
// Add equipped items weight
|
|
134
|
+
if (inventory.equipped.mainHand) {
|
|
135
|
+
total += getItemInstanceMass(inventory.equipped.mainHand);
|
|
136
|
+
}
|
|
137
|
+
if (inventory.equipped.offHand) {
|
|
138
|
+
total += getItemInstanceMass(inventory.equipped.offHand);
|
|
139
|
+
}
|
|
140
|
+
if (inventory.equipped.body) {
|
|
141
|
+
total += getItemInstanceMass(inventory.equipped.body);
|
|
142
|
+
}
|
|
143
|
+
if (inventory.equipped.head) {
|
|
144
|
+
total += getItemInstanceMass(inventory.equipped.head);
|
|
145
|
+
}
|
|
146
|
+
// Add equipped containers and their contents
|
|
147
|
+
for (const container of inventory.containers) {
|
|
148
|
+
if (container.isEquipped) {
|
|
149
|
+
total += calculateContainerWeight(container);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return total;
|
|
153
|
+
}
|
|
154
|
+
/** Recalculate encumbrance and update inventory. */
|
|
155
|
+
export function recalculateEncumbrance(inventory, attributes) {
|
|
156
|
+
inventory.encumbrance_Kg = calculateTotalEncumbrance(inventory);
|
|
157
|
+
inventory.maxEncumbrance_Kg = calculateMaxEncumbrance_Kg(attributes);
|
|
158
|
+
}
|
|
159
|
+
// ── Encumbrance Category ──────────────────────────────────────────────────────
|
|
160
|
+
/** Get the encumbrance category and penalties for current load. */
|
|
161
|
+
export function getEncumbranceCategory(inventory) {
|
|
162
|
+
if (inventory.maxEncumbrance_Kg <= 0) {
|
|
163
|
+
return ENCUMBRANCE_CATEGORIES[4]; // Overloaded if no capacity
|
|
164
|
+
}
|
|
165
|
+
const ratio = inventory.encumbrance_Kg / inventory.maxEncumbrance_Kg;
|
|
166
|
+
for (const category of ENCUMBRANCE_CATEGORIES) {
|
|
167
|
+
if (ratio < category.maxRatio) {
|
|
168
|
+
return category;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return ENCUMBRANCE_CATEGORIES[ENCUMBRANCE_CATEGORIES.length - 1];
|
|
172
|
+
}
|
|
173
|
+
/** Get effective encumbrance penalties (combines with any existing penalties). */
|
|
174
|
+
export function getEffectiveEncumbrancePenalties(inventory, baseFineControl) {
|
|
175
|
+
const categoryDef = getEncumbranceCategory(inventory);
|
|
176
|
+
// Calculate effective fine control after penalty
|
|
177
|
+
const effectiveFineControl = clampQ((baseFineControl - categoryDef.penalties.fineControlPenalty_Q), q(0.01), SCALE.Q);
|
|
178
|
+
return {
|
|
179
|
+
category: categoryDef.category,
|
|
180
|
+
penalties: categoryDef.penalties,
|
|
181
|
+
effectiveFineControl,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// ── Container Operations ───────────────────────────────────────────────────────
|
|
185
|
+
/** Add a container to inventory. */
|
|
186
|
+
export function addContainer(inventory, container) {
|
|
187
|
+
inventory.containers.push(container);
|
|
188
|
+
}
|
|
189
|
+
/** Remove a container from inventory. */
|
|
190
|
+
export function removeContainer(inventory, containerId) {
|
|
191
|
+
const idx = inventory.containers.findIndex((c) => c.containerId === containerId);
|
|
192
|
+
if (idx >= 0) {
|
|
193
|
+
inventory.containers.splice(idx, 1);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
/** Equip/unequip a container. */
|
|
199
|
+
export function setContainerEquipped(inventory, containerId, equipped) {
|
|
200
|
+
const container = inventory.containers.find((c) => c.containerId === containerId);
|
|
201
|
+
if (!container)
|
|
202
|
+
return false;
|
|
203
|
+
container.isEquipped = equipped;
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
/** Find a container by ID. */
|
|
207
|
+
export function findContainer(inventory, containerId) {
|
|
208
|
+
return inventory.containers.find((c) => c.containerId === containerId);
|
|
209
|
+
}
|
|
210
|
+
// ── Item Operations ────────────────────────────────────────────────────────────
|
|
211
|
+
/** Add an item to a specific container. */
|
|
212
|
+
export function addItemToContainer(container, item) {
|
|
213
|
+
// Check weight capacity
|
|
214
|
+
const itemWeight = getItemInstanceMass(item);
|
|
215
|
+
const currentWeight = calculateContainerWeight(container) - container.emptyMass_kg;
|
|
216
|
+
if (currentWeight + itemWeight > container.capacity_Kg) {
|
|
217
|
+
return { success: false, reason: "exceeds_capacity" };
|
|
218
|
+
}
|
|
219
|
+
container.items.push(item);
|
|
220
|
+
return { success: true };
|
|
221
|
+
}
|
|
222
|
+
/** Remove an item from a container by instance ID. */
|
|
223
|
+
export function removeItemFromContainer(container, instanceId) {
|
|
224
|
+
const idx = container.items.findIndex((i) => i.instanceId === instanceId);
|
|
225
|
+
if (idx >= 0) {
|
|
226
|
+
return container.items.splice(idx, 1)[0];
|
|
227
|
+
}
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
/** Find an item anywhere in the inventory. */
|
|
231
|
+
export function findItem(inventory, instanceId) {
|
|
232
|
+
// Check equipped items
|
|
233
|
+
const checkEquipped = (item) => {
|
|
234
|
+
if (item?.instanceId === instanceId) {
|
|
235
|
+
return { item, container: null };
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
};
|
|
239
|
+
let result = checkEquipped(inventory.equipped.mainHand);
|
|
240
|
+
if (result)
|
|
241
|
+
return result;
|
|
242
|
+
result = checkEquipped(inventory.equipped.offHand);
|
|
243
|
+
if (result)
|
|
244
|
+
return result;
|
|
245
|
+
result = checkEquipped(inventory.equipped.body);
|
|
246
|
+
if (result)
|
|
247
|
+
return result;
|
|
248
|
+
result = checkEquipped(inventory.equipped.head);
|
|
249
|
+
if (result)
|
|
250
|
+
return result;
|
|
251
|
+
// Check containers
|
|
252
|
+
for (const container of inventory.containers) {
|
|
253
|
+
const item = container.items.find((i) => i.instanceId === instanceId);
|
|
254
|
+
if (item) {
|
|
255
|
+
return { item, container };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
/** Move an item between containers. */
|
|
261
|
+
export function moveItem(inventory, itemId, fromContainerId, // null = equipped
|
|
262
|
+
toContainerId) {
|
|
263
|
+
// Find source
|
|
264
|
+
let item;
|
|
265
|
+
let fromContainer;
|
|
266
|
+
if (fromContainerId === null) {
|
|
267
|
+
// Check equipped items - simplified, would need full equipped item removal
|
|
268
|
+
return { success: false, reason: "unequip_first" };
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
fromContainer = findContainer(inventory, fromContainerId);
|
|
272
|
+
if (!fromContainer)
|
|
273
|
+
return { success: false, reason: "source_not_found" };
|
|
274
|
+
item = fromContainer.items.find((i) => i.instanceId === itemId);
|
|
275
|
+
}
|
|
276
|
+
if (!item)
|
|
277
|
+
return { success: false, reason: "item_not_found" };
|
|
278
|
+
// Find destination
|
|
279
|
+
const toContainer = findContainer(inventory, toContainerId);
|
|
280
|
+
if (!toContainer)
|
|
281
|
+
return { success: false, reason: "destination_not_found" };
|
|
282
|
+
// Check destination capacity
|
|
283
|
+
const itemWeight = getItemInstanceMass(item);
|
|
284
|
+
const destCurrentWeight = calculateContainerWeight(toContainer) - toContainer.emptyMass_kg;
|
|
285
|
+
if (destCurrentWeight + itemWeight > toContainer.capacity_Kg) {
|
|
286
|
+
return { success: false, reason: "destination_full" };
|
|
287
|
+
}
|
|
288
|
+
// Move
|
|
289
|
+
if (fromContainer) {
|
|
290
|
+
removeItemFromContainer(fromContainer, itemId);
|
|
291
|
+
}
|
|
292
|
+
toContainer.items.push(item);
|
|
293
|
+
return { success: true };
|
|
294
|
+
}
|
|
295
|
+
/** Equip an item from a container to an equipment slot. */
|
|
296
|
+
export function equipItem(inventory, itemId, slot) {
|
|
297
|
+
// Find item
|
|
298
|
+
const found = findItem(inventory, itemId);
|
|
299
|
+
if (!found)
|
|
300
|
+
return { success: false, reason: "item_not_found" };
|
|
301
|
+
// Remove from container if not already equipped
|
|
302
|
+
if (found.container) {
|
|
303
|
+
removeItemFromContainer(found.container, itemId);
|
|
304
|
+
}
|
|
305
|
+
// Store previous item
|
|
306
|
+
const previousItem = inventory.equipped[slot];
|
|
307
|
+
// Equip new item
|
|
308
|
+
inventory.equipped[slot] = found.item;
|
|
309
|
+
// Put previous item back in first equipped container if possible
|
|
310
|
+
if (previousItem) {
|
|
311
|
+
const equippedContainer = inventory.containers.find((c) => c.isEquipped);
|
|
312
|
+
if (equippedContainer) {
|
|
313
|
+
addItemToContainer(equippedContainer, previousItem);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return { success: true, previousItem };
|
|
317
|
+
}
|
|
318
|
+
/** Unequip an item and return it to a container. */
|
|
319
|
+
export function unequipItem(inventory, slot, targetContainerId) {
|
|
320
|
+
const item = inventory.equipped[slot];
|
|
321
|
+
if (!item)
|
|
322
|
+
return { success: false, reason: "nothing_equipped" };
|
|
323
|
+
// Find destination container
|
|
324
|
+
let container;
|
|
325
|
+
if (targetContainerId) {
|
|
326
|
+
container = findContainer(inventory, targetContainerId);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
container = inventory.containers.find((c) => c.isEquipped);
|
|
330
|
+
}
|
|
331
|
+
if (!container)
|
|
332
|
+
return { success: false, reason: "no_container" };
|
|
333
|
+
// Check capacity
|
|
334
|
+
const result = addItemToContainer(container, item);
|
|
335
|
+
if (!result.success) {
|
|
336
|
+
return { success: false, reason: result.reason };
|
|
337
|
+
}
|
|
338
|
+
// Remove from equipped
|
|
339
|
+
delete inventory.equipped[slot];
|
|
340
|
+
return { success: true, item };
|
|
341
|
+
}
|
|
342
|
+
// ── Item Query Functions ─────────────────────────────────────────────────────
|
|
343
|
+
/**
|
|
344
|
+
* Count items in inventory by template ID.
|
|
345
|
+
* Searches all containers and equipped items.
|
|
346
|
+
*/
|
|
347
|
+
export function getItemCountByTemplateId(inventory, templateId) {
|
|
348
|
+
let count = 0;
|
|
349
|
+
// Check equipped items
|
|
350
|
+
const checkEquipped = (item) => {
|
|
351
|
+
if (item?.templateId === templateId) {
|
|
352
|
+
count += item.quantity;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
checkEquipped(inventory.equipped.mainHand);
|
|
356
|
+
checkEquipped(inventory.equipped.offHand);
|
|
357
|
+
checkEquipped(inventory.equipped.body);
|
|
358
|
+
checkEquipped(inventory.equipped.head);
|
|
359
|
+
for (const item of inventory.equipped.containers.values()) {
|
|
360
|
+
if (item.templateId === templateId) {
|
|
361
|
+
count += item.quantity;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Check containers
|
|
365
|
+
for (const container of inventory.containers) {
|
|
366
|
+
for (const item of container.items) {
|
|
367
|
+
if (item.templateId === templateId) {
|
|
368
|
+
count += item.quantity;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return count;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Find all material items of a specific material type.
|
|
376
|
+
* Returns an array of Material items (requires kind "material" and materialTypeId).
|
|
377
|
+
* Note: This assumes ItemInstance can be cast to Material if kind === "material".
|
|
378
|
+
* The caller must ensure the item is a material.
|
|
379
|
+
*/
|
|
380
|
+
export function findMaterialsByType(inventory, materialTypeId) {
|
|
381
|
+
const results = [];
|
|
382
|
+
// Helper to check and add
|
|
383
|
+
const checkItem = (item) => {
|
|
384
|
+
// We need to examine the item's materialTypeId property.
|
|
385
|
+
// Since ItemInstance doesn't have materialTypeId, we need to rely on the templateId mapping
|
|
386
|
+
// or assume that the item is a Material (which extends ItemBase).
|
|
387
|
+
// For now, we'll assume that templateId indicates material type (e.g., "material_iron").
|
|
388
|
+
// This is a placeholder; we need to integrate with crafting material system.
|
|
389
|
+
if (item.templateId.startsWith("material_") && item.templateId.includes(materialTypeId)) {
|
|
390
|
+
results.push(item);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
// Check equipped items
|
|
394
|
+
if (inventory.equipped.mainHand)
|
|
395
|
+
checkItem(inventory.equipped.mainHand);
|
|
396
|
+
if (inventory.equipped.offHand)
|
|
397
|
+
checkItem(inventory.equipped.offHand);
|
|
398
|
+
if (inventory.equipped.body)
|
|
399
|
+
checkItem(inventory.equipped.body);
|
|
400
|
+
if (inventory.equipped.head)
|
|
401
|
+
checkItem(inventory.equipped.head);
|
|
402
|
+
for (const item of inventory.equipped.containers.values()) {
|
|
403
|
+
checkItem(item);
|
|
404
|
+
}
|
|
405
|
+
// Check containers
|
|
406
|
+
for (const container of inventory.containers) {
|
|
407
|
+
for (const item of container.items) {
|
|
408
|
+
checkItem(item);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return results;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Consume items by template ID, removing them from inventory.
|
|
415
|
+
* Returns true if enough items were found and consumed.
|
|
416
|
+
*/
|
|
417
|
+
export function consumeItemsByTemplateId(inventory, templateId, quantity) {
|
|
418
|
+
if (quantity <= 0)
|
|
419
|
+
return true;
|
|
420
|
+
let remaining = quantity;
|
|
421
|
+
// Helper to consume from an item instance (mutates item.quantity)
|
|
422
|
+
const consumeFromItem = (item) => {
|
|
423
|
+
if (item.templateId === templateId) {
|
|
424
|
+
const take = Math.min(item.quantity, remaining);
|
|
425
|
+
item.quantity -= take;
|
|
426
|
+
remaining -= take;
|
|
427
|
+
return remaining === 0;
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
};
|
|
431
|
+
// First, try equipped containers (they might be material items)
|
|
432
|
+
for (const container of inventory.containers) {
|
|
433
|
+
if (!container.isEquipped)
|
|
434
|
+
continue;
|
|
435
|
+
for (let i = 0; i < container.items.length; i++) {
|
|
436
|
+
const item = container.items[i];
|
|
437
|
+
if (consumeFromItem(item)) {
|
|
438
|
+
// Remove item if quantity zero
|
|
439
|
+
if (item.quantity === 0) {
|
|
440
|
+
container.items.splice(i, 1);
|
|
441
|
+
}
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
if (item.quantity === 0) {
|
|
445
|
+
container.items.splice(i, 1);
|
|
446
|
+
i--;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Then try any container
|
|
451
|
+
for (const container of inventory.containers) {
|
|
452
|
+
for (let i = 0; i < container.items.length; i++) {
|
|
453
|
+
const item = container.items[i];
|
|
454
|
+
if (consumeFromItem(item)) {
|
|
455
|
+
if (item.quantity === 0) {
|
|
456
|
+
container.items.splice(i, 1);
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
if (item.quantity === 0) {
|
|
461
|
+
container.items.splice(i, 1);
|
|
462
|
+
i--;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Finally, try equipped items (unlikely for materials)
|
|
467
|
+
const equippedItems = [
|
|
468
|
+
inventory.equipped.mainHand,
|
|
469
|
+
inventory.equipped.offHand,
|
|
470
|
+
inventory.equipped.body,
|
|
471
|
+
inventory.equipped.head,
|
|
472
|
+
];
|
|
473
|
+
for (const item of equippedItems) {
|
|
474
|
+
if (item && consumeFromItem(item)) {
|
|
475
|
+
// Unequip if quantity zero? Probably not, but we can just leave it equipped with zero quantity.
|
|
476
|
+
// For simplicity, we'll ignore zero quantity equipped items.
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return remaining === 0;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Add an item to inventory, attempting to place it in a suitable container.
|
|
484
|
+
* Prefers equipped containers with sufficient capacity.
|
|
485
|
+
* Returns success and the container it was added to (or null if equipped).
|
|
486
|
+
*/
|
|
487
|
+
export function addItemToInventory(inventory, item) {
|
|
488
|
+
// Try equipped containers first
|
|
489
|
+
for (const container of inventory.containers) {
|
|
490
|
+
if (container.isEquipped) {
|
|
491
|
+
const result = addItemToContainer(container, item);
|
|
492
|
+
if (result.success) {
|
|
493
|
+
return { success: true, container };
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// Try any container
|
|
498
|
+
for (const container of inventory.containers) {
|
|
499
|
+
const result = addItemToContainer(container, item);
|
|
500
|
+
if (result.success) {
|
|
501
|
+
return { success: true, container };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// No suitable container found
|
|
505
|
+
return { success: false, container: null, reason: "no_capacity" };
|
|
506
|
+
}
|
|
507
|
+
// ── Item Modifications ────────────────────────────────────────────────────────
|
|
508
|
+
/** Apply a modification to an item. */
|
|
509
|
+
export function applyItemMod(item, mod) {
|
|
510
|
+
if (!item.modifications) {
|
|
511
|
+
item.modifications = [];
|
|
512
|
+
}
|
|
513
|
+
item.modifications.push(mod);
|
|
514
|
+
}
|
|
515
|
+
/** Remove a modification from an item. */
|
|
516
|
+
export function removeItemMod(item, modType) {
|
|
517
|
+
if (!item.modifications)
|
|
518
|
+
return false;
|
|
519
|
+
const idx = item.modifications.findIndex((m) => m.type === modType);
|
|
520
|
+
if (idx >= 0) {
|
|
521
|
+
item.modifications.splice(idx, 1);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
/** Get effective stat multiplier from all modifications. */
|
|
527
|
+
export function getItemStatMultiplier(item, stat) {
|
|
528
|
+
if (!item.modifications || item.modifications.length === 0) {
|
|
529
|
+
return SCALE.Q; // 1.0
|
|
530
|
+
}
|
|
531
|
+
let multiplier = SCALE.Q;
|
|
532
|
+
for (const mod of item.modifications) {
|
|
533
|
+
if (mod.statMultipliers?.[stat]) {
|
|
534
|
+
multiplier = qMul(multiplier, mod.statMultipliers[stat]);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return multiplier;
|
|
538
|
+
}
|
|
539
|
+
// ── Serialization ─────────────────────────────────────────────────────────────
|
|
540
|
+
/** Serialize inventory to JSON-friendly format. */
|
|
541
|
+
export function serializeInventory(inventory) {
|
|
542
|
+
return {
|
|
543
|
+
ownerId: inventory.ownerId,
|
|
544
|
+
containers: inventory.containers.map((c) => ({
|
|
545
|
+
containerId: c.containerId,
|
|
546
|
+
name: c.name,
|
|
547
|
+
capacity_Kg: c.capacity_Kg,
|
|
548
|
+
volume_L: c.volume_L,
|
|
549
|
+
items: c.items.map(serializeItemInstance),
|
|
550
|
+
isEquipped: c.isEquipped,
|
|
551
|
+
emptyMass_kg: c.emptyMass_kg,
|
|
552
|
+
})),
|
|
553
|
+
equipped: {
|
|
554
|
+
mainHand: inventory.equipped.mainHand
|
|
555
|
+
? serializeItemInstance(inventory.equipped.mainHand)
|
|
556
|
+
: undefined,
|
|
557
|
+
offHand: inventory.equipped.offHand
|
|
558
|
+
? serializeItemInstance(inventory.equipped.offHand)
|
|
559
|
+
: undefined,
|
|
560
|
+
body: inventory.equipped.body
|
|
561
|
+
? serializeItemInstance(inventory.equipped.body)
|
|
562
|
+
: undefined,
|
|
563
|
+
head: inventory.equipped.head
|
|
564
|
+
? serializeItemInstance(inventory.equipped.head)
|
|
565
|
+
: undefined,
|
|
566
|
+
containers: Array.from(inventory.equipped.containers.entries()).map(([slot, item]) => [slot, serializeItemInstance(item)]),
|
|
567
|
+
},
|
|
568
|
+
encumbrance_Kg: inventory.encumbrance_Kg,
|
|
569
|
+
maxEncumbrance_Kg: inventory.maxEncumbrance_Kg,
|
|
570
|
+
currency: inventory.currency,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function serializeItemInstance(item) {
|
|
574
|
+
return {
|
|
575
|
+
instanceId: item.instanceId,
|
|
576
|
+
templateId: item.templateId,
|
|
577
|
+
quantity: item.quantity,
|
|
578
|
+
durability_Q: item.durability_Q,
|
|
579
|
+
modifications: item.modifications,
|
|
580
|
+
containerPath: item.containerPath,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/** Deserialize inventory. */
|
|
584
|
+
export function deserializeInventory(data) {
|
|
585
|
+
const d = data;
|
|
586
|
+
const inventory = {
|
|
587
|
+
ownerId: d.ownerId ?? 0,
|
|
588
|
+
containers: [],
|
|
589
|
+
equipped: {
|
|
590
|
+
containers: new Map(),
|
|
591
|
+
},
|
|
592
|
+
encumbrance_Kg: d.encumbrance_Kg ?? 0,
|
|
593
|
+
maxEncumbrance_Kg: d.maxEncumbrance_Kg ?? 0,
|
|
594
|
+
currency: d.currency ?? 0,
|
|
595
|
+
};
|
|
596
|
+
if (Array.isArray(d.containers)) {
|
|
597
|
+
inventory.containers = d.containers.map((c) => ({
|
|
598
|
+
containerId: c.containerId,
|
|
599
|
+
name: c.name,
|
|
600
|
+
capacity_Kg: c.capacity_Kg,
|
|
601
|
+
volume_L: c.volume_L,
|
|
602
|
+
items: Array.isArray(c.items)
|
|
603
|
+
? c.items.map(deserializeItemInstance)
|
|
604
|
+
: [],
|
|
605
|
+
isEquipped: Boolean(c.isEquipped),
|
|
606
|
+
emptyMass_kg: c.emptyMass_kg ?? 0,
|
|
607
|
+
}));
|
|
608
|
+
}
|
|
609
|
+
if (d.equipped && typeof d.equipped === "object") {
|
|
610
|
+
const e = d.equipped;
|
|
611
|
+
if (e.mainHand)
|
|
612
|
+
inventory.equipped.mainHand = deserializeItemInstance(e.mainHand);
|
|
613
|
+
if (e.offHand)
|
|
614
|
+
inventory.equipped.offHand = deserializeItemInstance(e.offHand);
|
|
615
|
+
if (e.body)
|
|
616
|
+
inventory.equipped.body = deserializeItemInstance(e.body);
|
|
617
|
+
if (e.head)
|
|
618
|
+
inventory.equipped.head = deserializeItemInstance(e.head);
|
|
619
|
+
if (Array.isArray(e.containers)) {
|
|
620
|
+
for (const [slot, item] of e.containers) {
|
|
621
|
+
inventory.equipped.containers.set(slot, deserializeItemInstance(item));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return inventory;
|
|
626
|
+
}
|
|
627
|
+
function deserializeItemInstance(data) {
|
|
628
|
+
const d = data;
|
|
629
|
+
return {
|
|
630
|
+
instanceId: d.instanceId ?? "",
|
|
631
|
+
templateId: d.templateId ?? "",
|
|
632
|
+
quantity: d.quantity ?? 1,
|
|
633
|
+
durability_Q: d.durability_Q,
|
|
634
|
+
modifications: Array.isArray(d.modifications) ? d.modifications : undefined,
|
|
635
|
+
containerPath: Array.isArray(d.containerPath) ? d.containerPath : [],
|
|
636
|
+
};
|
|
637
|
+
}
|