@nice2dev/game-engine 0.1.0 → 1.0.2

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +193 -1
  2. package/dist/cjs/audio/AudioBridge.js +454 -0
  3. package/dist/cjs/audio/AudioBridge.js.map +1 -0
  4. package/dist/cjs/devtools/GameplayAnalytics.js +651 -0
  5. package/dist/cjs/devtools/GameplayAnalytics.js.map +1 -0
  6. package/dist/cjs/dialogue/DialogueSystem.js +1023 -0
  7. package/dist/cjs/dialogue/DialogueSystem.js.map +1 -0
  8. package/dist/cjs/editor/NiceGameEditor.js +569 -71
  9. package/dist/cjs/editor/NiceGameEditor.js.map +1 -1
  10. package/dist/cjs/engine/SaveSystemV2.js +494 -0
  11. package/dist/cjs/engine/SaveSystemV2.js.map +1 -0
  12. package/dist/cjs/i18n/useTranslation.js +11 -11
  13. package/dist/cjs/index.js +90 -1
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/input/GamepadNavigation.js +21 -21
  16. package/dist/cjs/input/useGamepads.js +6 -6
  17. package/dist/cjs/integration/IconSprite.js +281 -0
  18. package/dist/cjs/integration/IconSprite.js.map +1 -0
  19. package/dist/cjs/inventory/InventorySystem.js +930 -0
  20. package/dist/cjs/inventory/InventorySystem.js.map +1 -0
  21. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AbortController.js.map +1 -1
  22. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js.map +1 -1
  23. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js.map +1 -1
  24. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js.map +1 -1
  25. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Errors.js.map +1 -1
  26. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js.map +1 -1
  27. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js.map +1 -1
  28. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js.map +1 -1
  29. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpClient.js.map +1 -1
  30. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js.map +1 -1
  31. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnection.js.map +1 -1
  32. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js.map +1 -1
  33. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js.map +1 -1
  34. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ILogger.js.map +1 -1
  35. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ITransport.js.map +1 -1
  36. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js.map +1 -1
  37. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Loggers.js.map +1 -1
  38. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js.map +1 -1
  39. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js.map +1 -1
  40. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js.map +1 -1
  41. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Subject.js.map +1 -1
  42. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js.map +1 -1
  43. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Utils.js.map +1 -1
  44. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js.map +1 -1
  45. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js.map +1 -1
  46. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/pkg-version.js.map +1 -1
  47. package/dist/cjs/quest/QuestSystem.js +924 -0
  48. package/dist/cjs/quest/QuestSystem.js.map +1 -0
  49. package/dist/cjs/rendering/WebGPURenderPipeline.js +658 -0
  50. package/dist/cjs/rendering/WebGPURenderPipeline.js.map +1 -0
  51. package/dist/cjs/xr/ARVR.js.map +1 -1
  52. package/dist/esm/audio/AudioBridge.js +446 -0
  53. package/dist/esm/audio/AudioBridge.js.map +1 -0
  54. package/dist/esm/devtools/GameplayAnalytics.js +639 -0
  55. package/dist/esm/devtools/GameplayAnalytics.js.map +1 -0
  56. package/dist/esm/dialogue/DialogueSystem.js +1008 -0
  57. package/dist/esm/dialogue/DialogueSystem.js.map +1 -0
  58. package/dist/esm/editor/NiceGameEditor.js +556 -58
  59. package/dist/esm/editor/NiceGameEditor.js.map +1 -1
  60. package/dist/esm/engine/SaveSystemV2.js +487 -0
  61. package/dist/esm/engine/SaveSystemV2.js.map +1 -0
  62. package/dist/esm/index.js +11 -3
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/integration/IconSprite.js +266 -0
  65. package/dist/esm/integration/IconSprite.js.map +1 -0
  66. package/dist/esm/inventory/InventorySystem.js +924 -0
  67. package/dist/esm/inventory/InventorySystem.js.map +1 -0
  68. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AbortController.js.map +1 -1
  69. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js.map +1 -1
  70. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js.map +1 -1
  71. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js.map +1 -1
  72. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Errors.js.map +1 -1
  73. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js.map +1 -1
  74. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js.map +1 -1
  75. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js.map +1 -1
  76. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpClient.js.map +1 -1
  77. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js.map +1 -1
  78. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnection.js.map +1 -1
  79. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js.map +1 -1
  80. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js.map +1 -1
  81. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ILogger.js.map +1 -1
  82. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ITransport.js.map +1 -1
  83. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js.map +1 -1
  84. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Loggers.js.map +1 -1
  85. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js.map +1 -1
  86. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js.map +1 -1
  87. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js.map +1 -1
  88. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Subject.js.map +1 -1
  89. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js.map +1 -1
  90. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Utils.js.map +1 -1
  91. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js.map +1 -1
  92. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js.map +1 -1
  93. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/pkg-version.js.map +1 -1
  94. package/dist/esm/quest/QuestSystem.js +916 -0
  95. package/dist/esm/quest/QuestSystem.js.map +1 -0
  96. package/dist/esm/rendering/WebGPURenderPipeline.js +642 -0
  97. package/dist/esm/rendering/WebGPURenderPipeline.js.map +1 -0
  98. package/dist/esm/xr/ARVR.js.map +1 -1
  99. package/dist/types/__tests__/setup.d.ts +1 -1
  100. package/dist/types/audio/AudioBridge.d.ts +199 -0
  101. package/dist/types/devtools/GameplayAnalytics.d.ts +279 -0
  102. package/dist/types/dialogue/DialogueSystem.d.ts +326 -0
  103. package/dist/types/dialogue/index.d.ts +2 -0
  104. package/dist/types/editor/NiceGameEditor.d.ts +12 -1
  105. package/dist/types/engine/SaveSystemV2.d.ts +155 -0
  106. package/dist/types/index.d.ts +19 -3
  107. package/dist/types/integration/IconSprite.d.ts +196 -0
  108. package/dist/types/inventory/InventorySystem.d.ts +336 -0
  109. package/dist/types/performance/WebGPUCompute.d.ts +0 -10
  110. package/dist/types/quest/QuestSystem.d.ts +287 -0
  111. package/dist/types/rendering/WebGPURenderPipeline.d.ts +255 -0
  112. package/package.json +7 -1
