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