@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
+ 'use strict';
2
+
3
+ var EventBus = require('../core/EventBus.js');
4
+ var React = require('react');
5
+
6
+ /* ────────────────────────────────────────────────────────────────
7
+ Quest & Mission System
8
+
9
+ A comprehensive quest system with:
10
+ - Multiple quest types (main, side, daily, achievement)
11
+ - Objectives with progress tracking
12
+ - Rewards and prerequisites
13
+ - Quest chains and dependencies
14
+ - State persistence
15
+ ──────────────────────────────────────────────────────────────── */
16
+ /* ═══════════════════════════════════════════════════════════════
17
+ QUEST MANAGER
18
+ ══════════════════════════════════════════════════════════════════ */
19
+ class QuestManager {
20
+ constructor(events) {
21
+ this.quests = new Map();
22
+ this.journal = {
23
+ activeQuests: [],
24
+ completedQuests: [],
25
+ failedQuests: [],
26
+ };
27
+ this.playerLevel = 1;
28
+ this.playerItems = new Set();
29
+ this.playerSkills = new Set();
30
+ this.reputation = new Map();
31
+ this.customConditions = new Map();
32
+ this.events = events !== null && events !== void 0 ? events : new EventBus.EventBus();
33
+ }
34
+ /* ── Quest Registration ───────────────────────────────────────── */
35
+ registerQuest(quest) {
36
+ this.quests.set(quest.id, quest);
37
+ }
38
+ registerQuests(quests) {
39
+ for (const quest of quests) {
40
+ this.registerQuest(quest);
41
+ }
42
+ }
43
+ unregisterQuest(questId) {
44
+ this.quests.delete(questId);
45
+ }
46
+ getQuest(questId) {
47
+ return this.quests.get(questId);
48
+ }
49
+ getAllQuests() {
50
+ return Array.from(this.quests.values());
51
+ }
52
+ /* ── Player State ─────────────────────────────────────────────── */
53
+ setPlayerLevel(level) {
54
+ this.playerLevel = level;
55
+ this.checkQuestUnlocks();
56
+ }
57
+ addPlayerItem(itemId) {
58
+ this.playerItems.add(itemId);
59
+ this.checkQuestUnlocks();
60
+ }
61
+ removePlayerItem(itemId) {
62
+ this.playerItems.delete(itemId);
63
+ }
64
+ addPlayerSkill(skillId) {
65
+ this.playerSkills.add(skillId);
66
+ this.checkQuestUnlocks();
67
+ }
68
+ setReputation(factionId, amount) {
69
+ this.reputation.set(factionId, amount);
70
+ this.checkQuestUnlocks();
71
+ }
72
+ addReputation(factionId, amount) {
73
+ var _a;
74
+ const current = (_a = this.reputation.get(factionId)) !== null && _a !== void 0 ? _a : 0;
75
+ this.reputation.set(factionId, current + amount);
76
+ this.checkQuestUnlocks();
77
+ }
78
+ registerCustomCondition(key, condition) {
79
+ this.customConditions.set(key, condition);
80
+ }
81
+ /* ── Quest State ──────────────────────────────────────────────── */
82
+ getQuestState(questId) {
83
+ var _a, _b;
84
+ return (_b = (_a = this.journal.activeQuests.find(q => q.questId === questId)) !== null && _a !== void 0 ? _a : this.journal.completedQuests.find(q => q.questId === questId)) !== null && _b !== void 0 ? _b : this.journal.failedQuests.find(q => q.questId === questId);
85
+ }
86
+ getQuestStatus(questId) {
87
+ const state = this.getQuestState(questId);
88
+ if (state)
89
+ return state.status;
90
+ const quest = this.quests.get(questId);
91
+ if (!quest)
92
+ return 'locked';
93
+ return this.canUnlockQuest(questId) ? 'available' : 'locked';
94
+ }
95
+ getActiveQuests() {
96
+ return [...this.journal.activeQuests];
97
+ }
98
+ getCompletedQuests() {
99
+ return [...this.journal.completedQuests];
100
+ }
101
+ getAvailableQuests() {
102
+ return this.getAllQuests().filter(q => {
103
+ const status = this.getQuestStatus(q.id);
104
+ return status === 'available';
105
+ });
106
+ }
107
+ getJournal() {
108
+ return { ...this.journal };
109
+ }
110
+ /* ── Quest Flow ───────────────────────────────────────────────── */
111
+ canUnlockQuest(questId) {
112
+ const quest = this.quests.get(questId);
113
+ if (!quest)
114
+ return false;
115
+ // Already active or completed
116
+ const existingState = this.getQuestState(questId);
117
+ if (existingState && (existingState.status === 'active' || existingState.status === 'completed')) {
118
+ // Check if repeatable
119
+ if (!quest.repeatable)
120
+ return false;
121
+ if (existingState.lastCompletedAt && quest.repeatCooldown) {
122
+ if (Date.now() - existingState.lastCompletedAt < quest.repeatCooldown) {
123
+ return false;
124
+ }
125
+ }
126
+ }
127
+ // Check prerequisites
128
+ return quest.prerequisites.every(prereq => this.checkPrerequisite(prereq));
129
+ }
130
+ checkPrerequisite(prereq) {
131
+ var _a, _b;
132
+ switch (prereq.type) {
133
+ case 'quest':
134
+ if (!prereq.questId)
135
+ return true;
136
+ const state = this.getQuestState(prereq.questId);
137
+ return (state === null || state === void 0 ? void 0 : state.status) === 'completed';
138
+ case 'level':
139
+ return this.playerLevel >= ((_a = prereq.level) !== null && _a !== void 0 ? _a : 0);
140
+ case 'item':
141
+ return prereq.itemId ? this.playerItems.has(prereq.itemId) : true;
142
+ case 'reputation':
143
+ if (!prereq.reputation)
144
+ return true;
145
+ const rep = (_b = this.reputation.get(prereq.reputation.factionId)) !== null && _b !== void 0 ? _b : 0;
146
+ return rep >= prereq.reputation.amount;
147
+ case 'skill':
148
+ return prereq.skillId ? this.playerSkills.has(prereq.skillId) : true;
149
+ case 'time':
150
+ if (!prereq.timeWindow)
151
+ return true;
152
+ const now = Date.now();
153
+ return now >= prereq.timeWindow.start && now <= prereq.timeWindow.end;
154
+ case 'custom':
155
+ if (!prereq.customKey)
156
+ return true;
157
+ const condition = this.customConditions.get(prereq.customKey);
158
+ return condition ? condition() : false;
159
+ default:
160
+ return true;
161
+ }
162
+ }
163
+ acceptQuest(questId) {
164
+ var _a, _b;
165
+ if (!this.canUnlockQuest(questId))
166
+ return false;
167
+ const quest = this.quests.get(questId);
168
+ if (!quest)
169
+ return false;
170
+ // Remove from failed if re-accepting
171
+ this.journal.failedQuests = this.journal.failedQuests.filter(q => q.questId !== questId);
172
+ // Create fresh objectives with 0 progress
173
+ const objectives = quest.objectives.map(obj => ({
174
+ ...obj,
175
+ current: 0,
176
+ completed: false,
177
+ }));
178
+ const state = {
179
+ questId,
180
+ status: 'active',
181
+ objectives,
182
+ startedAt: Date.now(),
183
+ completionCount: (_b = (_a = this.getQuestState(questId)) === null || _a === void 0 ? void 0 : _a.completionCount) !== null && _b !== void 0 ? _b : 0,
184
+ };
185
+ if (quest.timeLimit) {
186
+ state.timeRemaining = quest.timeLimit * 1000;
187
+ }
188
+ this.journal.activeQuests.push(state);
189
+ this.emitEvent('quest:accepted', questId, {});
190
+ return true;
191
+ }
192
+ abandonQuest(questId) {
193
+ const quest = this.quests.get(questId);
194
+ if (!(quest === null || quest === void 0 ? void 0 : quest.canAbandon))
195
+ return false;
196
+ const stateIndex = this.journal.activeQuests.findIndex(q => q.questId === questId);
197
+ if (stateIndex === -1)
198
+ return false;
199
+ const state = this.journal.activeQuests[stateIndex];
200
+ state.status = 'failed';
201
+ state.endedAt = Date.now();
202
+ this.journal.activeQuests.splice(stateIndex, 1);
203
+ this.journal.failedQuests.push(state);
204
+ this.emitEvent('quest:abandoned', questId, {});
205
+ return true;
206
+ }
207
+ /* ── Objective Progress ───────────────────────────────────────── */
208
+ updateObjectiveProgress(questId, objectiveId, amount) {
209
+ const state = this.journal.activeQuests.find(q => q.questId === questId);
210
+ if (!state || state.status !== 'active')
211
+ return false;
212
+ const objective = state.objectives.find(o => o.id === objectiveId);
213
+ if (!objective || objective.completed)
214
+ return false;
215
+ const previousValue = objective.current;
216
+ objective.current = Math.min(objective.current + amount, objective.target);
217
+ this.emitEvent('quest:objective-progress', questId, {
218
+ objectiveId,
219
+ previous: previousValue,
220
+ current: objective.current,
221
+ target: objective.target,
222
+ });
223
+ // Check if objective completed
224
+ if (objective.current >= objective.target) {
225
+ objective.completed = true;
226
+ this.emitEvent('quest:objective-completed', questId, { objectiveId });
227
+ this.checkQuestCompletion(questId);
228
+ }
229
+ return true;
230
+ }
231
+ setObjectiveProgress(questId, objectiveId, value) {
232
+ const state = this.journal.activeQuests.find(q => q.questId === questId);
233
+ if (!state || state.status !== 'active')
234
+ return false;
235
+ const objective = state.objectives.find(o => o.id === objectiveId);
236
+ if (!objective || objective.completed)
237
+ return false;
238
+ const previousValue = objective.current;
239
+ objective.current = Math.min(value, objective.target);
240
+ if (objective.current !== previousValue) {
241
+ this.emitEvent('quest:objective-progress', questId, {
242
+ objectiveId,
243
+ previous: previousValue,
244
+ current: objective.current,
245
+ target: objective.target,
246
+ });
247
+ }
248
+ if (objective.current >= objective.target) {
249
+ objective.completed = true;
250
+ this.emitEvent('quest:objective-completed', questId, { objectiveId });
251
+ this.checkQuestCompletion(questId);
252
+ }
253
+ return true;
254
+ }
255
+ completeObjective(questId, objectiveId) {
256
+ const state = this.journal.activeQuests.find(q => q.questId === questId);
257
+ if (!state || state.status !== 'active')
258
+ return false;
259
+ const objective = state.objectives.find(o => o.id === objectiveId);
260
+ if (!objective || objective.completed)
261
+ return false;
262
+ objective.current = objective.target;
263
+ objective.completed = true;
264
+ this.emitEvent('quest:objective-completed', questId, { objectiveId });
265
+ this.checkQuestCompletion(questId);
266
+ return true;
267
+ }
268
+ /* ── Quest Completion ─────────────────────────────────────────── */
269
+ checkQuestCompletion(questId) {
270
+ const state = this.journal.activeQuests.find(q => q.questId === questId);
271
+ if (!state)
272
+ return;
273
+ // Check if all required objectives are complete
274
+ const requiredComplete = state.objectives
275
+ .filter(o => !o.optional)
276
+ .every(o => o.completed);
277
+ if (requiredComplete) {
278
+ this.completeQuest(questId);
279
+ }
280
+ }
281
+ completeQuest(questId) {
282
+ const stateIndex = this.journal.activeQuests.findIndex(q => q.questId === questId);
283
+ if (stateIndex === -1)
284
+ return false;
285
+ const state = this.journal.activeQuests[stateIndex];
286
+ state.status = 'completed';
287
+ state.endedAt = Date.now();
288
+ state.completionCount++;
289
+ state.lastCompletedAt = Date.now();
290
+ this.journal.activeQuests.splice(stateIndex, 1);
291
+ this.journal.completedQuests.push(state);
292
+ this.emitEvent('quest:completed', questId, { state: { ...state } });
293
+ this.checkQuestUnlocks();
294
+ this.checkFollowUpQuests(questId);
295
+ return true;
296
+ }
297
+ failQuest(questId) {
298
+ const stateIndex = this.journal.activeQuests.findIndex(q => q.questId === questId);
299
+ if (stateIndex === -1)
300
+ return false;
301
+ const state = this.journal.activeQuests[stateIndex];
302
+ state.status = 'failed';
303
+ state.endedAt = Date.now();
304
+ this.journal.activeQuests.splice(stateIndex, 1);
305
+ this.journal.failedQuests.push(state);
306
+ this.emitEvent('quest:failed', questId, {});
307
+ return true;
308
+ }
309
+ checkQuestUnlocks() {
310
+ for (const quest of this.quests.values()) {
311
+ const status = this.getQuestStatus(quest.id);
312
+ if (status === 'locked' && this.canUnlockQuest(quest.id)) {
313
+ this.emitEvent('quest:unlocked', quest.id, {});
314
+ }
315
+ }
316
+ }
317
+ checkFollowUpQuests(completedQuestId) {
318
+ const quest = this.quests.get(completedQuestId);
319
+ if (!(quest === null || quest === void 0 ? void 0 : quest.followUpQuests))
320
+ return;
321
+ for (const followUpId of quest.followUpQuests) {
322
+ if (this.canUnlockQuest(followUpId)) {
323
+ this.emitEvent('quest:unlocked', followUpId, {});
324
+ }
325
+ }
326
+ }
327
+ /* ── Rewards ──────────────────────────────────────────────────── */
328
+ getQuestRewards(questId) {
329
+ const quest = this.quests.get(questId);
330
+ const state = this.getQuestState(questId);
331
+ if (!quest || (state === null || state === void 0 ? void 0 : state.status) !== 'completed')
332
+ return null;
333
+ const main = [...quest.rewards];
334
+ const bonus = [];
335
+ // Add bonus rewards if optional objectives completed
336
+ if (quest.bonusRewards && state) {
337
+ const optionalComplete = state.objectives
338
+ .filter(o => o.optional)
339
+ .every(o => o.completed);
340
+ if (optionalComplete) {
341
+ bonus.push(...quest.bonusRewards);
342
+ }
343
+ }
344
+ return { main, bonus };
345
+ }
346
+ claimRewards(questId) {
347
+ const rewards = this.getQuestRewards(questId);
348
+ if (!rewards)
349
+ return null;
350
+ const allRewards = [...rewards.main, ...rewards.bonus];
351
+ this.emitEvent('quest:reward-claimed', questId, { rewards: allRewards });
352
+ return allRewards;
353
+ }
354
+ /* ── Quest Tracking ───────────────────────────────────────────── */
355
+ trackQuest(questId) {
356
+ const state = this.journal.activeQuests.find(q => q.questId === questId);
357
+ if (!state)
358
+ return;
359
+ this.journal.trackedQuestId = questId;
360
+ this.emitEvent('quest:tracked', questId, {});
361
+ }
362
+ untrackQuest() {
363
+ this.journal.trackedQuestId = undefined;
364
+ }
365
+ getTrackedQuest() {
366
+ if (!this.journal.trackedQuestId)
367
+ return undefined;
368
+ return this.journal.activeQuests.find(q => q.questId === this.journal.trackedQuestId);
369
+ }
370
+ /* ── Timer Update ─────────────────────────────────────────────── */
371
+ update(deltaMs) {
372
+ for (const state of this.journal.activeQuests) {
373
+ if (state.timeRemaining !== undefined && state.timeRemaining > 0) {
374
+ state.timeRemaining -= deltaMs;
375
+ if (state.timeRemaining <= 0) {
376
+ state.timeRemaining = 0;
377
+ this.failQuest(state.questId);
378
+ }
379
+ }
380
+ }
381
+ }
382
+ /* ── Persistence ──────────────────────────────────────────────── */
383
+ saveState() {
384
+ return JSON.parse(JSON.stringify(this.journal));
385
+ }
386
+ loadState(journal) {
387
+ this.journal = JSON.parse(JSON.stringify(journal));
388
+ }
389
+ /* ── Events ───────────────────────────────────────────────────── */
390
+ emitEvent(type, questId, data) {
391
+ this.events.emit(type, {
392
+ type,
393
+ questId,
394
+ data,
395
+ timestamp: Date.now(),
396
+ });
397
+ }
398
+ subscribe(eventType, handler) {
399
+ return this.events.on(eventType, handler);
400
+ }
401
+ }
402
+ /* ═══════════════════════════════════════════════════════════════
403
+ QUEST BUILDER (Fluent API)
404
+ ══════════════════════════════════════════════════════════════════ */
405
+ class QuestBuilder {
406
+ constructor(id, name) {
407
+ this.objectiveCounter = 0;
408
+ this.rewardCounter = 0;
409
+ this.quest = {
410
+ id,
411
+ name,
412
+ summary: '',
413
+ description: '',
414
+ type: 'side',
415
+ difficulty: 'normal',
416
+ objectives: [],
417
+ rewards: [],
418
+ prerequisites: [],
419
+ canAbandon: true,
420
+ repeatable: false,
421
+ };
422
+ }
423
+ summary(summary) {
424
+ this.quest.summary = summary;
425
+ return this;
426
+ }
427
+ description(description) {
428
+ this.quest.description = description;
429
+ return this;
430
+ }
431
+ type(type) {
432
+ this.quest.type = type;
433
+ return this;
434
+ }
435
+ difficulty(difficulty) {
436
+ this.quest.difficulty = difficulty;
437
+ return this;
438
+ }
439
+ icon(icon) {
440
+ this.quest.icon = icon;
441
+ return this;
442
+ }
443
+ giver(npcId) {
444
+ this.quest.giverNpcId = npcId;
445
+ return this;
446
+ }
447
+ turnIn(npcId) {
448
+ this.quest.turnInNpcId = npcId;
449
+ return this;
450
+ }
451
+ location(hint) {
452
+ this.quest.locationHint = hint;
453
+ return this;
454
+ }
455
+ level(level) {
456
+ this.quest.recommendedLevel = level;
457
+ return this;
458
+ }
459
+ timeLimit(seconds) {
460
+ this.quest.timeLimit = seconds;
461
+ return this;
462
+ }
463
+ cannotAbandon() {
464
+ this.quest.canAbandon = false;
465
+ return this;
466
+ }
467
+ repeatable(cooldownMs = 0) {
468
+ this.quest.repeatable = true;
469
+ this.quest.repeatCooldown = cooldownMs;
470
+ return this;
471
+ }
472
+ tags(tags) {
473
+ this.quest.tags = tags;
474
+ return this;
475
+ }
476
+ followUp(questIds) {
477
+ this.quest.followUpQuests = questIds;
478
+ return this;
479
+ }
480
+ /* ── Prerequisites ────────────────────────────────────────────── */
481
+ requireQuest(questId) {
482
+ this.quest.prerequisites.push({ type: 'quest', questId });
483
+ return this;
484
+ }
485
+ requireLevel(level) {
486
+ this.quest.prerequisites.push({ type: 'level', level });
487
+ return this;
488
+ }
489
+ requireItem(itemId) {
490
+ this.quest.prerequisites.push({ type: 'item', itemId });
491
+ return this;
492
+ }
493
+ requireReputation(factionId, amount) {
494
+ this.quest.prerequisites.push({ type: 'reputation', reputation: { factionId, amount } });
495
+ return this;
496
+ }
497
+ requireSkill(skillId) {
498
+ this.quest.prerequisites.push({ type: 'skill', skillId });
499
+ return this;
500
+ }
501
+ requireCustom(customKey) {
502
+ this.quest.prerequisites.push({ type: 'custom', customKey });
503
+ return this;
504
+ }
505
+ /* ── Objectives ───────────────────────────────────────────────── */
506
+ addObjective(objective) {
507
+ const id = `obj_${++this.objectiveCounter}`;
508
+ this.quest.objectives.push({
509
+ ...objective,
510
+ id,
511
+ current: 0,
512
+ completed: false,
513
+ });
514
+ return this;
515
+ }
516
+ collect(itemId, amount, description, optional = false) {
517
+ return this.addObjective({
518
+ type: 'collect',
519
+ description,
520
+ target: amount,
521
+ optional,
522
+ trackingData: { itemId },
523
+ });
524
+ }
525
+ kill(enemyType, amount, description, optional = false) {
526
+ return this.addObjective({
527
+ type: 'kill',
528
+ description,
529
+ target: amount,
530
+ optional,
531
+ trackingData: { enemyType },
532
+ });
533
+ }
534
+ interact(entityId, description, optional = false) {
535
+ return this.addObjective({
536
+ type: 'interact',
537
+ description,
538
+ target: 1,
539
+ optional,
540
+ trackingData: { entityId },
541
+ });
542
+ }
543
+ reach(locationId, description, optional = false) {
544
+ return this.addObjective({
545
+ type: 'reach',
546
+ description,
547
+ target: 1,
548
+ optional,
549
+ trackingData: { locationId },
550
+ });
551
+ }
552
+ escort(npcId, locationId, description, optional = false) {
553
+ return this.addObjective({
554
+ type: 'escort',
555
+ description,
556
+ target: 1,
557
+ optional,
558
+ trackingData: { npcId, locationId },
559
+ });
560
+ }
561
+ defend(seconds, description, optional = false) {
562
+ return this.addObjective({
563
+ type: 'defend',
564
+ description,
565
+ target: seconds,
566
+ optional,
567
+ });
568
+ }
569
+ survive(seconds, description, optional = false) {
570
+ return this.addObjective({
571
+ type: 'survive',
572
+ description,
573
+ target: seconds,
574
+ optional,
575
+ });
576
+ }
577
+ craft(itemId, amount, description, optional = false) {
578
+ return this.addObjective({
579
+ type: 'craft',
580
+ description,
581
+ target: amount,
582
+ optional,
583
+ trackingData: { itemId },
584
+ });
585
+ }
586
+ talk(npcId, description, optional = false) {
587
+ return this.addObjective({
588
+ type: 'talk',
589
+ description,
590
+ target: 1,
591
+ optional,
592
+ trackingData: { npcId },
593
+ });
594
+ }
595
+ discover(locationId, description, optional = false) {
596
+ return this.addObjective({
597
+ type: 'discover',
598
+ description,
599
+ target: 1,
600
+ optional,
601
+ trackingData: { locationId },
602
+ });
603
+ }
604
+ score(threshold, description, optional = false) {
605
+ return this.addObjective({
606
+ type: 'score',
607
+ description,
608
+ target: threshold,
609
+ optional,
610
+ });
611
+ }
612
+ customObjective(type, target, description, data, optional = false) {
613
+ return this.addObjective({
614
+ type: 'custom',
615
+ description,
616
+ target,
617
+ optional,
618
+ trackingData: data,
619
+ });
620
+ }
621
+ /* ── Rewards ──────────────────────────────────────────────────── */
622
+ generateRewardId() {
623
+ return `reward_${++this.rewardCounter}`;
624
+ }
625
+ rewardXP(amount, name = 'Experience') {
626
+ this.quest.rewards.push({
627
+ id: this.generateRewardId(),
628
+ type: 'experience',
629
+ name,
630
+ amount,
631
+ });
632
+ return this;
633
+ }
634
+ rewardCurrency(amount, currencyName = 'Gold') {
635
+ this.quest.rewards.push({
636
+ id: this.generateRewardId(),
637
+ type: 'currency',
638
+ name: currencyName,
639
+ amount,
640
+ });
641
+ return this;
642
+ }
643
+ rewardItem(itemId, name, quantity = 1, icon) {
644
+ this.quest.rewards.push({
645
+ id: this.generateRewardId(),
646
+ type: 'item',
647
+ name,
648
+ itemId,
649
+ quantity,
650
+ icon,
651
+ });
652
+ return this;
653
+ }
654
+ rewardSkill(skillId, name, icon) {
655
+ this.quest.rewards.push({
656
+ id: this.generateRewardId(),
657
+ type: 'skill',
658
+ name,
659
+ skillId,
660
+ icon,
661
+ });
662
+ return this;
663
+ }
664
+ rewardReputation(factionId, amount, factionName) {
665
+ this.quest.rewards.push({
666
+ id: this.generateRewardId(),
667
+ type: 'reputation',
668
+ name: `${factionName} Reputation`,
669
+ reputation: { factionId, amount },
670
+ });
671
+ return this;
672
+ }
673
+ rewardUnlock(unlockId, name, icon) {
674
+ this.quest.rewards.push({
675
+ id: this.generateRewardId(),
676
+ type: 'unlock',
677
+ name,
678
+ unlockId,
679
+ icon,
680
+ });
681
+ return this;
682
+ }
683
+ rewardAchievement(achievementId, name, icon) {
684
+ this.quest.rewards.push({
685
+ id: this.generateRewardId(),
686
+ type: 'achievement',
687
+ name,
688
+ unlockId: achievementId,
689
+ icon,
690
+ });
691
+ return this;
692
+ }
693
+ bonusXP(amount, name = 'Bonus Experience') {
694
+ if (!this.quest.bonusRewards)
695
+ this.quest.bonusRewards = [];
696
+ this.quest.bonusRewards.push({
697
+ id: this.generateRewardId(),
698
+ type: 'experience',
699
+ name,
700
+ amount,
701
+ });
702
+ return this;
703
+ }
704
+ bonusItem(itemId, name, quantity = 1, icon) {
705
+ if (!this.quest.bonusRewards)
706
+ this.quest.bonusRewards = [];
707
+ this.quest.bonusRewards.push({
708
+ id: this.generateRewardId(),
709
+ type: 'item',
710
+ name,
711
+ itemId,
712
+ quantity,
713
+ icon,
714
+ });
715
+ return this;
716
+ }
717
+ build() {
718
+ return { ...this.quest };
719
+ }
720
+ }
721
+ const QuestList = ({ quests, questDefinitions, trackedId, onSelect, onTrack, filter, className = '', style, }) => {
722
+ const filteredQuests = filter
723
+ ? quests.filter(q => {
724
+ const def = questDefinitions.get(q.questId);
725
+ return def && filter.includes(def.type);
726
+ })
727
+ : quests;
728
+ return React.createElement('div', {
729
+ className: `nice-quest-list ${className}`,
730
+ style: {
731
+ display: 'flex',
732
+ flexDirection: 'column',
733
+ gap: '8px',
734
+ ...style,
735
+ },
736
+ }, filteredQuests.map(state => {
737
+ const quest = questDefinitions.get(state.questId);
738
+ if (!quest)
739
+ return null;
740
+ const progress = state.objectives.filter(o => !o.optional);
741
+ const completed = progress.filter(o => o.completed).length;
742
+ const total = progress.length;
743
+ const isTracked = trackedId === state.questId;
744
+ return React.createElement('div', {
745
+ key: state.questId,
746
+ className: `nice-quest-item ${isTracked ? 'tracked' : ''}`,
747
+ onClick: () => onSelect === null || onSelect === void 0 ? void 0 : onSelect(state.questId),
748
+ style: {
749
+ padding: '12px',
750
+ backgroundColor: isTracked ? 'rgba(100, 181, 246, 0.2)' : 'rgba(255, 255, 255, 0.05)',
751
+ border: isTracked ? '2px solid #64b5f6' : '2px solid transparent',
752
+ borderRadius: '8px',
753
+ cursor: 'pointer',
754
+ },
755
+ }, [
756
+ React.createElement('div', {
757
+ key: 'header',
758
+ style: { display: 'flex', justifyContent: 'space-between', marginBottom: '4px' },
759
+ }, [
760
+ React.createElement('span', {
761
+ key: 'name',
762
+ style: { fontWeight: 'bold' },
763
+ }, quest.name),
764
+ React.createElement('span', {
765
+ key: 'type',
766
+ style: {
767
+ fontSize: '12px',
768
+ padding: '2px 6px',
769
+ backgroundColor: getTypeColor(quest.type),
770
+ borderRadius: '4px',
771
+ },
772
+ }, quest.type.toUpperCase()),
773
+ ]),
774
+ React.createElement('div', {
775
+ key: 'progress',
776
+ style: { display: 'flex', alignItems: 'center', gap: '8px', marginTop: '8px' },
777
+ }, [
778
+ React.createElement('div', {
779
+ key: 'bar',
780
+ style: {
781
+ flex: 1,
782
+ height: '4px',
783
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
784
+ borderRadius: '2px',
785
+ overflow: 'hidden',
786
+ },
787
+ }, React.createElement('div', {
788
+ style: {
789
+ width: `${(completed / total) * 100}%`,
790
+ height: '100%',
791
+ backgroundColor: '#4caf50',
792
+ },
793
+ })),
794
+ React.createElement('span', {
795
+ key: 'text',
796
+ style: { fontSize: '12px', opacity: 0.7 },
797
+ }, `${completed}/${total}`),
798
+ onTrack && React.createElement('button', {
799
+ key: 'track',
800
+ onClick: (e) => {
801
+ e.stopPropagation();
802
+ onTrack(state.questId);
803
+ },
804
+ style: {
805
+ padding: '4px 8px',
806
+ fontSize: '10px',
807
+ backgroundColor: isTracked ? '#64b5f6' : 'transparent',
808
+ border: '1px solid #64b5f6',
809
+ borderRadius: '4px',
810
+ color: isTracked ? '#000' : '#64b5f6',
811
+ cursor: 'pointer',
812
+ },
813
+ }, isTracked ? 'TRACKED' : 'TRACK'),
814
+ ]),
815
+ ]);
816
+ }));
817
+ };
818
+ function getTypeColor(type) {
819
+ switch (type) {
820
+ case 'main': return '#f44336';
821
+ case 'side': return '#2196f3';
822
+ case 'daily': return '#4caf50';
823
+ case 'weekly': return '#9c27b0';
824
+ case 'achievement': return '#ff9800';
825
+ case 'tutorial': return '#00bcd4';
826
+ case 'hidden': return '#607d8b';
827
+ default: return '#9e9e9e';
828
+ }
829
+ }
830
+ const QuestTracker = ({ state, quest, showOptional = false, className = '', style, }) => {
831
+ const objectives = showOptional
832
+ ? state.objectives
833
+ : state.objectives.filter(o => !o.optional);
834
+ return React.createElement('div', {
835
+ className: `nice-quest-tracker ${className}`,
836
+ style: {
837
+ padding: '12px',
838
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
839
+ borderRadius: '8px',
840
+ color: 'white',
841
+ minWidth: '200px',
842
+ ...style,
843
+ },
844
+ }, [
845
+ React.createElement('div', {
846
+ key: 'header',
847
+ style: {
848
+ fontWeight: 'bold',
849
+ marginBottom: '8px',
850
+ paddingBottom: '8px',
851
+ borderBottom: '1px solid rgba(255, 255, 255, 0.2)',
852
+ },
853
+ }, quest.name),
854
+ React.createElement('div', {
855
+ key: 'objectives',
856
+ style: { display: 'flex', flexDirection: 'column', gap: '4px' },
857
+ }, objectives.map(obj => React.createElement('div', {
858
+ key: obj.id,
859
+ style: {
860
+ display: 'flex',
861
+ alignItems: 'center',
862
+ gap: '8px',
863
+ opacity: obj.completed ? 0.5 : 1,
864
+ textDecoration: obj.completed ? 'line-through' : 'none',
865
+ fontSize: '14px',
866
+ },
867
+ }, [
868
+ React.createElement('span', {
869
+ key: 'check',
870
+ style: { color: obj.completed ? '#4caf50' : '#9e9e9e' },
871
+ }, obj.completed ? '✓' : '○'),
872
+ React.createElement('span', { key: 'desc' }, obj.description),
873
+ obj.target > 1 && React.createElement('span', {
874
+ key: 'progress',
875
+ style: { marginLeft: 'auto', fontSize: '12px', opacity: 0.7 },
876
+ }, `${obj.current}/${obj.target}`),
877
+ ]))),
878
+ state.timeRemaining !== undefined && state.timeRemaining > 0 && React.createElement('div', {
879
+ key: 'timer',
880
+ style: {
881
+ marginTop: '8px',
882
+ paddingTop: '8px',
883
+ borderTop: '1px solid rgba(255, 255, 255, 0.2)',
884
+ textAlign: 'center',
885
+ color: state.timeRemaining < 30000 ? '#f44336' : '#fff',
886
+ },
887
+ }, `Time: ${formatTime(state.timeRemaining)}`),
888
+ ]);
889
+ };
890
+ function formatTime(ms) {
891
+ const seconds = Math.floor(ms / 1000);
892
+ const minutes = Math.floor(seconds / 60);
893
+ const remainingSeconds = seconds % 60;
894
+ return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
895
+ }
896
+ /* ═══════════════════════════════════════════════════════════════
897
+ UTILITY FUNCTIONS
898
+ ══════════════════════════════════════════════════════════════════ */
899
+ function createEmptyQuest(id, name) {
900
+ return new QuestBuilder(id, name).build();
901
+ }
902
+ const DIFFICULTY_COLORS = {
903
+ trivial: '#9e9e9e',
904
+ easy: '#4caf50',
905
+ normal: '#2196f3',
906
+ hard: '#ff9800',
907
+ legendary: '#f44336',
908
+ };
909
+ const DIFFICULTY_XP_MULTIPLIERS = {
910
+ trivial: 0.5,
911
+ easy: 0.75,
912
+ normal: 1,
913
+ hard: 1.5,
914
+ legendary: 2,
915
+ };
916
+
917
+ exports.DIFFICULTY_COLORS = DIFFICULTY_COLORS;
918
+ exports.DIFFICULTY_XP_MULTIPLIERS = DIFFICULTY_XP_MULTIPLIERS;
919
+ exports.QuestBuilder = QuestBuilder;
920
+ exports.QuestList = QuestList;
921
+ exports.QuestManager = QuestManager;
922
+ exports.QuestTracker = QuestTracker;
923
+ exports.createEmptyQuest = createEmptyQuest;
924
+ //# sourceMappingURL=QuestSystem.js.map