@@ -0,0 +1,924 @@
1
+ import React from 'react';
2
+
3
+ /* ────────────────────────────────────────────────────────────────
4
+ Inventory System
5
+
6
+ A comprehensive inventory management system with:
7
+ - Multiple container types (player, chest, shop, crafting)
8
+ - Equipment slots with stat modifiers
9
+ - Item stacking, splitting, and merging
10
+ - Weight/capacity limits
11
+ - Item categories and rarity
12
+ - Crafting recipes
13
+ - Trade/shop transactions
14
+ - Loot tables and drop rates
15
+ - Item durability and repair
16
+ - Consumables with effects
17
+ - State persistence
18
+ ──────────────────────────────────────────────────────────────── */
19
+ const DEFAULT_CONFIG = {
20
+ defaultSlots: 40,
21
+ enableWeight: true,
22
+ enableDurability: true,
23
+ autoStack: true,
24
+ autoSort: false,
25
+ maxContainersPerEntity: 5,
26
+ };
27
+ class InventoryManager {
28
+ constructor(config = {}) {
29
+ this.eventBus = null;
30
+ // Registries
31
+ this.itemDefinitions = new Map();
32
+ this.containers = new Map();
33
+ this.equipment = new Map();
34
+ this.recipes = new Map();
35
+ this.lootTables = new Map();
36
+ this.shops = new Map();
37
+ this.learnedRecipes = new Map();
38
+ this.cooldowns = new Map(); // instanceId -> lastUsed
39
+ this.config = { ...DEFAULT_CONFIG, ...config };
40
+ }
41
+ /* ── Initialization ─────────────────────────────────────────── */
42
+ setEventBus(bus) {
43
+ this.eventBus = bus;
44
+ }
45
+ emit(event) {
46
+ var _a;
47
+ (_a = this.eventBus) === null || _a === void 0 ? void 0 : _a.emit(`inventory:${event.type}`, event);
48
+ }
49
+ /* ── Item Definitions ───────────────────────────────────────── */
50
+ registerItem(definition) {
51
+ this.itemDefinitions.set(definition.id, definition);
52
+ }
53
+ registerItems(definitions) {
54
+ definitions.forEach(def => this.registerItem(def));
55
+ }
56
+ getItemDefinition(itemId) {
57
+ return this.itemDefinitions.get(itemId);
58
+ }
59
+ getAllItemDefinitions() {
60
+ return Array.from(this.itemDefinitions.values());
61
+ }
62
+ /* ── Container Management ───────────────────────────────────── */
63
+ createContainer(type, ownerId, options = {}) {
64
+ var _a, _b, _c, _d;
65
+ const id = (_a = options.id) !== null && _a !== void 0 ? _a : `container_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
66
+ const maxSlots = (_b = options.maxSlots) !== null && _b !== void 0 ? _b : this.config.defaultSlots;
67
+ const container = {
68
+ id,
69
+ type,
70
+ name: (_c = options.name) !== null && _c !== void 0 ? _c : `${type} container`,
71
+ ownerId,
72
+ maxSlots,
73
+ maxWeight: options.maxWeight,
74
+ slots: Array.from({ length: maxSlots }, (_, i) => ({ index: i, item: null })),
75
+ currency: (_d = options.currency) !== null && _d !== void 0 ? _d : new Map(),
76
+ allowedCategories: options.allowedCategories,
77
+ disallowedCategories: options.disallowedCategories,
78
+ };
79
+ this.containers.set(id, container);
80
+ this.emit({ type: 'container:created', containerId: id, ownerId, timestamp: Date.now() });
81
+ return container;
82
+ }
83
+ getContainer(containerId) {
84
+ return this.containers.get(containerId);
85
+ }
86
+ getContainersByOwner(ownerId) {
87
+ return Array.from(this.containers.values()).filter(c => c.ownerId === ownerId);
88
+ }
89
+ destroyContainer(containerId) {
90
+ const container = this.containers.get(containerId);
91
+ if (!container)
92
+ return false;
93
+ this.containers.delete(containerId);
94
+ this.emit({ type: 'container:destroyed', containerId, ownerId: container.ownerId, timestamp: Date.now() });
95
+ return true;
96
+ }
97
+ /* ── Item Instance Creation ─────────────────────────────────── */
98
+ createItemInstance(itemId, count = 1, options = {}) {
99
+ var _a;
100
+ const def = this.itemDefinitions.get(itemId);
101
+ if (!def)
102
+ return null;
103
+ // Extract count from options to prevent override
104
+ const { count: _ignoredCount, ...restOptions } = options;
105
+ const instance = {
106
+ instanceId: (_a = restOptions.instanceId) !== null && _a !== void 0 ? _a : `item_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
107
+ itemId,
108
+ count: Math.min(count, def.maxStack),
109
+ durability: def.maxDurability,
110
+ createdAt: Date.now(),
111
+ ...restOptions,
112
+ };
113
+ return instance;
114
+ }
115
+ /* ── Basic Operations ───────────────────────────────────────── */
116
+ addItem(containerId, itemId, count = 1, options = {}) {
117
+ var _a, _b, _c;
118
+ const container = this.containers.get(containerId);
119
+ if (!container)
120
+ return null;
121
+ const def = this.itemDefinitions.get(itemId);
122
+ if (!def)
123
+ return null;
124
+ // Check category restrictions
125
+ if (container.allowedCategories && !container.allowedCategories.includes(def.category)) {
126
+ return null;
127
+ }
128
+ if ((_a = container.disallowedCategories) === null || _a === void 0 ? void 0 : _a.includes(def.category)) {
129
+ return null;
130
+ }
131
+ // Check weight
132
+ if (this.config.enableWeight && container.maxWeight !== undefined) {
133
+ const currentWeight = this.getContainerWeight(containerId);
134
+ if (currentWeight + (def.weight * count) > container.maxWeight) {
135
+ return null;
136
+ }
137
+ }
138
+ // Try to stack with existing items first
139
+ if (this.config.autoStack && def.maxStack > 1) {
140
+ for (const slot of container.slots) {
141
+ if (slot.item && slot.item.itemId === itemId && slot.item.count < def.maxStack) {
142
+ const space = def.maxStack - slot.item.count;
143
+ const toAdd = Math.min(count, space);
144
+ slot.item.count += toAdd;
145
+ count -= toAdd;
146
+ this.emit({
147
+ type: 'item:stacked',
148
+ containerId,
149
+ ownerId: container.ownerId,
150
+ item: slot.item,
151
+ slot: slot.index,
152
+ count: toAdd,
153
+ timestamp: Date.now(),
154
+ });
155
+ if (count <= 0)
156
+ return slot.item;
157
+ }
158
+ }
159
+ }
160
+ // Find empty slot for remaining
161
+ while (count > 0) {
162
+ const emptySlot = container.slots.find(s => !s.item && !s.locked);
163
+ if (!emptySlot)
164
+ return null; // No space
165
+ const stackCount = Math.min(count, def.maxStack);
166
+ const instance = this.createItemInstance(itemId, stackCount, options);
167
+ if (!instance)
168
+ return null;
169
+ emptySlot.item = instance;
170
+ count -= stackCount;
171
+ this.emit({
172
+ type: 'item:added',
173
+ containerId,
174
+ ownerId: container.ownerId,
175
+ item: instance,
176
+ slot: emptySlot.index,
177
+ count: stackCount,
178
+ timestamp: Date.now(),
179
+ });
180
+ }
181
+ return (_c = (_b = container.slots.find(s => { var _a; return ((_a = s.item) === null || _a === void 0 ? void 0 : _a.itemId) === itemId; })) === null || _b === void 0 ? void 0 : _b.item) !== null && _c !== void 0 ? _c : null;
182
+ }
183
+ removeItem(containerId, slotIndex, count = 1) {
184
+ const container = this.containers.get(containerId);
185
+ if (!container)
186
+ return null;
187
+ const slot = container.slots[slotIndex];
188
+ if (!(slot === null || slot === void 0 ? void 0 : slot.item))
189
+ return null;
190
+ const removed = { ...slot.item };
191
+ if (slot.item.count <= count) {
192
+ slot.item = null;
193
+ // removed already has the correct count from the spread above
194
+ }
195
+ else {
196
+ slot.item.count -= count;
197
+ removed.count = count;
198
+ }
199
+ this.emit({
200
+ type: 'item:removed',
201
+ containerId,
202
+ ownerId: container.ownerId,
203
+ item: removed,
204
+ slot: slotIndex,
205
+ count: removed.count,
206
+ timestamp: Date.now(),
207
+ });
208
+ return removed;
209
+ }
210
+ moveItem(fromContainerId, fromSlot, toContainerId, toSlot) {
211
+ const fromContainer = this.containers.get(fromContainerId);
212
+ const toContainer = this.containers.get(toContainerId);
213
+ if (!fromContainer || !toContainer)
214
+ return false;
215
+ const sourceSlot = fromContainer.slots[fromSlot];
216
+ const targetSlot = toContainer.slots[toSlot];
217
+ if (!(sourceSlot === null || sourceSlot === void 0 ? void 0 : sourceSlot.item) || !targetSlot)
218
+ return false;
219
+ const def = this.itemDefinitions.get(sourceSlot.item.itemId);
220
+ if (!def)
221
+ return false;
222
+ // Check target container restrictions
223
+ if (toContainer.allowedCategories && !toContainer.allowedCategories.includes(def.category)) {
224
+ return false;
225
+ }
226
+ // Swap or stack
227
+ if (targetSlot.item) {
228
+ if (targetSlot.item.itemId === sourceSlot.item.itemId &&
229
+ targetSlot.item.count < def.maxStack) {
230
+ // Stack
231
+ const space = def.maxStack - targetSlot.item.count;
232
+ const toMove = Math.min(sourceSlot.item.count, space);
233
+ targetSlot.item.count += toMove;
234
+ sourceSlot.item.count -= toMove;
235
+ if (sourceSlot.item.count <= 0)
236
+ sourceSlot.item = null;
237
+ }
238
+ else {
239
+ // Swap
240
+ const temp = sourceSlot.item;
241
+ sourceSlot.item = targetSlot.item;
242
+ targetSlot.item = temp;
243
+ }
244
+ }
245
+ else {
246
+ // Move to empty slot
247
+ targetSlot.item = sourceSlot.item;
248
+ sourceSlot.item = null;
249
+ }
250
+ this.emit({
251
+ type: 'item:moved',
252
+ containerId: fromContainerId,
253
+ ownerId: fromContainer.ownerId,
254
+ fromSlot,
255
+ toSlot,
256
+ timestamp: Date.now(),
257
+ });
258
+ return true;
259
+ }
260
+ splitStack(containerId, slotIndex, splitCount) {
261
+ const container = this.containers.get(containerId);
262
+ if (!container)
263
+ return false;
264
+ const slot = container.slots[slotIndex];
265
+ if (!(slot === null || slot === void 0 ? void 0 : slot.item) || slot.item.count <= splitCount)
266
+ return false;
267
+ const emptySlot = container.slots.find(s => !s.item && !s.locked);
268
+ if (!emptySlot)
269
+ return false;
270
+ const newInstance = this.createItemInstance(slot.item.itemId, splitCount, { ...slot.item, instanceId: undefined });
271
+ if (!newInstance)
272
+ return false;
273
+ slot.item.count -= splitCount;
274
+ emptySlot.item = newInstance;
275
+ this.emit({
276
+ type: 'item:split',
277
+ containerId,
278
+ ownerId: container.ownerId,
279
+ item: newInstance,
280
+ fromSlot: slotIndex,
281
+ toSlot: emptySlot.index,
282
+ count: splitCount,
283
+ timestamp: Date.now(),
284
+ });
285
+ return true;
286
+ }
287
+ /* ── Weight & Capacity ──────────────────────────────────────── */
288
+ getContainerWeight(containerId) {
289
+ const container = this.containers.get(containerId);
290
+ if (!container)
291
+ return 0;
292
+ let weight = 0;
293
+ for (const slot of container.slots) {
294
+ if (slot.item) {
295
+ const def = this.itemDefinitions.get(slot.item.itemId);
296
+ if (def)
297
+ weight += def.weight * slot.item.count;
298
+ }
299
+ }
300
+ return weight;
301
+ }
302
+ getContainerFreeSlots(containerId) {
303
+ const container = this.containers.get(containerId);
304
+ if (!container)
305
+ return 0;
306
+ return container.slots.filter(s => !s.item && !s.locked).length;
307
+ }
308
+ /* ── Equipment System ───────────────────────────────────────── */
309
+ initializeEquipment(ownerId) {
310
+ const state = {
311
+ ownerId,
312
+ slots: new Map(),
313
+ totalStats: new Map(),
314
+ };
315
+ // Initialize all slots as empty
316
+ const allSlots = [
317
+ 'head', 'chest', 'legs', 'feet', 'hands', 'shoulders', 'back', 'waist',
318
+ 'mainHand', 'offHand', 'ring1', 'ring2', 'neck', 'trinket1', 'trinket2',
319
+ ];
320
+ allSlots.forEach(slot => state.slots.set(slot, null));
321
+ this.equipment.set(ownerId, state);
322
+ return state;
323
+ }
324
+ getEquipment(ownerId) {
325
+ return this.equipment.get(ownerId);
326
+ }
327
+ equipItem(ownerId, containerId, slotIndex) {
328
+ let equipState = this.equipment.get(ownerId);
329
+ if (!equipState) {
330
+ equipState = this.initializeEquipment(ownerId);
331
+ }
332
+ const container = this.containers.get(containerId);
333
+ if (!container)
334
+ return false;
335
+ const invSlot = container.slots[slotIndex];
336
+ if (!(invSlot === null || invSlot === void 0 ? void 0 : invSlot.item))
337
+ return false;
338
+ const def = this.itemDefinitions.get(invSlot.item.itemId);
339
+ if (!(def === null || def === void 0 ? void 0 : def.equipSlot))
340
+ return false;
341
+ // Check requirements
342
+ // (In real implementation, would check level, class, stats)
343
+ // Handle two-hand weapons
344
+ let targetSlot = def.equipSlot;
345
+ if (targetSlot === 'twoHand') {
346
+ // Unequip both hands
347
+ const mainHand = equipState.slots.get('mainHand');
348
+ const offHand = equipState.slots.get('offHand');
349
+ if (mainHand)
350
+ this.unequipToContainer(ownerId, 'mainHand', containerId);
351
+ if (offHand)
352
+ this.unequipToContainer(ownerId, 'offHand', containerId);
353
+ targetSlot = 'mainHand';
354
+ }
355
+ // Clone the item to equip BEFORE modifying the slot
356
+ const itemToEquip = { ...invSlot.item };
357
+ // Swap with existing equipped item
358
+ const currentEquipped = equipState.slots.get(targetSlot);
359
+ if (currentEquipped) {
360
+ // Put currently equipped item back in inventory
361
+ invSlot.item = currentEquipped;
362
+ }
363
+ else {
364
+ invSlot.item = null;
365
+ }
366
+ equipState.slots.set(targetSlot, itemToEquip);
367
+ // Apply binding
368
+ if (def.binding === 'onEquip' && !itemToEquip.boundTo) {
369
+ itemToEquip.boundTo = String(ownerId);
370
+ }
371
+ // Recalculate stats
372
+ this.recalculateEquipmentStats(ownerId);
373
+ this.emit({
374
+ type: 'item:equipped',
375
+ ownerId,
376
+ item: itemToEquip,
377
+ slot: slotIndex,
378
+ timestamp: Date.now(),
379
+ });
380
+ return true;
381
+ }
382
+ unequipItem(ownerId, equipSlot, containerId) {
383
+ return this.unequipToContainer(ownerId, equipSlot, containerId);
384
+ }
385
+ unequipToContainer(ownerId, equipSlot, containerId) {
386
+ const equipState = this.equipment.get(ownerId);
387
+ if (!equipState)
388
+ return false;
389
+ const equippedItem = equipState.slots.get(equipSlot);
390
+ if (!equippedItem)
391
+ return false;
392
+ const container = this.containers.get(containerId);
393
+ if (!container)
394
+ return false;
395
+ // Find empty slot in container
396
+ const emptySlot = container.slots.find(s => !s.item && !s.locked);
397
+ if (!emptySlot)
398
+ return false; // Inventory full
399
+ emptySlot.item = equippedItem;
400
+ equipState.slots.set(equipSlot, null);
401
+ this.recalculateEquipmentStats(ownerId);
402
+ this.emit({
403
+ type: 'item:unequipped',
404
+ ownerId,
405
+ item: equippedItem,
406
+ slot: emptySlot.index,
407
+ timestamp: Date.now(),
408
+ });
409
+ return true;
410
+ }
411
+ recalculateEquipmentStats(ownerId) {
412
+ var _a, _b;
413
+ const equipState = this.equipment.get(ownerId);
414
+ if (!equipState)
415
+ return;
416
+ equipState.totalStats.clear();
417
+ for (const [, item] of equipState.slots) {
418
+ if (!item)
419
+ continue;
420
+ const def = this.itemDefinitions.get(item.itemId);
421
+ if (!(def === null || def === void 0 ? void 0 : def.stats))
422
+ continue;
423
+ for (const mod of def.stats) {
424
+ const current = (_a = equipState.totalStats.get(mod.stat)) !== null && _a !== void 0 ? _a : 0;
425
+ equipState.totalStats.set(mod.stat, current + ((_b = mod.flat) !== null && _b !== void 0 ? _b : 0));
426
+ // Percent modifiers would need separate handling
427
+ }
428
+ }
429
+ }
430
+ /* ── Durability & Repair ────────────────────────────────────── */
431
+ damageDurability(instance, damage) {
432
+ if (!this.config.enableDurability)
433
+ return false;
434
+ if (instance.durability === undefined)
435
+ return false;
436
+ instance.durability = Math.max(0, instance.durability - damage);
437
+ if (instance.durability <= 0) {
438
+ this.emit({
439
+ type: 'item:destroyed',
440
+ item: instance,
441
+ timestamp: Date.now(),
442
+ });
443
+ return true; // Item broken
444
+ }
445
+ return false;
446
+ }
447
+ repairItem(instance, amount) {
448
+ const def = this.itemDefinitions.get(instance.itemId);
449
+ if (!(def === null || def === void 0 ? void 0 : def.maxDurability) || instance.durability === undefined)
450
+ return false;
451
+ instance.durability = Math.min(def.maxDurability, amount !== undefined ? instance.durability + amount : def.maxDurability);
452
+ this.emit({
453
+ type: 'item:repaired',
454
+ item: instance,
455
+ timestamp: Date.now(),
456
+ });
457
+ return true;
458
+ }
459
+ /* ── Consumables ────────────────────────────────────────────── */
460
+ useItem(containerId, slotIndex, targetEntityId) {
461
+ var _a;
462
+ const container = this.containers.get(containerId);
463
+ if (!container)
464
+ return false;
465
+ const slot = container.slots[slotIndex];
466
+ if (!(slot === null || slot === void 0 ? void 0 : slot.item))
467
+ return false;
468
+ const def = this.itemDefinitions.get(slot.item.itemId);
469
+ if (!def)
470
+ return false;
471
+ // Check cooldown
472
+ if (def.cooldown) {
473
+ const lastUsed = (_a = this.cooldowns.get(slot.item.instanceId)) !== null && _a !== void 0 ? _a : 0;
474
+ if (Date.now() - lastUsed < def.cooldown) {
475
+ return false; // On cooldown
476
+ }
477
+ }
478
+ // Set cooldown
479
+ if (def.cooldown) {
480
+ this.cooldowns.set(slot.item.instanceId, Date.now());
481
+ }
482
+ this.emit({
483
+ type: 'item:used',
484
+ containerId,
485
+ ownerId: container.ownerId,
486
+ item: slot.item,
487
+ slot: slotIndex,
488
+ customData: { targetEntityId, effectId: def.effectId },
489
+ timestamp: Date.now(),
490
+ });
491
+ // Consume if applicable
492
+ if (def.consumeOnUse) {
493
+ slot.item.count--;
494
+ if (slot.item.count <= 0) {
495
+ slot.item = null;
496
+ }
497
+ }
498
+ return true;
499
+ }
500
+ /* ── Crafting System ────────────────────────────────────────── */
501
+ registerRecipe(recipe) {
502
+ this.recipes.set(recipe.id, recipe);
503
+ }
504
+ learnRecipe(entityId, recipeId) {
505
+ const recipe = this.recipes.get(recipeId);
506
+ if (!recipe || !recipe.requiresLearning)
507
+ return false;
508
+ let learned = this.learnedRecipes.get(entityId);
509
+ if (!learned) {
510
+ learned = new Set();
511
+ this.learnedRecipes.set(entityId, learned);
512
+ }
513
+ learned.add(recipeId);
514
+ return true;
515
+ }
516
+ canCraft(entityId, recipeId, containerId) {
517
+ const recipe = this.recipes.get(recipeId);
518
+ if (!recipe)
519
+ return false;
520
+ // Check if recipe is learned
521
+ if (recipe.requiresLearning) {
522
+ const learned = this.learnedRecipes.get(entityId);
523
+ if (!(learned === null || learned === void 0 ? void 0 : learned.has(recipeId)))
524
+ return false;
525
+ }
526
+ // Check ingredients
527
+ const container = this.containers.get(containerId);
528
+ if (!container)
529
+ return false;
530
+ for (const ingredient of recipe.ingredients) {
531
+ const available = this.countItemsInContainer(containerId, ingredient.itemId);
532
+ if (available < ingredient.count)
533
+ return false;
534
+ }
535
+ return true;
536
+ }
537
+ craft(entityId, recipeId, containerId) {
538
+ if (!this.canCraft(entityId, recipeId, containerId))
539
+ return null;
540
+ const recipe = this.recipes.get(recipeId);
541
+ this.containers.get(containerId);
542
+ // Check success
543
+ if (Math.random() > recipe.successRate) {
544
+ // Failed - consume ingredients anyway if consumed flag set
545
+ for (const ingredient of recipe.ingredients) {
546
+ if (ingredient.consumed) {
547
+ this.removeItemsByIdFromContainer(containerId, ingredient.itemId, ingredient.count);
548
+ }
549
+ }
550
+ this.emit({
551
+ type: 'craft:failed',
552
+ ownerId: entityId,
553
+ recipeId,
554
+ containerId,
555
+ timestamp: Date.now(),
556
+ });
557
+ return null;
558
+ }
559
+ // Consume ingredients
560
+ for (const ingredient of recipe.ingredients) {
561
+ if (ingredient.consumed) {
562
+ this.removeItemsByIdFromContainer(containerId, ingredient.itemId, ingredient.count);
563
+ }
564
+ }
565
+ // Create output
566
+ const output = this.addItem(containerId, recipe.outputItemId, recipe.outputCount);
567
+ this.emit({
568
+ type: 'craft:completed',
569
+ ownerId: entityId,
570
+ recipeId,
571
+ containerId,
572
+ item: output !== null && output !== void 0 ? output : undefined,
573
+ timestamp: Date.now(),
574
+ });
575
+ return output;
576
+ }
577
+ /* ── Loot Tables ────────────────────────────────────────────── */
578
+ registerLootTable(table) {
579
+ this.lootTables.set(table.id, table);
580
+ }
581
+ generateLoot(tableId, modifiers = {}) {
582
+ var _a;
583
+ const table = this.lootTables.get(tableId);
584
+ if (!table)
585
+ return [];
586
+ const loot = [];
587
+ // Guaranteed drops
588
+ if (table.guaranteedDrops) {
589
+ for (const drop of table.guaranteedDrops) {
590
+ const instance = this.createItemInstance(drop.itemId, drop.count);
591
+ if (instance)
592
+ loot.push(instance);
593
+ }
594
+ }
595
+ // Roll on table
596
+ const validEntries = table.entries.filter(e => {
597
+ var _a, _b;
598
+ if (((_a = e.conditions) === null || _a === void 0 ? void 0 : _a.minLevel) && modifiers.levelContext && modifiers.levelContext < e.conditions.minLevel)
599
+ return false;
600
+ if (((_b = e.conditions) === null || _b === void 0 ? void 0 : _b.maxLevel) && modifiers.levelContext && modifiers.levelContext > e.conditions.maxLevel)
601
+ return false;
602
+ return true;
603
+ });
604
+ const totalWeight = validEntries.reduce((sum, e) => sum + e.weight, 0);
605
+ for (let roll = 0; roll < table.rolls; roll++) {
606
+ let random = Math.random() * totalWeight;
607
+ for (const entry of validEntries) {
608
+ random -= entry.weight;
609
+ if (random <= 0) {
610
+ // Apply drop chance with luck modifier
611
+ const effectiveChance = Math.min(1, entry.dropChance + ((_a = modifiers.luckBonus) !== null && _a !== void 0 ? _a : 0));
612
+ if (Math.random() <= effectiveChance) {
613
+ const count = Math.floor(entry.minCount + Math.random() * (entry.maxCount - entry.minCount + 1));
614
+ const instance = this.createItemInstance(entry.itemId, count);
615
+ if (instance)
616
+ loot.push(instance);
617
+ }
618
+ break;
619
+ }
620
+ }
621
+ }
622
+ this.emit({
623
+ type: 'loot:generated',
624
+ customData: { tableId, loot },
625
+ timestamp: Date.now(),
626
+ });
627
+ return loot;
628
+ }
629
+ /* ── Shop System ────────────────────────────────────────────── */
630
+ registerShop(shop) {
631
+ this.shops.set(shop.id, shop);
632
+ }
633
+ getShop(shopId) {
634
+ return this.shops.get(shopId);
635
+ }
636
+ buyFromShop(shopId, listingIndex, quantity, buyerContainerId) {
637
+ var _a, _b, _c;
638
+ const shop = this.shops.get(shopId);
639
+ const container = this.containers.get(buyerContainerId);
640
+ if (!shop || !container)
641
+ return false;
642
+ const listing = shop.listings[listingIndex];
643
+ if (!listing)
644
+ return false;
645
+ // Check stock
646
+ if (listing.stock !== -1 && listing.stock < quantity)
647
+ return false;
648
+ // Calculate price
649
+ const def = this.itemDefinitions.get(listing.itemId);
650
+ if (!def)
651
+ return false;
652
+ const unitPrice = (_a = listing.price) !== null && _a !== void 0 ? _a : Math.ceil(def.value * shop.sellRate);
653
+ const totalPrice = unitPrice * quantity;
654
+ // Check buyer currency
655
+ const buyerCurrency = (_c = (_b = container.currency) === null || _b === void 0 ? void 0 : _b.get(listing.currencyId)) !== null && _c !== void 0 ? _c : 0;
656
+ if (buyerCurrency < totalPrice)
657
+ return false;
658
+ // Execute transaction
659
+ container.currency.set(listing.currencyId, buyerCurrency - totalPrice);
660
+ const purchased = this.addItem(buyerContainerId, listing.itemId, quantity);
661
+ if (!purchased) {
662
+ // Refund if couldn't add item
663
+ container.currency.set(listing.currencyId, buyerCurrency);
664
+ return false;
665
+ }
666
+ // Update stock
667
+ if (listing.stock !== -1) {
668
+ listing.stock -= quantity;
669
+ }
670
+ this.emit({
671
+ type: 'shop:bought',
672
+ shopId,
673
+ containerId: buyerContainerId,
674
+ ownerId: container.ownerId,
675
+ itemId: listing.itemId,
676
+ count: quantity,
677
+ currencyId: listing.currencyId,
678
+ amount: totalPrice,
679
+ timestamp: Date.now(),
680
+ });
681
+ return true;
682
+ }
683
+ sellToShop(shopId, sellerContainerId, slotIndex, quantity) {
684
+ var _a, _b;
685
+ const shop = this.shops.get(shopId);
686
+ const container = this.containers.get(sellerContainerId);
687
+ if (!shop || !container)
688
+ return false;
689
+ const slot = container.slots[slotIndex];
690
+ if (!(slot === null || slot === void 0 ? void 0 : slot.item) || slot.item.count < quantity)
691
+ return false;
692
+ const def = this.itemDefinitions.get(slot.item.itemId);
693
+ if (!def || !def.sellable)
694
+ return false;
695
+ // Calculate sell price
696
+ const unitPrice = Math.floor(def.value * shop.buyRate);
697
+ const totalPrice = unitPrice * quantity;
698
+ // Use first accepted currency
699
+ const currencyId = shop.acceptedCurrencies[0];
700
+ if (!currencyId)
701
+ return false;
702
+ // Remove items
703
+ this.removeItem(sellerContainerId, slotIndex, quantity);
704
+ // Add currency
705
+ const currentCurrency = (_b = (_a = container.currency) === null || _a === void 0 ? void 0 : _a.get(currencyId)) !== null && _b !== void 0 ? _b : 0;
706
+ container.currency.set(currencyId, currentCurrency + totalPrice);
707
+ this.emit({
708
+ type: 'shop:sold',
709
+ shopId,
710
+ containerId: sellerContainerId,
711
+ ownerId: container.ownerId,
712
+ itemId: def.id,
713
+ count: quantity,
714
+ currencyId,
715
+ amount: totalPrice,
716
+ timestamp: Date.now(),
717
+ });
718
+ return true;
719
+ }
720
+ /* ── Currency ───────────────────────────────────────────────── */
721
+ addCurrency(containerId, currencyId, amount) {
722
+ var _a;
723
+ const container = this.containers.get(containerId);
724
+ if (!container)
725
+ return false;
726
+ if (!container.currency)
727
+ container.currency = new Map();
728
+ const current = (_a = container.currency.get(currencyId)) !== null && _a !== void 0 ? _a : 0;
729
+ container.currency.set(currencyId, current + amount);
730
+ this.emit({
731
+ type: 'currency:changed',
732
+ containerId,
733
+ ownerId: container.ownerId,
734
+ currencyId,
735
+ amount,
736
+ timestamp: Date.now(),
737
+ });
738
+ return true;
739
+ }
740
+ removeCurrency(containerId, currencyId, amount) {
741
+ var _a;
742
+ const container = this.containers.get(containerId);
743
+ if (!(container === null || container === void 0 ? void 0 : container.currency))
744
+ return false;
745
+ const current = (_a = container.currency.get(currencyId)) !== null && _a !== void 0 ? _a : 0;
746
+ if (current < amount)
747
+ return false;
748
+ container.currency.set(currencyId, current - amount);
749
+ this.emit({
750
+ type: 'currency:changed',
751
+ containerId,
752
+ ownerId: container.ownerId,
753
+ currencyId,
754
+ amount: -amount,
755
+ timestamp: Date.now(),
756
+ });
757
+ return true;
758
+ }
759
+ getCurrency(containerId, currencyId) {
760
+ var _a, _b, _c;
761
+ return (_c = (_b = (_a = this.containers.get(containerId)) === null || _a === void 0 ? void 0 : _a.currency) === null || _b === void 0 ? void 0 : _b.get(currencyId)) !== null && _c !== void 0 ? _c : 0;
762
+ }
763
+ /* ── Sorting ────────────────────────────────────────────────── */
764
+ sortContainer(containerId, criteria = 'category') {
765
+ const container = this.containers.get(containerId);
766
+ if (!container)
767
+ return;
768
+ const items = container.slots
769
+ .filter(s => s.item && !s.locked)
770
+ .map(s => s.item)
771
+ .sort((a, b) => {
772
+ const defA = this.itemDefinitions.get(a.itemId);
773
+ const defB = this.itemDefinitions.get(b.itemId);
774
+ if (!defA || !defB)
775
+ return 0;
776
+ switch (criteria) {
777
+ case 'category':
778
+ return defA.category.localeCompare(defB.category) || defA.name.localeCompare(defB.name);
779
+ case 'rarity': {
780
+ const rarityOrder = ['common', 'uncommon', 'rare', 'epic', 'legendary', 'mythic', 'unique'];
781
+ return rarityOrder.indexOf(defB.rarity) - rarityOrder.indexOf(defA.rarity);
782
+ }
783
+ case 'name':
784
+ return defA.name.localeCompare(defB.name);
785
+ case 'value':
786
+ return defB.value - defA.value;
787
+ default:
788
+ return 0;
789
+ }
790
+ });
791
+ // Clear unlocked slots
792
+ container.slots.forEach(s => {
793
+ if (!s.locked)
794
+ s.item = null;
795
+ });
796
+ // Fill with sorted items
797
+ let itemIndex = 0;
798
+ for (const slot of container.slots) {
799
+ if (!slot.locked && itemIndex < items.length) {
800
+ slot.item = items[itemIndex++];
801
+ }
802
+ }
803
+ container.sorted = true;
804
+ this.emit({
805
+ type: 'container:sorted',
806
+ containerId,
807
+ ownerId: container.ownerId,
808
+ timestamp: Date.now(),
809
+ });
810
+ }
811
+ /* ── Utility Methods ────────────────────────────────────────── */
812
+ countItemsInContainer(containerId, itemId) {
813
+ const container = this.containers.get(containerId);
814
+ if (!container)
815
+ return 0;
816
+ return container.slots.reduce((sum, slot) => {
817
+ var _a;
818
+ if (((_a = slot.item) === null || _a === void 0 ? void 0 : _a.itemId) === itemId)
819
+ return sum + slot.item.count;
820
+ return sum;
821
+ }, 0);
822
+ }
823
+ removeItemsByIdFromContainer(containerId, itemId, count) {
824
+ const container = this.containers.get(containerId);
825
+ if (!container)
826
+ return 0;
827
+ let removed = 0;
828
+ for (const slot of container.slots) {
829
+ if (!slot.item || slot.item.itemId !== itemId)
830
+ continue;
831
+ const toRemove = Math.min(count - removed, slot.item.count);
832
+ slot.item.count -= toRemove;
833
+ removed += toRemove;
834
+ if (slot.item.count <= 0)
835
+ slot.item = null;
836
+ if (removed >= count)
837
+ break;
838
+ }
839
+ return removed;
840
+ }
841
+ findItem(containerId, itemId) {
842
+ var _a;
843
+ const container = this.containers.get(containerId);
844
+ if (!container)
845
+ return null;
846
+ for (const slot of container.slots) {
847
+ if (((_a = slot.item) === null || _a === void 0 ? void 0 : _a.itemId) === itemId) {
848
+ return { slot, instance: slot.item };
849
+ }
850
+ }
851
+ return null;
852
+ }
853
+ /* ── Serialization ──────────────────────────────────────────── */
854
+ serialize() {
855
+ return {
856
+ containers: Array.from(this.containers.entries()).map(([id, c]) => ({
857
+ ...c,
858
+ currency: c.currency ? Array.from(c.currency.entries()) : [],
859
+ })),
860
+ equipment: Array.from(this.equipment.entries()).map(([id, e]) => ({
861
+ ownerId: id,
862
+ slots: Array.from(e.slots.entries()),
863
+ totalStats: Array.from(e.totalStats.entries()),
864
+ })),
865
+ learnedRecipes: Array.from(this.learnedRecipes.entries()).map(([id, recipes]) => ({
866
+ entityId: id,
867
+ recipes: Array.from(recipes),
868
+ })),
869
+ };
870
+ }
871
+ deserialize(snapshot) {
872
+ this.containers.clear();
873
+ this.equipment.clear();
874
+ this.learnedRecipes.clear();
875
+ for (const c of snapshot.containers) {
876
+ this.containers.set(c.id, {
877
+ ...c,
878
+ currency: new Map(c.currency),
879
+ });
880
+ }
881
+ for (const e of snapshot.equipment) {
882
+ this.equipment.set(e.ownerId, {
883
+ ownerId: e.ownerId,
884
+ slots: new Map(e.slots),
885
+ totalStats: new Map(e.totalStats),
886
+ });
887
+ }
888
+ for (const r of snapshot.learnedRecipes) {
889
+ this.learnedRecipes.set(r.entityId, new Set(r.recipes));
890
+ }
891
+ }
892
+ }
893
+ /* ═══════════════════════════════════════════════════════════════
894
+ REACT INTEGRATION
895
+ ══════════════════════════════════════════════════════════════════ */
896
+ const InventoryContext = React.createContext(null);
897
+ const InventoryProvider = ({ manager, children }) => {
898
+ return React.createElement(InventoryContext.Provider, { value: manager }, children);
899
+ };
900
+ function useInventoryManager() {
901
+ const ctx = React.useContext(InventoryContext);
902
+ if (!ctx)
903
+ throw new Error('useInventoryManager must be used within InventoryProvider');
904
+ return ctx;
905
+ }
906
+ function useContainer(containerId) {
907
+ const manager = useInventoryManager();
908
+ const [container, setContainer] = React.useState(() => manager.getContainer(containerId));
909
+ React.useEffect(() => {
910
+ setContainer(manager.getContainer(containerId));
911
+ }, [manager, containerId]);
912
+ return container;
913
+ }
914
+ function useEquipment(ownerId) {
915
+ const manager = useInventoryManager();
916
+ const [equipment, setEquipment] = React.useState(() => manager.getEquipment(ownerId));
917
+ React.useEffect(() => {
918
+ setEquipment(manager.getEquipment(ownerId));
919
+ }, [manager, ownerId]);
920
+ return equipment;
921
+ }
922
+
923
+ export { InventoryManager, InventoryProvider, useContainer, useEquipment, useInventoryManager };
924
+ //# sourceMappingURL=InventorySystem.js.map