dopaminejs 1.0.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.
- package/README.md +167 -60
- package/dist/dopamine.mjs +324 -291
- package/dist/dopamine.umd.js +4 -4
- package/package.json +10 -6
- package/src/audio/SoundManager.js +4 -5
- package/src/core/DataService.js +6 -7
- package/src/core/EventEmitter.js +58 -0
- package/src/core/RewardSystem.js +12 -34
- package/src/effects/ParticleSystem.js +5 -7
- package/src/index.js +48 -11
- package/src/ui/GameUI.js +8 -11
package/dist/dopamine.mjs
CHANGED
|
@@ -1,52 +1,55 @@
|
|
|
1
|
-
class
|
|
1
|
+
class p {
|
|
2
2
|
constructor() {
|
|
3
|
-
this.
|
|
3
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param {string}
|
|
8
|
-
* @param {
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
*
|
|
20
|
-
* @param {string}
|
|
21
|
-
* @param {
|
|
15
|
+
* Unsubscribe from an event
|
|
16
|
+
* @param {string} event - Event name
|
|
17
|
+
* @param {Function} callback - Callback function
|
|
22
18
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
*
|
|
33
|
-
* @param {string}
|
|
23
|
+
* Emit an event
|
|
24
|
+
* @param {string} event - Event name
|
|
25
|
+
* @param {*} data - Event data
|
|
34
26
|
*/
|
|
35
|
-
|
|
36
|
-
this.
|
|
27
|
+
emit(t, e) {
|
|
28
|
+
this.listeners.has(t) && this.listeners.get(t).forEach((i) => {
|
|
29
|
+
try {
|
|
30
|
+
i(e);
|
|
31
|
+
} catch (s) {
|
|
32
|
+
console.error(`Error in listener for event '${t}':`, s);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Clear all listeners
|
|
38
|
+
*/
|
|
39
|
+
clear() {
|
|
40
|
+
this.listeners.clear();
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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
|
|
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(
|
|
89
|
+
async addXP(t, e = "") {
|
|
87
90
|
const i = this.player.level;
|
|
88
|
-
this.player.xp +=
|
|
91
|
+
this.player.xp += t;
|
|
89
92
|
const s = this._calculateLevel(this.player.xp), a = s > i;
|
|
90
|
-
return a && (this.player.level = s), await this.save(), this.
|
|
93
|
+
return a && (this.player.level = s), await this.save(), this.emit("xp_gained", { amount: t, reason: e, leveledUp: a, newLevel: s }), a && this.emit("level_up", { oldLevel: i, newLevel: s }), { leveledUp: a, newLevel: s, xpGained: t };
|
|
91
94
|
}
|
|
92
95
|
/**
|
|
93
96
|
* Calculate level from XP (exponential curve)
|
|
94
97
|
*/
|
|
95
|
-
_calculateLevel(
|
|
96
|
-
const
|
|
97
|
-
return Math.max(1,
|
|
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
|
|
106
|
+
const t = this.player.level + 1, e = 50 * t * (t - 1), i = 50 * this.player.level * (this.player.level - 1);
|
|
104
107
|
return {
|
|
105
|
-
total:
|
|
106
|
-
needed:
|
|
107
|
-
progress: (this.player.xp - i) / (
|
|
108
|
+
total: e,
|
|
109
|
+
needed: e - this.player.xp,
|
|
110
|
+
progress: (this.player.xp - i) / (e - i)
|
|
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(
|
|
116
|
-
this.player.stats[
|
|
117
|
-
const i = this.player.stats[
|
|
118
|
-
i.totalPlays++, this.player.totalGamesPlayed++,
|
|
119
|
-
a !== "score" && (i[a] = (i[a] || 0) + (typeof
|
|
118
|
+
async recordGame(t, e) {
|
|
119
|
+
this.player.stats[t] || (this.player.stats[t] = { totalPlays: 0, highScore: 0 });
|
|
120
|
+
const i = this.player.stats[t];
|
|
121
|
+
i.totalPlays++, this.player.totalGamesPlayed++, e.score !== void 0 && e.score > i.highScore && (i.highScore = e.score, this.emit("new_high_score", { gameName: t, score: e.score })), Object.keys(e).forEach((a) => {
|
|
122
|
+
a !== "score" && (i[a] = (i[a] || 0) + (typeof e[a] == "number" ? e[a] : 0));
|
|
120
123
|
});
|
|
121
124
|
let s = 10;
|
|
122
|
-
|
|
125
|
+
e.score && (s += Math.floor(e.score / 5)), s = Math.floor(s * this.getStreakMultiplier()), await this.addXP(s, `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(
|
|
130
|
+
async checkAchievements(t, e) {
|
|
128
131
|
const i = [];
|
|
129
132
|
for (const [s, a] of Object.entries(this.achievements))
|
|
130
|
-
this.player.achievements[s] || a.check(this.player,
|
|
133
|
+
this.player.achievements[s] || a.check(this.player, t, e) && (await this.unlockAchievement(s), i.push(a));
|
|
131
134
|
return i;
|
|
132
135
|
}
|
|
133
136
|
/**
|
|
134
137
|
* Unlock an achievement
|
|
135
138
|
*/
|
|
136
|
-
async unlockAchievement(
|
|
137
|
-
const
|
|
138
|
-
return
|
|
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(
|
|
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(([
|
|
148
|
-
...this.achievements[
|
|
149
|
-
id:
|
|
150
|
-
...
|
|
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((
|
|
160
|
+
return this.getUnlockedAchievements().filter((t) => !t.seen);
|
|
158
161
|
}
|
|
159
162
|
/**
|
|
160
163
|
* Mark achievements as seen
|
|
161
164
|
*/
|
|
162
|
-
async markAchievementsSeen(
|
|
163
|
-
for (const
|
|
164
|
-
this.player.achievements[
|
|
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
|
|
172
|
-
if (
|
|
174
|
+
const t = this._getTodayDateString(), e = this.player.streak.lastPlayDate;
|
|
175
|
+
if (e === t)
|
|
173
176
|
return;
|
|
174
177
|
const i = this._getYesterdayDateString();
|
|
175
|
-
|
|
178
|
+
e === 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 = t;
|
|
176
179
|
}
|
|
177
180
|
/**
|
|
178
181
|
* Get streak multiplier for XP
|
|
179
182
|
*/
|
|
180
183
|
getStreakMultiplier() {
|
|
181
|
-
const
|
|
182
|
-
return
|
|
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
|
|
195
|
-
return
|
|
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,196 +209,80 @@ class u {
|
|
|
215
209
|
description: "Play your first game",
|
|
216
210
|
icon: "🎮",
|
|
217
211
|
xp: 50,
|
|
218
|
-
check: (
|
|
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: (
|
|
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: (
|
|
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: (
|
|
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: (
|
|
240
|
+
check: (t) => t.streak.current >= 7
|
|
247
241
|
}
|
|
248
242
|
};
|
|
249
243
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Register custom achievements
|
|
252
|
-
* @param {Object} achievements
|
|
253
|
-
*/
|
|
254
|
-
registerAchievements(e) {
|
|
255
|
-
this.achievements = { ...this.achievements, ...e };
|
|
256
|
-
}
|
|
257
244
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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());
|
|
262
|
-
}
|
|
263
|
-
_resize() {
|
|
264
|
-
this.canvas.width = window.innerWidth, this.canvas.height = window.innerHeight;
|
|
245
|
+
class y {
|
|
246
|
+
constructor(t = {}) {
|
|
247
|
+
this.storage = t.storage || window.localStorage, this.prefix = t.prefix || "dopamine_";
|
|
265
248
|
}
|
|
266
249
|
/**
|
|
267
|
-
*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const s = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#f7dc6f", "#c06c84", "#6c5ce7"];
|
|
271
|
-
for (let a = 0; a < i; a++)
|
|
272
|
-
this.particles.push({
|
|
273
|
-
x: e,
|
|
274
|
-
y: t,
|
|
275
|
-
vx: (Math.random() - 0.5) * 10,
|
|
276
|
-
vy: (Math.random() - 0.5) * 10 - 5,
|
|
277
|
-
rotation: Math.random() * Math.PI * 2,
|
|
278
|
-
rotationSpeed: (Math.random() - 0.5) * 0.2,
|
|
279
|
-
color: s[Math.floor(Math.random() * s.length)],
|
|
280
|
-
size: Math.random() * 8 + 4,
|
|
281
|
-
life: 1,
|
|
282
|
-
decay: 0.015 + Math.random() * 0.01,
|
|
283
|
-
gravity: 0.3,
|
|
284
|
-
type: "confetti"
|
|
285
|
-
});
|
|
286
|
-
this._startAnimation();
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Sparkle effect (for score milestones, combos)
|
|
250
|
+
* Save data to local storage
|
|
251
|
+
* @param {string} key
|
|
252
|
+
* @param {any} data
|
|
290
253
|
*/
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
this.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
vx: Math.cos(n) * o,
|
|
298
|
-
vy: Math.sin(n) * o,
|
|
299
|
-
color: s,
|
|
300
|
-
size: Math.random() * 4 + 2,
|
|
301
|
-
life: 1,
|
|
302
|
-
decay: 0.02,
|
|
303
|
-
gravity: -0.1,
|
|
304
|
-
// Slight upward drift
|
|
305
|
-
type: "spark"
|
|
306
|
-
});
|
|
254
|
+
async save(t, e) {
|
|
255
|
+
try {
|
|
256
|
+
const i = JSON.stringify(e);
|
|
257
|
+
return this.storage.setItem(this.prefix + t, i), !0;
|
|
258
|
+
} catch (i) {
|
|
259
|
+
return console.error("Error saving data:", i), !1;
|
|
307
260
|
}
|
|
308
|
-
this._startAnimation();
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Coin shower (for collecting coins)
|
|
312
|
-
*/
|
|
313
|
-
coinShower(e, t, i = 10) {
|
|
314
|
-
for (let s = 0; s < i; s++)
|
|
315
|
-
this.particles.push({
|
|
316
|
-
x: e + (Math.random() - 0.5) * 100,
|
|
317
|
-
y: t - Math.random() * 50,
|
|
318
|
-
vx: (Math.random() - 0.5) * 3,
|
|
319
|
-
vy: Math.random() * -2 - 3,
|
|
320
|
-
color: "#ffd700",
|
|
321
|
-
size: 12,
|
|
322
|
-
life: 1,
|
|
323
|
-
decay: 0.012,
|
|
324
|
-
gravity: 0.4,
|
|
325
|
-
type: "coin"
|
|
326
|
-
});
|
|
327
|
-
this._startAnimation();
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Fire particles (for streaks, combos)
|
|
331
|
-
*/
|
|
332
|
-
fire(e, t, i = 15) {
|
|
333
|
-
const s = ["#ff6b00", "#ff8800", "#ffaa00", "#ff4400"];
|
|
334
|
-
for (let a = 0; a < i; a++)
|
|
335
|
-
this.particles.push({
|
|
336
|
-
x: e + (Math.random() - 0.5) * 20,
|
|
337
|
-
y: t,
|
|
338
|
-
vx: (Math.random() - 0.5) * 2,
|
|
339
|
-
vy: -Math.random() * 3 - 2,
|
|
340
|
-
color: s[Math.floor(Math.random() * s.length)],
|
|
341
|
-
size: Math.random() * 6 + 3,
|
|
342
|
-
life: 1,
|
|
343
|
-
decay: 0.025,
|
|
344
|
-
gravity: -0.2,
|
|
345
|
-
// Upward
|
|
346
|
-
type: "fire"
|
|
347
|
-
});
|
|
348
|
-
this._startAnimation();
|
|
349
261
|
}
|
|
350
262
|
/**
|
|
351
|
-
*
|
|
263
|
+
* Load data from local storage
|
|
264
|
+
* @param {string} key
|
|
265
|
+
* @param {any} defaultValue
|
|
352
266
|
*/
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
vx: Math.cos(a) * n,
|
|
360
|
-
vy: Math.sin(a) * n,
|
|
361
|
-
rotation: a,
|
|
362
|
-
color: "#ffffff",
|
|
363
|
-
size: 15,
|
|
364
|
-
life: 1,
|
|
365
|
-
decay: 0.03,
|
|
366
|
-
gravity: 0,
|
|
367
|
-
type: "star"
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
this._startAnimation();
|
|
371
|
-
}
|
|
372
|
-
_startAnimation() {
|
|
373
|
-
this.isAnimating || (this.isAnimating = !0, this._animate());
|
|
374
|
-
}
|
|
375
|
-
_animate() {
|
|
376
|
-
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);
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
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();
|
|
384
|
-
}
|
|
385
|
-
this.particles.length > 0 ? requestAnimationFrame(() => this._animate()) : this.isAnimating = !1;
|
|
386
|
-
}
|
|
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;
|
|
267
|
+
async load(t, e = null) {
|
|
268
|
+
try {
|
|
269
|
+
const i = this.storage.getItem(this.prefix + t);
|
|
270
|
+
return i ? JSON.parse(i) : e;
|
|
271
|
+
} catch (i) {
|
|
272
|
+
return console.error("Error loading data:", i), e;
|
|
394
273
|
}
|
|
395
|
-
this.ctx.lineTo(e, t - s), this.ctx.closePath(), this.ctx.fill();
|
|
396
274
|
}
|
|
397
275
|
/**
|
|
398
|
-
* Clear
|
|
276
|
+
* Clear specific data
|
|
277
|
+
* @param {string} key
|
|
399
278
|
*/
|
|
400
|
-
clear() {
|
|
401
|
-
this.
|
|
279
|
+
async clear(t) {
|
|
280
|
+
this.storage.removeItem(this.prefix + t);
|
|
402
281
|
}
|
|
403
282
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
this.container = null, this.xpBar = null, this.levelDisplay = null, this.streakDisplay = null, this.init();
|
|
283
|
+
class v {
|
|
284
|
+
constructor(t) {
|
|
285
|
+
this.particleSystem = t, this.container = null, this.xpBar = null, this.levelDisplay = null, this.streakDisplay = null, this.init();
|
|
408
286
|
}
|
|
409
287
|
/**
|
|
410
288
|
* Initialize UI overlay
|
|
@@ -434,69 +312,69 @@ class g {
|
|
|
434
312
|
/**
|
|
435
313
|
* Update XP bar
|
|
436
314
|
*/
|
|
437
|
-
updateXP(
|
|
438
|
-
const s =
|
|
439
|
-
this.xpBar.style.width = `${Math.min(100, s)}%`, this.xpText.textContent = `${
|
|
315
|
+
updateXP(t, e, i) {
|
|
316
|
+
const s = t / i * 100;
|
|
317
|
+
this.xpBar.style.width = `${Math.min(100, s)}%`, this.xpText.textContent = `${t} / ${i} XP`, this.xpBar.style.animation = "none", setTimeout(() => {
|
|
440
318
|
this.xpBar.style.animation = "xp-pulse 0.3s ease-out";
|
|
441
319
|
}, 10);
|
|
442
320
|
}
|
|
443
321
|
/**
|
|
444
322
|
* Update level display
|
|
445
323
|
*/
|
|
446
|
-
updateLevel(
|
|
447
|
-
this.levelDisplay.textContent =
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
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";
|
|
451
329
|
}, 10);
|
|
452
330
|
}
|
|
453
331
|
/**
|
|
454
332
|
* Update streak display
|
|
455
333
|
*/
|
|
456
|
-
updateStreak(
|
|
457
|
-
this.streakDisplay.textContent =
|
|
458
|
-
const
|
|
459
|
-
|
|
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%)");
|
|
460
338
|
}
|
|
461
339
|
/**
|
|
462
340
|
* Show achievement unlocked popup
|
|
463
341
|
*/
|
|
464
|
-
showAchievement(
|
|
465
|
-
const
|
|
466
|
-
|
|
342
|
+
showAchievement(t) {
|
|
343
|
+
const e = document.createElement("div");
|
|
344
|
+
e.className = "achievement-popup", e.innerHTML = `
|
|
467
345
|
<div class="achievement-shine"></div>
|
|
468
346
|
<div class="achievement-content">
|
|
469
|
-
<div class="achievement-icon">${
|
|
347
|
+
<div class="achievement-icon">${t.icon}</div>
|
|
470
348
|
<div class="achievement-info">
|
|
471
349
|
<div class="achievement-title">Achievement Unlocked!</div>
|
|
472
|
-
<div class="achievement-name">${
|
|
473
|
-
<div class="achievement-desc">${
|
|
474
|
-
<div class="achievement-xp">+${
|
|
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>
|
|
475
353
|
</div>
|
|
476
354
|
</div>
|
|
477
|
-
`, document.body.appendChild(
|
|
478
|
-
const i =
|
|
479
|
-
|
|
355
|
+
`, document.body.appendChild(e), setTimeout(() => {
|
|
356
|
+
const i = e.getBoundingClientRect();
|
|
357
|
+
this.particleSystem.confetti(i.left + i.width / 2, i.top + i.height / 2, 40);
|
|
480
358
|
}, 300), setTimeout(() => {
|
|
481
|
-
|
|
359
|
+
e.style.animation = "slideOut 0.3s ease-in forwards", setTimeout(() => e.remove(), 300);
|
|
482
360
|
}, 4e3);
|
|
483
361
|
}
|
|
484
362
|
/**
|
|
485
363
|
* Show level up screen
|
|
486
364
|
*/
|
|
487
|
-
showLevelUp(
|
|
365
|
+
showLevelUp(t, e) {
|
|
488
366
|
const i = document.createElement("div");
|
|
489
367
|
i.className = "level-up-overlay", i.innerHTML = `
|
|
490
368
|
<div class="level-up-content">
|
|
491
369
|
<div class="level-up-title">LEVEL UP!</div>
|
|
492
|
-
<div class="level-up-number">${
|
|
370
|
+
<div class="level-up-number">${e}</div>
|
|
493
371
|
<div class="level-up-subtitle">Amazing progress!</div>
|
|
494
372
|
</div>
|
|
495
373
|
`, document.body.appendChild(i), setTimeout(() => {
|
|
496
374
|
for (let s = 0; s < 5; s++)
|
|
497
375
|
setTimeout(() => {
|
|
498
376
|
const a = Math.random() * window.innerWidth, n = Math.random() * window.innerHeight * 0.6;
|
|
499
|
-
|
|
377
|
+
this.particleSystem.confetti(a, n, 30);
|
|
500
378
|
}, s * 200);
|
|
501
379
|
}, 300), setTimeout(() => {
|
|
502
380
|
i.style.opacity = "0", setTimeout(() => i.remove(), 500);
|
|
@@ -505,57 +383,201 @@ class g {
|
|
|
505
383
|
/**
|
|
506
384
|
* Show floating text (e.g., "+50 XP", "COMBO!")
|
|
507
385
|
*/
|
|
508
|
-
showFloatingText(
|
|
386
|
+
showFloatingText(t, e, i, s = "#ffd700", a = "24px") {
|
|
509
387
|
const n = document.createElement("div");
|
|
510
|
-
n.className = "floating-text", n.textContent =
|
|
388
|
+
n.className = "floating-text", n.textContent = t, n.style.left = e + "px", n.style.top = i + "px", n.style.color = s, n.style.fontSize = a, document.body.appendChild(n), setTimeout(() => n.remove(), 2e3);
|
|
511
389
|
}
|
|
512
390
|
/**
|
|
513
391
|
* Show combo multiplier
|
|
514
392
|
*/
|
|
515
|
-
showCombo(
|
|
516
|
-
this.showFloatingText(`${
|
|
393
|
+
showCombo(t, e, i) {
|
|
394
|
+
this.showFloatingText(`${t}× COMBO!`, e, i, "#ff6b6b", "32px"), this.particleSystem.fire(e, i, 20);
|
|
517
395
|
}
|
|
518
396
|
/**
|
|
519
397
|
* Show "near miss"indicator
|
|
520
398
|
*/
|
|
521
|
-
showNearMiss(
|
|
522
|
-
this.showFloatingText("CLOSE CALL!",
|
|
399
|
+
showNearMiss(t, e) {
|
|
400
|
+
this.showFloatingText("CLOSE CALL!", t, e, "#45b7d1", "20px"), this.particleSystem.sparkle(t, e, 15, "#45b7d1");
|
|
523
401
|
}
|
|
524
402
|
/**
|
|
525
403
|
* Show lucky moment (random multiplier)
|
|
526
404
|
*/
|
|
527
|
-
showLuckyMoment(
|
|
528
|
-
this.showFloatingText(`LUCKY ${
|
|
405
|
+
showLuckyMoment(t, e, i) {
|
|
406
|
+
this.showFloatingText(`LUCKY ${t}×!`, e, i, "#ffd700", "36px"), this.particleSystem.starBurst(e, i, 12), this.particleSystem.confetti(e, i, 25);
|
|
529
407
|
}
|
|
530
408
|
/**
|
|
531
409
|
* Show generic notification
|
|
532
410
|
*/
|
|
533
|
-
showNotification(
|
|
411
|
+
showNotification(t, e = "normal") {
|
|
534
412
|
const i = window.innerWidth / 2, s = window.innerHeight / 3;
|
|
535
|
-
|
|
413
|
+
e === "legendary" ? this.showLuckyMoment(t, i, s) : e === "rare" ? this.showCombo(t, i, s) : this.showFloatingText(t, i, s, "#fff", "24px");
|
|
536
414
|
}
|
|
537
415
|
/**
|
|
538
416
|
* Show Game Over Summary
|
|
539
417
|
*/
|
|
540
|
-
showSummary(
|
|
541
|
-
const
|
|
542
|
-
|
|
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";
|
|
543
421
|
let i = "";
|
|
544
|
-
|
|
422
|
+
t.metrics && (i = Object.entries(t.metrics).map(([s, a]) => `<div style="display:flex;justify-content:space-between;width:100%"><span>${s}:</span> <strong>${a}</strong></div>`).join("")), e.innerHTML = `
|
|
545
423
|
<h1>Game Over</h1>
|
|
546
|
-
<div style="font-size: 3rem; font-weight: bold; color: #e06020; margin: 10px 0;">${
|
|
424
|
+
<div style="font-size: 3rem; font-weight: bold; color: #e06020; margin: 10px 0;">${t.score}</div>
|
|
547
425
|
<div style="width: 100%; margin-bottom: 20px;">
|
|
548
426
|
${i}
|
|
549
427
|
</div>
|
|
550
428
|
<button class="btn" onclick="location.reload()">Play Again</button>
|
|
551
429
|
<button class="btn" onclick="window.location.href='index.html'" style="margin-top: 10px; background: #543847;">Exit</button>
|
|
552
|
-
`, document.body.appendChild(
|
|
430
|
+
`, document.body.appendChild(e);
|
|
553
431
|
}
|
|
554
432
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
433
|
+
class u {
|
|
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());
|
|
436
|
+
}
|
|
437
|
+
_resize() {
|
|
438
|
+
this.canvas.width = window.innerWidth, this.canvas.height = window.innerHeight;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Confetti explosion (for achievements, level ups)
|
|
442
|
+
*/
|
|
443
|
+
confetti(t, e, i = 50) {
|
|
444
|
+
const s = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#f7dc6f", "#c06c84", "#6c5ce7"];
|
|
445
|
+
for (let a = 0; a < i; a++)
|
|
446
|
+
this.particles.push({
|
|
447
|
+
x: t,
|
|
448
|
+
y: e,
|
|
449
|
+
vx: (Math.random() - 0.5) * 10,
|
|
450
|
+
vy: (Math.random() - 0.5) * 10 - 5,
|
|
451
|
+
rotation: Math.random() * Math.PI * 2,
|
|
452
|
+
rotationSpeed: (Math.random() - 0.5) * 0.2,
|
|
453
|
+
color: s[Math.floor(Math.random() * s.length)],
|
|
454
|
+
size: Math.random() * 8 + 4,
|
|
455
|
+
life: 1,
|
|
456
|
+
decay: 0.015 + Math.random() * 0.01,
|
|
457
|
+
gravity: 0.3,
|
|
458
|
+
type: "confetti"
|
|
459
|
+
});
|
|
460
|
+
this._startAnimation();
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Sparkle effect (for score milestones, combos)
|
|
464
|
+
*/
|
|
465
|
+
sparkle(t, e, i = 20, s = "#ffd700") {
|
|
466
|
+
for (let a = 0; a < i; a++) {
|
|
467
|
+
const n = Math.PI * 2 / i * a, r = 2 + Math.random() * 3;
|
|
468
|
+
this.particles.push({
|
|
469
|
+
x: t,
|
|
470
|
+
y: e,
|
|
471
|
+
vx: Math.cos(n) * r,
|
|
472
|
+
vy: Math.sin(n) * r,
|
|
473
|
+
color: s,
|
|
474
|
+
size: Math.random() * 4 + 2,
|
|
475
|
+
life: 1,
|
|
476
|
+
decay: 0.02,
|
|
477
|
+
gravity: -0.1,
|
|
478
|
+
// Slight upward drift
|
|
479
|
+
type: "spark"
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
this._startAnimation();
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Coin shower (for collecting coins)
|
|
486
|
+
*/
|
|
487
|
+
coinShower(t, e, i = 10) {
|
|
488
|
+
for (let s = 0; s < i; s++)
|
|
489
|
+
this.particles.push({
|
|
490
|
+
x: t + (Math.random() - 0.5) * 100,
|
|
491
|
+
y: e - Math.random() * 50,
|
|
492
|
+
vx: (Math.random() - 0.5) * 3,
|
|
493
|
+
vy: Math.random() * -2 - 3,
|
|
494
|
+
color: "#ffd700",
|
|
495
|
+
size: 12,
|
|
496
|
+
life: 1,
|
|
497
|
+
decay: 0.012,
|
|
498
|
+
gravity: 0.4,
|
|
499
|
+
type: "coin"
|
|
500
|
+
});
|
|
501
|
+
this._startAnimation();
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Fire particles (for streaks, combos)
|
|
505
|
+
*/
|
|
506
|
+
fire(t, e, i = 15) {
|
|
507
|
+
const s = ["#ff6b00", "#ff8800", "#ffaa00", "#ff4400"];
|
|
508
|
+
for (let a = 0; a < i; a++)
|
|
509
|
+
this.particles.push({
|
|
510
|
+
x: t + (Math.random() - 0.5) * 20,
|
|
511
|
+
y: e,
|
|
512
|
+
vx: (Math.random() - 0.5) * 2,
|
|
513
|
+
vy: -Math.random() * 3 - 2,
|
|
514
|
+
color: s[Math.floor(Math.random() * s.length)],
|
|
515
|
+
size: Math.random() * 6 + 3,
|
|
516
|
+
life: 1,
|
|
517
|
+
decay: 0.025,
|
|
518
|
+
gravity: -0.2,
|
|
519
|
+
// Upward
|
|
520
|
+
type: "fire"
|
|
521
|
+
});
|
|
522
|
+
this._startAnimation();
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Star burst (for "perfect" moments)
|
|
526
|
+
*/
|
|
527
|
+
starBurst(t, e, i = 8) {
|
|
528
|
+
for (let s = 0; s < i; s++) {
|
|
529
|
+
const a = Math.PI * 2 / i * s, n = 4;
|
|
530
|
+
this.particles.push({
|
|
531
|
+
x: t,
|
|
532
|
+
y: e,
|
|
533
|
+
vx: Math.cos(a) * n,
|
|
534
|
+
vy: Math.sin(a) * n,
|
|
535
|
+
rotation: a,
|
|
536
|
+
color: "#ffffff",
|
|
537
|
+
size: 15,
|
|
538
|
+
life: 1,
|
|
539
|
+
decay: 0.03,
|
|
540
|
+
gravity: 0,
|
|
541
|
+
type: "star"
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
this._startAnimation();
|
|
545
|
+
}
|
|
546
|
+
_startAnimation() {
|
|
547
|
+
this.isAnimating || (this.isAnimating = !0, this._animate());
|
|
548
|
+
}
|
|
549
|
+
_animate() {
|
|
550
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
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);
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
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();
|
|
558
|
+
}
|
|
559
|
+
this.particles.length > 0 ? requestAnimationFrame(() => this._animate()) : this.isAnimating = !1;
|
|
560
|
+
}
|
|
561
|
+
_drawStar(t, e, i, s, a, n) {
|
|
562
|
+
let r = Math.PI / 2 * 3 + n;
|
|
563
|
+
const c = Math.PI / i;
|
|
564
|
+
this.ctx.beginPath(), this.ctx.moveTo(t, e - s);
|
|
565
|
+
for (let d = 0; d < i; d++) {
|
|
566
|
+
let l = t + Math.cos(r) * s, h = e + Math.sin(r) * s;
|
|
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;
|
|
568
|
+
}
|
|
569
|
+
this.ctx.lineTo(t, e - s), this.ctx.closePath(), this.ctx.fill();
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Clear all particles
|
|
573
|
+
*/
|
|
574
|
+
clear() {
|
|
575
|
+
this.particles = [], this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
class f {
|
|
579
|
+
constructor(t = {}) {
|
|
580
|
+
this.audioContext = null, this.storageKey = t.storageKey || "dopamineSoundsMuted", this.muted = localStorage.getItem(this.storageKey) === "true";
|
|
559
581
|
}
|
|
560
582
|
/**
|
|
561
583
|
* Initialize Audio Context (must be called after user interaction)
|
|
@@ -568,7 +590,7 @@ class w {
|
|
|
568
590
|
* @returns {boolean} New mute state
|
|
569
591
|
*/
|
|
570
592
|
toggleMute() {
|
|
571
|
-
return this.muted = !this.muted, localStorage.setItem(
|
|
593
|
+
return this.muted = !this.muted, localStorage.setItem(this.storageKey, this.muted), this.muted;
|
|
572
594
|
}
|
|
573
595
|
/**
|
|
574
596
|
* Play a synthesized tone
|
|
@@ -577,11 +599,11 @@ class w {
|
|
|
577
599
|
* @param {string} type - 'sine', 'square', 'sawtooth', 'triangle'
|
|
578
600
|
* @param {number} volume
|
|
579
601
|
*/
|
|
580
|
-
playTone(
|
|
602
|
+
playTone(t, e, i = "sine", s = 0.3) {
|
|
581
603
|
if (this.muted) return;
|
|
582
604
|
this.initAudio();
|
|
583
605
|
const a = this.audioContext.createOscillator(), n = this.audioContext.createGain();
|
|
584
|
-
a.connect(n), n.connect(this.audioContext.destination), a.frequency.value =
|
|
606
|
+
a.connect(n), n.connect(this.audioContext.destination), a.frequency.value = t, a.type = i, n.gain.setValueAtTime(s, this.audioContext.currentTime), n.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + e), a.start(this.audioContext.currentTime), a.stop(this.audioContext.currentTime + e);
|
|
585
607
|
}
|
|
586
608
|
// --- Presets ---
|
|
587
609
|
playJump() {
|
|
@@ -603,25 +625,36 @@ class w {
|
|
|
603
625
|
this.playTone(200, 0.2, "sawtooth", 0.3);
|
|
604
626
|
}
|
|
605
627
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.config = e;
|
|
628
|
+
class g {
|
|
629
|
+
constructor(t = {}) {
|
|
630
|
+
this.config = t, this.dataService = new y(t.data || {}), this.rewardSystem = new m(this.dataService, t.rewards || {}), this.soundManager = new f(t.sound || {}), this.particleSystem = new u(t.particles || {}), this.gameUI = new v(this.particleSystem), this._bindEvents();
|
|
610
631
|
}
|
|
611
632
|
async init() {
|
|
612
|
-
return await
|
|
613
|
-
rewardSystem:
|
|
614
|
-
gameUI:
|
|
615
|
-
particleSystem:
|
|
616
|
-
soundManager:
|
|
633
|
+
return await this.rewardSystem.init(), console.log("DopamineJS Initialized 🚀"), {
|
|
634
|
+
rewardSystem: this.rewardSystem,
|
|
635
|
+
gameUI: this.gameUI,
|
|
636
|
+
particleSystem: this.particleSystem,
|
|
637
|
+
soundManager: this.soundManager
|
|
617
638
|
};
|
|
618
639
|
}
|
|
640
|
+
_bindEvents() {
|
|
641
|
+
this.rewardSystem.on("xp_gained", (t) => {
|
|
642
|
+
const { total: e, needed: i } = this.rewardSystem.getXPForNextLevel();
|
|
643
|
+
this.gameUI.updateXP(this.rewardSystem.player.xp, i, e), t.xpGained > 0;
|
|
644
|
+
}), this.rewardSystem.on("level_up", (t) => {
|
|
645
|
+
this.gameUI.updateLevel(t.newLevel), this.gameUI.showLevelUp(t.oldLevel, t.newLevel);
|
|
646
|
+
}), this.rewardSystem.on("achievement_unlocked", (t) => {
|
|
647
|
+
this.gameUI.showAchievement(t);
|
|
648
|
+
}), this.rewardSystem.on("new_high_score", (t) => {
|
|
649
|
+
this.gameUI.showNotification(`New High Score: ${t.score}!`, "legendary"), this.soundManager.playSuccess();
|
|
650
|
+
});
|
|
651
|
+
}
|
|
619
652
|
}
|
|
620
653
|
export {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
654
|
+
y as DataService,
|
|
655
|
+
v as GameUI,
|
|
656
|
+
u as ParticleSystem,
|
|
657
|
+
m as RewardSystem,
|
|
658
|
+
f as SoundManager,
|
|
659
|
+
g as default
|
|
627
660
|
};
|