dopaminejs 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dopamine.mjs CHANGED
@@ -1,52 +1,55 @@
1
- class v {
1
+ class p {
2
2
  constructor() {
3
- this.storage = window.localStorage;
3
+ this.listeners = /* @__PURE__ */ new Map();
4
4
  }
5
5
  /**
6
- * Save data to local storage
7
- * @param {string} key
8
- * @param {any} data
6
+ * Subscribe to an event
7
+ * @param {string} event - Event name
8
+ * @param {Function} callback - Callback function
9
+ * @returns {Function} Unsubscribe function
9
10
  */
10
- async save(e, t) {
11
- try {
12
- const i = JSON.stringify(t);
13
- return this.storage.setItem(e, i), !0;
14
- } catch (i) {
15
- return console.error("Error saving data:", i), !1;
16
- }
11
+ on(t, e) {
12
+ return this.listeners.has(t) || this.listeners.set(t, /* @__PURE__ */ new Set()), this.listeners.get(t).add(e), () => this.off(t, e);
17
13
  }
18
14
  /**
19
- * Load data from local storage
20
- * @param {string} key
21
- * @param {any} defaultValue
15
+ * Unsubscribe from an event
16
+ * @param {string} event - Event name
17
+ * @param {Function} callback - Callback function
22
18
  */
23
- async load(e, t = null) {
24
- try {
25
- const i = this.storage.getItem(e);
26
- return i ? JSON.parse(i) : t;
27
- } catch (i) {
28
- return console.error("Error loading data:", i), t;
29
- }
19
+ off(t, e) {
20
+ this.listeners.has(t) && this.listeners.get(t).delete(e);
30
21
  }
31
22
  /**
32
- * Clear specific data
33
- * @param {string} key
23
+ * Emit an event
24
+ * @param {string} event - Event name
25
+ * @param {*} data - Event data
26
+ */
27
+ emit(t, e) {
28
+ this.listeners.has(t) && this.listeners.get(t).forEach((s) => {
29
+ try {
30
+ s(e);
31
+ } catch (i) {
32
+ console.error(`Error in listener for event '${t}':`, i);
33
+ }
34
+ });
35
+ }
36
+ /**
37
+ * Clear all listeners
34
38
  */
35
- async clear(e) {
36
- this.storage.removeItem(e);
39
+ clear() {
40
+ this.listeners.clear();
37
41
  }
38
42
  }
39
- const m = new v();
40
- class u {
41
- constructor() {
42
- this.player = null, this.achievements = this._initAchievements(), this.listeners = [];
43
+ class m extends p {
44
+ constructor(t, e = {}) {
45
+ super(), this.dataService = t, this.player = null, this.achievements = { ...this._initAchievements(), ...e.achievements || {} };
43
46
  }
44
47
  /**
45
48
  * Initialize the reward system for current player
46
49
  * @returns {Promise<Object>} Player data
47
50
  */
48
51
  async init() {
49
- return this.player = await m.load("player", this._getDefaultPlayer()), this._updateDailyStreak(), await this.save(), this.player;
52
+ return this.player = await this.dataService.load("player", this._getDefaultPlayer()), this._updateDailyStreak(), await this.save(), this.player;
50
53
  }
51
54
  /**
52
55
  * Get default player data structure
@@ -75,7 +78,7 @@ class u {
75
78
  * Save player data
76
79
  */
77
80
  async save() {
78
- this.player && (this.player.lastPlayedAt = Date.now(), await m.save("player", this.player));
81
+ this.player && (this.player.lastPlayedAt = Date.now(), await this.dataService.save("player", this.player));
79
82
  }
80
83
  /**
81
84
  * Add XP and handle level ups
@@ -83,28 +86,28 @@ class u {
83
86
  * @param {string} reason - Why XP was awarded (for notifications)
84
87
  * @returns {Object} { leveledUp: boolean, newLevel: number, xpGained: number }
85
88
  */
86
- async addXP(e, t = "") {
87
- const i = this.player.level;
88
- this.player.xp += e;
89
- const s = this._calculateLevel(this.player.xp), a = s > i;
90
- return a && (this.player.level = s), await this.save(), this._emit("xp_gained", { amount: e, reason: t, leveledUp: a, newLevel: s }), a && this._emit("level_up", { oldLevel: i, newLevel: s }), { leveledUp: a, newLevel: s, xpGained: e };
89
+ async addXP(t, e = "") {
90
+ const s = this.player.level;
91
+ this.player.xp += t;
92
+ const i = this._calculateLevel(this.player.xp), a = i > s;
93
+ return a && (this.player.level = i), await this.save(), this.emit("xp_gained", { amount: t, reason: e, leveledUp: a, newLevel: i }), a && this.emit("level_up", { oldLevel: s, newLevel: i }), { leveledUp: a, newLevel: i, xpGained: t };
91
94
  }
92
95
  /**
93
96
  * Calculate level from XP (exponential curve)
94
97
  */
95
- _calculateLevel(e) {
96
- const t = Math.floor((1 + Math.sqrt(1 + 8 * e / 50)) / 2);
97
- return Math.max(1, t);
98
+ _calculateLevel(t) {
99
+ const e = Math.floor((1 + Math.sqrt(1 + 8 * t / 50)) / 2);
100
+ return Math.max(1, e);
98
101
  }
99
102
  /**
100
103
  * Get XP needed for next level
101
104
  */
102
105
  getXPForNextLevel() {
103
- const e = this.player.level + 1, t = 50 * e * (e - 1), i = 50 * this.player.level * (this.player.level - 1);
106
+ const t = this.player.level + 1, e = 50 * t * (t - 1), s = 50 * this.player.level * (this.player.level - 1);
104
107
  return {
105
- total: t,
106
- needed: t - this.player.xp,
107
- progress: (this.player.xp - i) / (t - i)
108
+ total: e,
109
+ needed: e - this.player.xp,
110
+ progress: (this.player.xp - s) / (e - s)
108
111
  };
109
112
  }
110
113
  /**
@@ -112,74 +115,74 @@ class u {
112
115
  * @param {string} gameName - Unique ID for the game
113
116
  * @param {Object} result - Game-specific result data
114
117
  */
115
- async recordGame(e, t) {
116
- this.player.stats[e] || (this.player.stats[e] = { totalPlays: 0, highScore: 0 });
117
- const i = this.player.stats[e];
118
- i.totalPlays++, this.player.totalGamesPlayed++, t.score !== void 0 && t.score > i.highScore && (i.highScore = t.score, this._emit("new_high_score", { gameName: e, score: t.score })), Object.keys(t).forEach((a) => {
119
- a !== "score" && (i[a] = (i[a] || 0) + (typeof t[a] == "number" ? t[a] : 0));
118
+ async recordGame(t, e) {
119
+ this.player.stats[t] || (this.player.stats[t] = { totalPlays: 0, highScore: 0 });
120
+ const s = this.player.stats[t];
121
+ s.totalPlays++, this.player.totalGamesPlayed++, e.score !== void 0 && e.score > s.highScore && (s.highScore = e.score, this.emit("new_high_score", { gameName: t, score: e.score })), Object.keys(e).forEach((a) => {
122
+ a !== "score" && (s[a] = (s[a] || 0) + (typeof e[a] == "number" ? e[a] : 0));
120
123
  });
121
- let s = 10;
122
- t.score && (s += Math.floor(t.score / 5)), s = Math.floor(s * this.getStreakMultiplier()), await this.addXP(s, `Played ${e}`), await this.checkAchievements(e, t), await this.save();
124
+ let i = 10;
125
+ e.score && (i += Math.floor(e.score / 5)), i = Math.floor(i * this.getStreakMultiplier()), await this.addXP(i, `Played ${t}`), await this.checkAchievements(t, e), await this.save();
123
126
  }
124
127
  /**
125
128
  * Check and unlock achievements
126
129
  */
127
- async checkAchievements(e, t) {
128
- const i = [];
129
- for (const [s, a] of Object.entries(this.achievements))
130
- this.player.achievements[s] || a.check(this.player, e, t) && (await this.unlockAchievement(s), i.push(a));
131
- return i;
130
+ async checkAchievements(t, e) {
131
+ const s = [];
132
+ for (const [i, a] of Object.entries(this.achievements))
133
+ this.player.achievements[i] || a.check(this.player, t, e) && (await this.unlockAchievement(i), s.push(a));
134
+ return s;
132
135
  }
133
136
  /**
134
137
  * Unlock an achievement
135
138
  */
136
- async unlockAchievement(e) {
137
- const t = this.achievements[e];
138
- return t ? (this.player.achievements[e] = {
139
+ async unlockAchievement(t) {
140
+ const e = this.achievements[t];
141
+ return e ? (this.player.achievements[t] = {
139
142
  unlockedAt: Date.now(),
140
143
  seen: !1
141
- }, await this.addXP(t.xp, `Achievement: ${t.name}`), this._emit("achievement_unlocked", t), await this.save(), !0) : !1;
144
+ }, await this.addXP(e.xp, `Achievement: ${e.name}`), this.emit("achievement_unlocked", e), await this.save(), !0) : !1;
142
145
  }
143
146
  /**
144
147
  * Get all unlocked achievements
145
148
  */
146
149
  getUnlockedAchievements() {
147
- return Object.entries(this.player.achievements).map(([e, t]) => ({
148
- ...this.achievements[e],
149
- id: e,
150
- ...t
150
+ return Object.entries(this.player.achievements).map(([t, e]) => ({
151
+ ...this.achievements[t],
152
+ id: t,
153
+ ...e
151
154
  }));
152
155
  }
153
156
  /**
154
157
  * Get achievements user hasn't seen the notification for yet
155
158
  */
156
159
  getUnseenAchievements() {
157
- return this.getUnlockedAchievements().filter((e) => !e.seen);
160
+ return this.getUnlockedAchievements().filter((t) => !t.seen);
158
161
  }
159
162
  /**
160
163
  * Mark achievements as seen
161
164
  */
162
- async markAchievementsSeen(e) {
163
- for (const t of e)
164
- this.player.achievements[t] && (this.player.achievements[t].seen = !0);
165
+ async markAchievementsSeen(t) {
166
+ for (const e of t)
167
+ this.player.achievements[e] && (this.player.achievements[e].seen = !0);
165
168
  await this.save();
166
169
  }
167
170
  /**
168
171
  * Update daily streak
169
172
  */
170
173
  _updateDailyStreak() {
171
- const e = this._getTodayDateString(), t = this.player.streak.lastPlayDate;
172
- if (t === e)
174
+ const t = this._getTodayDateString(), e = this.player.streak.lastPlayDate;
175
+ if (e === t)
173
176
  return;
174
- const i = this._getYesterdayDateString();
175
- t === i ? (this.player.streak.current++, this.player.streak.current > this.player.streak.longest && (this.player.streak.longest = this.player.streak.current)) : this.player.streak.current = 1, this.player.streak.lastPlayDate = e;
177
+ const s = this._getYesterdayDateString();
178
+ e === s ? (this.player.streak.current++, this.player.streak.current > this.player.streak.longest && (this.player.streak.longest = this.player.streak.current)) : this.player.streak.current = 1, this.player.streak.lastPlayDate = t;
176
179
  }
177
180
  /**
178
181
  * Get streak multiplier for XP
179
182
  */
180
183
  getStreakMultiplier() {
181
- const e = this.player.streak.current;
182
- return e >= 7 ? 2 : e >= 5 ? 1.5 : e >= 3 ? 1.25 : 1;
184
+ const t = this.player.streak.current;
185
+ return t >= 7 ? 2 : t >= 5 ? 1.5 : t >= 3 ? 1.25 : 1;
183
186
  }
184
187
  /**
185
188
  * Get today's date as YYYY-MM-DD string
@@ -191,17 +194,8 @@ class u {
191
194
  * Get yesterday's date as YYYY-MM-DD string
192
195
  */
193
196
  _getYesterdayDateString() {
194
- const e = /* @__PURE__ */ new Date();
195
- return e.setDate(e.getDate() - 1), e.toISOString().split("T")[0];
196
- }
197
- /**
198
- * Event system for UI updates
199
- */
200
- on(e, t) {
201
- this.listeners.push({ event: e, callback: t });
202
- }
203
- _emit(e, t) {
204
- this.listeners.filter((i) => i.event === e).forEach((i) => i.callback(t));
197
+ const t = /* @__PURE__ */ new Date();
198
+ return t.setDate(t.getDate() - 1), t.toISOString().split("T")[0];
205
199
  }
206
200
  /**
207
201
  * Initialize achievement definitions
@@ -215,50 +209,230 @@ class u {
215
209
  description: "Play your first game",
216
210
  icon: "🎮",
217
211
  xp: 50,
218
- check: (e) => e.totalGamesPlayed >= 1
212
+ check: (t) => t.totalGamesPlayed >= 1
219
213
  },
220
214
  level_5: {
221
215
  name: "Rising Star",
222
216
  description: "Reach level 5",
223
217
  icon: "⭐",
224
218
  xp: 100,
225
- check: (e) => e.level >= 5
219
+ check: (t) => t.level >= 5
226
220
  },
227
221
  level_10: {
228
222
  name: "Expert Player",
229
223
  description: "Reach level 10",
230
224
  icon: "🏆",
231
225
  xp: 250,
232
- check: (e) => e.level >= 10
226
+ check: (t) => t.level >= 10
233
227
  },
234
228
  streak_3: {
235
229
  name: "Dedicated",
236
230
  description: "Play 3 days in a row",
237
231
  icon: "🔥",
238
232
  xp: 100,
239
- check: (e) => e.streak.current >= 3
233
+ check: (t) => t.streak.current >= 3
240
234
  },
241
235
  streak_7: {
242
236
  name: "Unstoppable",
243
237
  description: "Play 7 days in a row",
244
238
  icon: "💥",
245
239
  xp: 300,
246
- check: (e) => e.streak.current >= 7
240
+ check: (t) => t.streak.current >= 7
247
241
  }
248
242
  };
249
243
  }
244
+ }
245
+ class u {
246
+ constructor(t = {}) {
247
+ this.storage = t.storage || window.localStorage, this.prefix = t.prefix || "dopamine_";
248
+ }
249
+ /**
250
+ * Save data to local storage
251
+ * @param {string} key
252
+ * @param {any} data
253
+ */
254
+ async save(t, e) {
255
+ try {
256
+ const s = JSON.stringify(e);
257
+ return this.storage.setItem(this.prefix + t, s), !0;
258
+ } catch (s) {
259
+ return console.error("Error saving data:", s), !1;
260
+ }
261
+ }
262
+ /**
263
+ * Load data from local storage
264
+ * @param {string} key
265
+ * @param {any} defaultValue
266
+ */
267
+ async load(t, e = null) {
268
+ try {
269
+ const s = this.storage.getItem(this.prefix + t);
270
+ return s ? JSON.parse(s) : e;
271
+ } catch (s) {
272
+ return console.error("Error loading data:", s), e;
273
+ }
274
+ }
250
275
  /**
251
- * Register custom achievements
252
- * @param {Object} achievements
276
+ * Clear specific data
277
+ * @param {string} key
253
278
  */
254
- registerAchievements(e) {
255
- this.achievements = { ...this.achievements, ...e };
279
+ async clear(t) {
280
+ this.storage.removeItem(this.prefix + t);
256
281
  }
257
282
  }
258
- const y = new u();
259
- class f {
260
- constructor(e = "dopamine-particle-canvas") {
261
- this.canvasId = e, this.canvas = document.getElementById(e), this.canvas || (this.canvas = document.createElement("canvas"), this.canvas.id = e, this.canvas.style.position = "fixed", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.pointerEvents = "none", this.canvas.style.zIndex = "9999", document.body.appendChild(this.canvas)), this.ctx = this.canvas.getContext("2d"), this.particles = [], this.isAnimating = !1, this._resize(), window.addEventListener("resize", () => this._resize());
283
+ class y {
284
+ constructor(t) {
285
+ this.particleSystem = t, this.container = null, this.xpBar = null, this.levelDisplay = null, this.streakDisplay = null, this.init();
286
+ }
287
+ /**
288
+ * Initialize UI overlay
289
+ */
290
+ init() {
291
+ this.container = document.createElement("div"), this.container.id = "game-ui-overlay", this.container.innerHTML = `
292
+ <div class="game-ui-top-bar">
293
+ <div class="level-badge" id="level-badge">
294
+ <span class="level-label">LVL</span>
295
+ <span class="level-number" id="level-number">1</span>
296
+ </div>
297
+
298
+ <div class="xp-container">
299
+ <div class="xp-bar-bg">
300
+ <div class="xp-bar-fill" id="xp-bar-fill" style="width: 0%"></div>
301
+ </div>
302
+ <div class="xp-text" id="xp-text">0 /100 XP</div>
303
+ </div>
304
+
305
+ <div class="streak-badge" id="streak-badge">
306
+ <span class="streak-icon">🔥</span>
307
+ <span class="streak-number" id="streak-number">1</span>
308
+ </div>
309
+ </div>
310
+ `, document.body.appendChild(this.container), this.xpBar = document.getElementById("xp-bar-fill"), this.xpText = document.getElementById("xp-text"), this.levelDisplay = document.getElementById("level-number"), this.streakDisplay = document.getElementById("streak-number");
311
+ }
312
+ /**
313
+ * Update XP bar
314
+ */
315
+ updateXP(t, e, s) {
316
+ const i = t / s * 100;
317
+ this.xpBar.style.width = `${Math.min(100, i)}%`, this.xpText.textContent = `${t} / ${s} XP`, this.xpBar.style.animation = "none", setTimeout(() => {
318
+ this.xpBar.style.animation = "xp-pulse 0.3s ease-out";
319
+ }, 10);
320
+ }
321
+ /**
322
+ * Update level display
323
+ */
324
+ updateLevel(t) {
325
+ this.levelDisplay.textContent = t;
326
+ const e = document.getElementById("level-badge");
327
+ e.style.animation = "none", setTimeout(() => {
328
+ e.style.animation = "level-pulse 0.5s ease-out";
329
+ }, 10);
330
+ }
331
+ /**
332
+ * Update streak display
333
+ */
334
+ updateStreak(t) {
335
+ this.streakDisplay.textContent = t;
336
+ const e = document.getElementById("streak-badge");
337
+ t >= 7 ? e.style.background = "linear-gradient(135deg, #ff6b00 0%, #ff4400 100%)" : t >= 3 && (e.style.background = "linear-gradient(135deg, #ff8800 0%, #ff6b00 100%)");
338
+ }
339
+ /**
340
+ * Show achievement unlocked popup
341
+ */
342
+ showAchievement(t) {
343
+ const e = document.createElement("div");
344
+ e.className = "achievement-popup", e.innerHTML = `
345
+ <div class="achievement-shine"></div>
346
+ <div class="achievement-content">
347
+ <div class="achievement-icon">${t.icon}</div>
348
+ <div class="achievement-info">
349
+ <div class="achievement-title">Achievement Unlocked!</div>
350
+ <div class="achievement-name">${t.name}</div>
351
+ <div class="achievement-desc">${t.description}</div>
352
+ <div class="achievement-xp">+${t.xp} XP</div>
353
+ </div>
354
+ </div>
355
+ `, document.body.appendChild(e), setTimeout(() => {
356
+ const s = e.getBoundingClientRect();
357
+ this.particleSystem.confetti(s.left + s.width / 2, s.top + s.height / 2, 40);
358
+ }, 300), setTimeout(() => {
359
+ e.style.animation = "slideOut 0.3s ease-in forwards", setTimeout(() => e.remove(), 300);
360
+ }, 4e3);
361
+ }
362
+ /**
363
+ * Show level up screen
364
+ */
365
+ showLevelUp(t, e) {
366
+ const s = document.createElement("div");
367
+ s.className = "level-up-overlay", s.innerHTML = `
368
+ <div class="level-up-content">
369
+ <div class="level-up-title">LEVEL UP!</div>
370
+ <div class="level-up-number">${e}</div>
371
+ <div class="level-up-subtitle">Amazing progress!</div>
372
+ </div>
373
+ `, document.body.appendChild(s), setTimeout(() => {
374
+ for (let i = 0; i < 5; i++)
375
+ setTimeout(() => {
376
+ const a = Math.random() * window.innerWidth, n = Math.random() * window.innerHeight * 0.6;
377
+ this.particleSystem.confetti(a, n, 30);
378
+ }, i * 200);
379
+ }, 300), setTimeout(() => {
380
+ s.style.opacity = "0", setTimeout(() => s.remove(), 500);
381
+ }, 2500);
382
+ }
383
+ /**
384
+ * Show floating text (e.g., "+50 XP", "COMBO!")
385
+ */
386
+ showFloatingText(t, e, s, i = "#ffd700", a = "24px") {
387
+ const n = document.createElement("div");
388
+ n.className = "floating-text", n.textContent = t, n.style.left = e + "px", n.style.top = s + "px", n.style.color = i, n.style.fontSize = a, document.body.appendChild(n), setTimeout(() => n.remove(), 2e3);
389
+ }
390
+ /**
391
+ * Show combo multiplier
392
+ */
393
+ showCombo(t, e, s) {
394
+ this.showFloatingText(`${t}× COMBO!`, e, s, "#ff6b6b", "32px"), this.particleSystem.fire(e, s, 20);
395
+ }
396
+ /**
397
+ * Show "near miss"indicator
398
+ */
399
+ showNearMiss(t, e) {
400
+ this.showFloatingText("CLOSE CALL!", t, e, "#45b7d1", "20px"), this.particleSystem.sparkle(t, e, 15, "#45b7d1");
401
+ }
402
+ /**
403
+ * Show lucky moment (random multiplier)
404
+ */
405
+ showLuckyMoment(t, e, s) {
406
+ this.showFloatingText(`LUCKY ${t}×!`, e, s, "#ffd700", "36px"), this.particleSystem.starBurst(e, s, 12), this.particleSystem.confetti(e, s, 25);
407
+ }
408
+ /**
409
+ * Show generic notification
410
+ */
411
+ showNotification(t, e = "normal") {
412
+ const s = window.innerWidth / 2, i = window.innerHeight / 3;
413
+ e === "legendary" ? this.showLuckyMoment(t, s, i) : e === "rare" ? this.showCombo(t, s, i) : this.showFloatingText(t, s, i, "#fff", "24px");
414
+ }
415
+ /**
416
+ * Show Game Over Summary
417
+ */
418
+ showSummary(t) {
419
+ const e = document.createElement("div");
420
+ e.className = "message-box", e.style.display = "flex", e.style.position = "absolute", e.style.top = "50%", e.style.left = "50%", e.style.transform = "translate(-50%, -50%)", e.style.zIndex = "1000";
421
+ let s = "";
422
+ t.metrics && (s = Object.entries(t.metrics).map(([i, a]) => `<div style="display:flex;justify-content:space-between;width:100%"><span>${i}:</span> <strong>${a}</strong></div>`).join("")), e.innerHTML = `
423
+ <h1>Game Over</h1>
424
+ <div style="font-size: 3rem; font-weight: bold; color: #e06020; margin: 10px 0;">${t.score}</div>
425
+ <div style="width: 100%; margin-bottom: 20px;">
426
+ ${s}
427
+ </div>
428
+ <button class="btn" onclick="location.reload()">Play Again</button>
429
+ <button class="btn" onclick="window.location.href='index.html'" style="margin-top: 10px; background: #543847;">Exit</button>
430
+ `, document.body.appendChild(e);
431
+ }
432
+ }
433
+ class v {
434
+ constructor(t = {}) {
435
+ this.canvasId = t.canvasId || "dopamine-particle-canvas", this.canvas = document.getElementById(this.canvasId), this.canvas || (this.canvas = document.createElement("canvas"), this.canvas.id = this.canvasId, this.canvas.style.position = "fixed", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.pointerEvents = "none", this.canvas.style.zIndex = t.zIndex || "9999", document.body.appendChild(this.canvas)), this.ctx = this.canvas.getContext("2d"), this.particles = [], this.isAnimating = !1, this._resize(), window.addEventListener("resize", () => this._resize());
262
436
  }
263
437
  _resize() {
264
438
  this.canvas.width = window.innerWidth, this.canvas.height = window.innerHeight;
@@ -266,17 +440,17 @@ class f {
266
440
  /**
267
441
  * Confetti explosion (for achievements, level ups)
268
442
  */
269
- confetti(e, t, i = 50) {
270
- const s = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#f7dc6f", "#c06c84", "#6c5ce7"];
271
- for (let a = 0; a < i; a++)
443
+ confetti(t, e, s = 50) {
444
+ const i = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#f7dc6f", "#c06c84", "#6c5ce7"];
445
+ for (let a = 0; a < s; a++)
272
446
  this.particles.push({
273
- x: e,
274
- y: t,
447
+ x: t,
448
+ y: e,
275
449
  vx: (Math.random() - 0.5) * 10,
276
450
  vy: (Math.random() - 0.5) * 10 - 5,
277
451
  rotation: Math.random() * Math.PI * 2,
278
452
  rotationSpeed: (Math.random() - 0.5) * 0.2,
279
- color: s[Math.floor(Math.random() * s.length)],
453
+ color: i[Math.floor(Math.random() * i.length)],
280
454
  size: Math.random() * 8 + 4,
281
455
  life: 1,
282
456
  decay: 0.015 + Math.random() * 0.01,
@@ -288,15 +462,15 @@ class f {
288
462
  /**
289
463
  * Sparkle effect (for score milestones, combos)
290
464
  */
291
- sparkle(e, t, i = 20, s = "#ffd700") {
292
- for (let a = 0; a < i; a++) {
293
- const n = Math.PI * 2 / i * a, o = 2 + Math.random() * 3;
465
+ sparkle(t, e, s = 20, i = "#ffd700") {
466
+ for (let a = 0; a < s; a++) {
467
+ const n = Math.PI * 2 / s * a, r = 2 + Math.random() * 3;
294
468
  this.particles.push({
295
- x: e,
296
- y: t,
297
- vx: Math.cos(n) * o,
298
- vy: Math.sin(n) * o,
299
- color: s,
469
+ x: t,
470
+ y: e,
471
+ vx: Math.cos(n) * r,
472
+ vy: Math.sin(n) * r,
473
+ color: i,
300
474
  size: Math.random() * 4 + 2,
301
475
  life: 1,
302
476
  decay: 0.02,
@@ -310,11 +484,11 @@ class f {
310
484
  /**
311
485
  * Coin shower (for collecting coins)
312
486
  */
313
- coinShower(e, t, i = 10) {
314
- for (let s = 0; s < i; s++)
487
+ coinShower(t, e, s = 10) {
488
+ for (let i = 0; i < s; i++)
315
489
  this.particles.push({
316
- x: e + (Math.random() - 0.5) * 100,
317
- y: t - Math.random() * 50,
490
+ x: t + (Math.random() - 0.5) * 100,
491
+ y: e - Math.random() * 50,
318
492
  vx: (Math.random() - 0.5) * 3,
319
493
  vy: Math.random() * -2 - 3,
320
494
  color: "#ffd700",
@@ -329,15 +503,15 @@ class f {
329
503
  /**
330
504
  * Fire particles (for streaks, combos)
331
505
  */
332
- fire(e, t, i = 15) {
333
- const s = ["#ff6b00", "#ff8800", "#ffaa00", "#ff4400"];
334
- for (let a = 0; a < i; a++)
506
+ fire(t, e, s = 15) {
507
+ const i = ["#ff6b00", "#ff8800", "#ffaa00", "#ff4400"];
508
+ for (let a = 0; a < s; a++)
335
509
  this.particles.push({
336
- x: e + (Math.random() - 0.5) * 20,
337
- y: t,
510
+ x: t + (Math.random() - 0.5) * 20,
511
+ y: e,
338
512
  vx: (Math.random() - 0.5) * 2,
339
513
  vy: -Math.random() * 3 - 2,
340
- color: s[Math.floor(Math.random() * s.length)],
514
+ color: i[Math.floor(Math.random() * i.length)],
341
515
  size: Math.random() * 6 + 3,
342
516
  life: 1,
343
517
  decay: 0.025,
@@ -350,12 +524,12 @@ class f {
350
524
  /**
351
525
  * Star burst (for "perfect" moments)
352
526
  */
353
- starBurst(e, t, i = 8) {
354
- for (let s = 0; s < i; s++) {
355
- const a = Math.PI * 2 / i * s, n = 4;
527
+ starBurst(t, e, s = 8) {
528
+ for (let i = 0; i < s; i++) {
529
+ const a = Math.PI * 2 / s * i, n = 4;
356
530
  this.particles.push({
357
- x: e,
358
- y: t,
531
+ x: t,
532
+ y: e,
359
533
  vx: Math.cos(a) * n,
360
534
  vy: Math.sin(a) * n,
361
535
  rotation: a,
@@ -374,25 +548,25 @@ class f {
374
548
  }
375
549
  _animate() {
376
550
  this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
377
- for (let e = this.particles.length - 1; e >= 0; e--) {
378
- const t = this.particles[e];
379
- if (t.vy += t.gravity, t.x += t.vx, t.y += t.vy, t.life -= t.decay, t.rotationSpeed && (t.rotation += t.rotationSpeed), t.life <= 0) {
380
- this.particles.splice(e, 1);
551
+ for (let t = this.particles.length - 1; t >= 0; t--) {
552
+ const e = this.particles[t];
553
+ if (e.vy += e.gravity, e.x += e.vx, e.y += e.vy, e.life -= e.decay, e.rotationSpeed && (e.rotation += e.rotationSpeed), e.life <= 0) {
554
+ this.particles.splice(t, 1);
381
555
  continue;
382
556
  }
383
- this.ctx.save(), this.ctx.globalAlpha = t.life, this.ctx.fillStyle = t.color, t.type === "confetti" ? (this.ctx.translate(t.x, t.y), this.ctx.rotate(t.rotation), this.ctx.fillRect(-t.size / 2, -t.size / 2, t.size, t.size / 2)) : t.type === "coin" ? (this.ctx.beginPath(), this.ctx.arc(t.x, t.y, t.size, 0, Math.PI * 2), this.ctx.fill(), this.ctx.fillStyle = "#ffee88", this.ctx.beginPath(), this.ctx.arc(t.x - t.size / 3, t.y - t.size / 3, t.size / 3, 0, Math.PI * 2), this.ctx.fill()) : t.type === "star" ? this._drawStar(t.x, t.y, 5, t.size, t.size / 2, t.rotation) : (this.ctx.beginPath(), this.ctx.arc(t.x, t.y, t.size, 0, Math.PI * 2), this.ctx.fill()), this.ctx.restore();
557
+ this.ctx.save(), this.ctx.globalAlpha = e.life, this.ctx.fillStyle = e.color, e.type === "confetti" ? (this.ctx.translate(e.x, e.y), this.ctx.rotate(e.rotation), this.ctx.fillRect(-e.size / 2, -e.size / 2, e.size, e.size / 2)) : e.type === "coin" ? (this.ctx.beginPath(), this.ctx.arc(e.x, e.y, e.size, 0, Math.PI * 2), this.ctx.fill(), this.ctx.fillStyle = "#ffee88", this.ctx.beginPath(), this.ctx.arc(e.x - e.size / 3, e.y - e.size / 3, e.size / 3, 0, Math.PI * 2), this.ctx.fill()) : e.type === "star" ? this._drawStar(e.x, e.y, 5, e.size, e.size / 2, e.rotation) : (this.ctx.beginPath(), this.ctx.arc(e.x, e.y, e.size, 0, Math.PI * 2), this.ctx.fill()), this.ctx.restore();
384
558
  }
385
559
  this.particles.length > 0 ? requestAnimationFrame(() => this._animate()) : this.isAnimating = !1;
386
560
  }
387
- _drawStar(e, t, i, s, a, n) {
388
- let o = Math.PI / 2 * 3 + n;
389
- const d = Math.PI / i;
390
- this.ctx.beginPath(), this.ctx.moveTo(e, t - s);
391
- for (let p = 0; p < i; p++) {
392
- let c = e + Math.cos(o) * s, h = t + Math.sin(o) * s;
393
- this.ctx.lineTo(c, h), o += d, c = e + Math.cos(o) * a, h = t + Math.sin(o) * a, this.ctx.lineTo(c, h), o += d;
561
+ _drawStar(t, e, s, i, a, n) {
562
+ let r = Math.PI / 2 * 3 + n;
563
+ const c = Math.PI / s;
564
+ this.ctx.beginPath(), this.ctx.moveTo(t, e - i);
565
+ for (let d = 0; d < s; d++) {
566
+ let l = t + Math.cos(r) * i, h = e + Math.sin(r) * i;
567
+ this.ctx.lineTo(l, h), r += c, l = t + Math.cos(r) * a, h = e + Math.sin(r) * a, this.ctx.lineTo(l, h), r += c;
394
568
  }
395
- this.ctx.lineTo(e, t - s), this.ctx.closePath(), this.ctx.fill();
569
+ this.ctx.lineTo(t, e - i), this.ctx.closePath(), this.ctx.fill();
396
570
  }
397
571
  /**
398
572
  * Clear all particles
@@ -401,227 +575,150 @@ class f {
401
575
  this.particles = [], this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
402
576
  }
403
577
  }
404
- const l = new f();
405
- class g {
406
- constructor() {
407
- this.container = null, this.xpBar = null, this.levelDisplay = null, this.streakDisplay = null, this.init();
408
- }
409
- /**
410
- * Initialize UI overlay
411
- */
412
- init() {
413
- this.container = document.createElement("div"), this.container.id = "game-ui-overlay", this.container.innerHTML = `
414
- <div class="game-ui-top-bar">
415
- <div class="level-badge" id="level-badge">
416
- <span class="level-label">LVL</span>
417
- <span class="level-number" id="level-number">1</span>
418
- </div>
419
-
420
- <div class="xp-container">
421
- <div class="xp-bar-bg">
422
- <div class="xp-bar-fill" id="xp-bar-fill" style="width: 0%"></div>
423
- </div>
424
- <div class="xp-text" id="xp-text">0 /100 XP</div>
425
- </div>
426
-
427
- <div class="streak-badge" id="streak-badge">
428
- <span class="streak-icon">🔥</span>
429
- <span class="streak-number" id="streak-number">1</span>
430
- </div>
431
- </div>
432
- `, document.body.appendChild(this.container), this.xpBar = document.getElementById("xp-bar-fill"), this.xpText = document.getElementById("xp-text"), this.levelDisplay = document.getElementById("level-number"), this.streakDisplay = document.getElementById("streak-number");
433
- }
434
- /**
435
- * Update XP bar
436
- */
437
- updateXP(e, t, i) {
438
- const s = e / i * 100;
439
- this.xpBar.style.width = `${Math.min(100, s)}%`, this.xpText.textContent = `${e} / ${i} XP`, this.xpBar.style.animation = "none", setTimeout(() => {
440
- this.xpBar.style.animation = "xp-pulse 0.3s ease-out";
441
- }, 10);
442
- }
443
- /**
444
- * Update level display
445
- */
446
- updateLevel(e) {
447
- this.levelDisplay.textContent = e;
448
- const t = document.getElementById("level-badge");
449
- t.style.animation = "none", setTimeout(() => {
450
- t.style.animation = "level-pulse 0.5s ease-out";
451
- }, 10);
452
- }
453
- /**
454
- * Update streak display
455
- */
456
- updateStreak(e) {
457
- this.streakDisplay.textContent = e;
458
- const t = document.getElementById("streak-badge");
459
- e >= 7 ? t.style.background = "linear-gradient(135deg, #ff6b00 0%, #ff4400 100%)" : e >= 3 && (t.style.background = "linear-gradient(135deg, #ff8800 0%, #ff6b00 100%)");
460
- }
461
- /**
462
- * Show achievement unlocked popup
463
- */
464
- showAchievement(e) {
465
- const t = document.createElement("div");
466
- t.className = "achievement-popup", t.innerHTML = `
467
- <div class="achievement-shine"></div>
468
- <div class="achievement-content">
469
- <div class="achievement-icon">${e.icon}</div>
470
- <div class="achievement-info">
471
- <div class="achievement-title">Achievement Unlocked!</div>
472
- <div class="achievement-name">${e.name}</div>
473
- <div class="achievement-desc">${e.description}</div>
474
- <div class="achievement-xp">+${e.xp} XP</div>
475
- </div>
476
- </div>
477
- `, document.body.appendChild(t), setTimeout(() => {
478
- const i = t.getBoundingClientRect();
479
- l.confetti(i.left + i.width / 2, i.top + i.height / 2, 40);
480
- }, 300), setTimeout(() => {
481
- t.style.animation = "slideOut 0.3s ease-in forwards", setTimeout(() => t.remove(), 300);
482
- }, 4e3);
483
- }
484
- /**
485
- * Show level up screen
486
- */
487
- showLevelUp(e, t) {
488
- const i = document.createElement("div");
489
- i.className = "level-up-overlay", i.innerHTML = `
490
- <div class="level-up-content">
491
- <div class="level-up-title">LEVEL UP!</div>
492
- <div class="level-up-number">${t}</div>
493
- <div class="level-up-subtitle">Amazing progress!</div>
494
- </div>
495
- `, document.body.appendChild(i), setTimeout(() => {
496
- for (let s = 0; s < 5; s++)
497
- setTimeout(() => {
498
- const a = Math.random() * window.innerWidth, n = Math.random() * window.innerHeight * 0.6;
499
- l.confetti(a, n, 30);
500
- }, s * 200);
501
- }, 300), setTimeout(() => {
502
- i.style.opacity = "0", setTimeout(() => i.remove(), 500);
503
- }, 2500);
504
- }
505
- /**
506
- * Show floating text (e.g., "+50 XP", "COMBO!")
507
- */
508
- showFloatingText(e, t, i, s = "#ffd700", a = "24px") {
509
- const n = document.createElement("div");
510
- n.className = "floating-text", n.textContent = e, n.style.left = t + "px", n.style.top = i + "px", n.style.color = s, n.style.fontSize = a, document.body.appendChild(n), setTimeout(() => n.remove(), 2e3);
578
+ class f {
579
+ constructor(t = {}) {
580
+ this.audioContext = null, this.storageKey = t.storageKey || "dopamineSoundsMuted", this.muted = localStorage.getItem(this.storageKey) === "true", this.assets = /* @__PURE__ */ new Map(), this.soundPack = t.soundPack || "synth", this.customSounds = t.customSounds || {}, Object.keys(this.customSounds).length > 0 && this.preloadSounds(this.customSounds);
511
581
  }
512
582
  /**
513
- * Show combo multiplier
583
+ * Initialize Audio Context (must be called after user interaction)
514
584
  */
515
- showCombo(e, t, i) {
516
- this.showFloatingText(`${e}× COMBO!`, t, i, "#ff6b6b", "32px"), l.fire(t, i, 20);
585
+ initAudio() {
586
+ if (!this.audioContext) {
587
+ const t = window.AudioContext || window.webkitAudioContext;
588
+ this.audioContext = new t();
589
+ }
590
+ this.audioContext.state === "suspended" && this.audioContext.resume();
517
591
  }
518
592
  /**
519
- * Show "near miss"indicator
593
+ * Toggle mute state
594
+ * @returns {boolean} New mute state
520
595
  */
521
- showNearMiss(e, t) {
522
- this.showFloatingText("CLOSE CALL!", e, t, "#45b7d1", "20px"), l.sparkle(e, t, 15, "#45b7d1");
596
+ toggleMute() {
597
+ return this.muted = !this.muted, localStorage.setItem(this.storageKey, this.muted), this.muted;
523
598
  }
524
599
  /**
525
- * Show lucky moment (random multiplier)
600
+ * Register a custom sound URL
601
+ * @param {string} key - Unique identifier (e.g., 'jump', 'win')
602
+ * @param {string} url - Path to audio file
526
603
  */
527
- showLuckyMoment(e, t, i) {
528
- this.showFloatingText(`LUCKY ${e}×!`, t, i, "#ffd700", "36px"), l.starBurst(t, i, 12), l.confetti(t, i, 25);
604
+ registerSound(t, e) {
605
+ this.customSounds[t] = e, this.audioContext && this.loadSound(t, e);
529
606
  }
530
607
  /**
531
- * Show generic notification
608
+ * Preload multiple sounds
609
+ * @param {Object} soundMap - { key: url }
532
610
  */
533
- showNotification(e, t = "normal") {
534
- const i = window.innerWidth / 2, s = window.innerHeight / 3;
535
- t === "legendary" ? this.showLuckyMoment(e, i, s) : t === "rare" ? this.showCombo(e, i, s) : this.showFloatingText(e, i, s, "#fff", "24px");
611
+ async preloadSounds(t) {
612
+ const e = Object.entries(t).map(([s, i]) => this.loadSound(s, i));
613
+ await Promise.allSettled(e);
536
614
  }
537
615
  /**
538
- * Show Game Over Summary
616
+ * Load and decode a sound file
539
617
  */
540
- showSummary(e) {
541
- const t = document.createElement("div");
542
- t.className = "message-box", t.style.display = "flex", t.style.position = "absolute", t.style.top = "50%", t.style.left = "50%", t.style.transform = "translate(-50%, -50%)", t.style.zIndex = "1000";
543
- let i = "";
544
- e.metrics && (i = Object.entries(e.metrics).map(([s, a]) => `<div style="display:flex;justify-content:space-between;width:100%"><span>${s}:</span> <strong>${a}</strong></div>`).join("")), t.innerHTML = `
545
- <h1>Game Over</h1>
546
- <div style="font-size: 3rem; font-weight: bold; color: #e06020; margin: 10px 0;">${e.score}</div>
547
- <div style="width: 100%; margin-bottom: 20px;">
548
- ${i}
549
- </div>
550
- <button class="btn" onclick="location.reload()">Play Again</button>
551
- <button class="btn" onclick="window.location.href='index.html'" style="margin-top: 10px; background: #543847;">Exit</button>
552
- `, document.body.appendChild(t);
553
- }
554
- }
555
- const x = new g();
556
- class w {
557
- constructor() {
558
- this.audioContext = null, this.muted = localStorage.getItem("dopamineSoundsMuted") === "true";
618
+ async loadSound(t, e) {
619
+ try {
620
+ if (!this.audioContext)
621
+ return;
622
+ const i = await (await fetch(e)).arrayBuffer(), a = await this.audioContext.decodeAudioData(i);
623
+ this.assets.set(t, a);
624
+ } catch (s) {
625
+ console.warn(`[DopamineJS] Failed to load sound '${t}':`, s);
626
+ }
559
627
  }
560
628
  /**
561
- * Initialize Audio Context (must be called after user interaction)
629
+ * Play a sound by key (custom) or fallback to synth
630
+ * @param {string} key - 'jump', 'score', 'success', etc.
562
631
  */
563
- initAudio() {
564
- this.audioContext || (this.audioContext = new (window.AudioContext || window.webkitAudioContext)());
632
+ async play(t) {
633
+ if (this.muted) return;
634
+ if (this.initAudio(), this.assets.has(t)) {
635
+ this._playBuffer(this.assets.get(t));
636
+ return;
637
+ }
638
+ if (this.customSounds[t] && !this.assets.has(t) && (await this.loadSound(t, this.customSounds[t]), this.assets.has(t))) {
639
+ this._playBuffer(this.assets.get(t));
640
+ return;
641
+ }
642
+ const e = {
643
+ jump: () => this.playJump(!0),
644
+ // true = force synth
645
+ score: () => this.playScore(!0),
646
+ success: () => this.playSuccess(!0),
647
+ click: () => this.playClick(!0),
648
+ error: () => this.playError(!0),
649
+ gameover: () => this.playGameOver(!0)
650
+ };
651
+ e[t] && e[t]();
565
652
  }
566
- /**
567
- * Toggle mute state
568
- * @returns {boolean} New mute state
569
- */
570
- toggleMute() {
571
- return this.muted = !this.muted, localStorage.setItem("dopamineSoundsMuted", this.muted), this.muted;
653
+ _playBuffer(t) {
654
+ const e = this.audioContext.createBufferSource();
655
+ e.buffer = t, e.connect(this.audioContext.destination), e.start(0);
572
656
  }
573
657
  /**
574
658
  * Play a synthesized tone
575
- * @param {number} frequency
576
- * @param {number} duration
577
- * @param {string} type - 'sine', 'square', 'sawtooth', 'triangle'
578
- * @param {number} volume
579
659
  */
580
- playTone(e, t, i = "sine", s = 0.3) {
660
+ playTone(t, e, s = "sine", i = 0.3) {
581
661
  if (this.muted) return;
582
662
  this.initAudio();
583
663
  const a = this.audioContext.createOscillator(), n = this.audioContext.createGain();
584
- a.connect(n), n.connect(this.audioContext.destination), a.frequency.value = e, a.type = i, n.gain.setValueAtTime(s, this.audioContext.currentTime), n.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + t), a.start(this.audioContext.currentTime), a.stop(this.audioContext.currentTime + t);
664
+ a.connect(n), n.connect(this.audioContext.destination), a.frequency.value = t, a.type = s, n.gain.setValueAtTime(i, this.audioContext.currentTime), n.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + e), a.start(this.audioContext.currentTime), a.stop(this.audioContext.currentTime + e);
585
665
  }
586
- // --- Presets ---
587
- playJump() {
666
+ // --- Presets (Synth) ---
667
+ playJump(t = !1) {
668
+ if (!t && this.assets.has("jump")) return this.play("jump");
588
669
  this.playTone(400, 0.1, "square", 0.2);
589
670
  }
590
- playScore() {
671
+ playScore(t = !1) {
672
+ if (!t && this.assets.has("score")) return this.play("score");
591
673
  this.playTone(800, 0.15, "sine", 0.25), setTimeout(() => this.playTone(1e3, 0.15, "sine", 0.25), 50);
592
674
  }
593
- playGameOver() {
675
+ playGameOver(t = !1) {
676
+ if (!t && this.assets.has("gameover")) return this.play("gameover");
594
677
  this.playTone(300, 0.2, "sawtooth", 0.3), setTimeout(() => this.playTone(200, 0.2, "sawtooth", 0.3), 100), setTimeout(() => this.playTone(150, 0.3, "sawtooth", 0.3), 200);
595
678
  }
596
- playClick() {
679
+ playClick(t = !1) {
680
+ if (!t && this.assets.has("click")) return this.play("click");
597
681
  this.playTone(600, 0.05, "sine", 0.1);
598
682
  }
599
- playSuccess() {
683
+ playSuccess(t = !1) {
684
+ if (!t && this.assets.has("success")) return this.play("success");
600
685
  this.playTone(600, 0.1, "sine", 0.2), setTimeout(() => this.playTone(800, 0.2, "sine", 0.2), 100);
601
686
  }
602
- playError() {
687
+ playError(t = !1) {
688
+ if (!t && this.assets.has("error")) return this.play("error");
603
689
  this.playTone(200, 0.2, "sawtooth", 0.3);
604
690
  }
605
691
  }
606
- const b = new w();
607
- class M {
608
- constructor(e = {}) {
609
- this.config = e;
692
+ class g {
693
+ constructor(t = {}) {
694
+ this.config = t, this.dataService = new u(t.data || {}), this.rewardSystem = new m(this.dataService, t.rewards || {}), this.soundManager = new f(t.sound || {}), this.particleSystem = new v(t.particles || {}), this.gameUI = new y(this.particleSystem), this._bindEvents();
610
695
  }
611
696
  async init() {
612
- return await y.init(), console.log("DopamineJS Initialized 🚀"), {
613
- rewardSystem: y,
614
- gameUI: x,
615
- particleSystem: l,
616
- soundManager: b
697
+ return await this.rewardSystem.init(), console.log("DopamineJS Initialized 🚀"), {
698
+ rewardSystem: this.rewardSystem,
699
+ gameUI: this.gameUI,
700
+ particleSystem: this.particleSystem,
701
+ soundManager: this.soundManager
617
702
  };
618
703
  }
704
+ _bindEvents() {
705
+ this.rewardSystem.on("xp_gained", (t) => {
706
+ const { total: e, needed: s } = this.rewardSystem.getXPForNextLevel();
707
+ this.gameUI.updateXP(this.rewardSystem.player.xp, s, e), t.xpGained > 0;
708
+ }), this.rewardSystem.on("level_up", (t) => {
709
+ this.gameUI.updateLevel(t.newLevel), this.gameUI.showLevelUp(t.oldLevel, t.newLevel);
710
+ }), this.rewardSystem.on("achievement_unlocked", (t) => {
711
+ this.gameUI.showAchievement(t);
712
+ }), this.rewardSystem.on("new_high_score", (t) => {
713
+ this.gameUI.showNotification(`New High Score: ${t.score}!`, "legendary"), this.soundManager.playSuccess();
714
+ });
715
+ }
619
716
  }
620
717
  export {
621
- m as dataService,
622
- M as default,
623
- x as gameUI,
624
- l as particleSystem,
625
- y as rewardSystem,
626
- b as soundManager
718
+ u as DataService,
719
+ y as GameUI,
720
+ v as ParticleSystem,
721
+ m as RewardSystem,
722
+ f as SoundManager,
723
+ g as default
627
724
  };