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