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/dist/dopamine.mjs CHANGED
@@ -1,4 +1,4 @@
1
- class p {
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(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);
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(t, e) {
20
- this.listeners.has(t) && this.listeners.get(t).delete(e);
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(t, e) {
28
- this.listeners.has(t) && this.listeners.get(t).forEach((s) => {
27
+ emit(e, t) {
28
+ this.listeners.has(e) && this.listeners.get(e).forEach((s) => {
29
29
  try {
30
- s(e);
30
+ s(t);
31
31
  } catch (i) {
32
- console.error(`Error in listener for event '${t}':`, i);
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 m extends p {
44
- constructor(t, e = {}) {
45
- super(), this.dataService = t, this.player = null, this.achievements = { ...this._initAchievements(), ...e.achievements || {} };
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(t, e = "") {
89
+ async addXP(e, t = "") {
90
90
  const s = this.player.level;
91
- this.player.xp += t;
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: t, reason: e, leveledUp: a, newLevel: i }), a && this.emit("level_up", { oldLevel: s, newLevel: i }), { leveledUp: a, newLevel: i, xpGained: t };
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(t) {
99
- const e = Math.floor((1 + Math.sqrt(1 + 8 * t / 50)) / 2);
100
- return Math.max(1, e);
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 t = this.player.level + 1, e = 50 * t * (t - 1), s = 50 * this.player.level * (this.player.level - 1);
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: e,
109
- needed: e - this.player.xp,
110
- progress: (this.player.xp - s) / (e - 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(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));
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
- 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();
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(t, e) {
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, t, e) && (await this.unlockAchievement(i), s.push(a));
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(t) {
140
- const e = this.achievements[t];
141
- return e ? (this.player.achievements[t] = {
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(e.xp, `Achievement: ${e.name}`), this.emit("achievement_unlocked", e), await this.save(), !0) : !1;
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(([t, e]) => ({
151
- ...this.achievements[t],
152
- id: t,
153
- ...e
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((t) => !t.seen);
160
+ return this.getUnlockedAchievements().filter((e) => !e.seen);
161
161
  }
162
162
  /**
163
163
  * Mark achievements as seen
164
164
  */
165
- async markAchievementsSeen(t) {
166
- for (const e of t)
167
- this.player.achievements[e] && (this.player.achievements[e].seen = !0);
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 t = this._getTodayDateString(), e = this.player.streak.lastPlayDate;
175
- if (e === t)
174
+ const e = this._getTodayDateString(), t = this.player.streak.lastPlayDate;
175
+ if (t === e)
176
176
  return;
177
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;
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 t = this.player.streak.current;
185
- return t >= 7 ? 2 : t >= 5 ? 1.5 : t >= 3 ? 1.25 : 1;
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 t = /* @__PURE__ */ new Date();
198
- return t.setDate(t.getDate() - 1), t.toISOString().split("T")[0];
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: (t) => t.totalGamesPlayed >= 1
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: (t) => t.level >= 5
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: (t) => t.level >= 10
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: (t) => t.streak.current >= 3
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: (t) => t.streak.current >= 7
240
+ check: (e) => e.streak.current >= 7
241
241
  }
242
242
  };
243
243
  }
244
244
  }
245
- class u {
246
- constructor(t = {}) {
247
- this.storage = t.storage || window.localStorage, this.prefix = t.prefix || "dopamine_";
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(t, e) {
254
+ async save(e, t) {
255
255
  try {
256
- const s = JSON.stringify(e);
257
- return this.storage.setItem(this.prefix + t, s), !0;
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(t, e = null) {
267
+ async load(e, t = null) {
268
268
  try {
269
- const s = this.storage.getItem(this.prefix + t);
270
- return s ? JSON.parse(s) : e;
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), e;
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(t) {
280
- this.storage.removeItem(this.prefix + t);
279
+ async clear(e) {
280
+ this.storage.removeItem(this.prefix + e);
281
281
  }
282
282
  }
283
- class y {
284
- constructor(t) {
285
- this.particleSystem = t, this.container = null, this.xpBar = null, this.levelDisplay = null, this.streakDisplay = null, this.init();
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(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(() => {
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(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";
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(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%)");
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(t) {
343
- const e = document.createElement("div");
344
- e.className = "achievement-popup", e.innerHTML = `
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">${t.icon}</div>
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">${t.name}</div>
351
- <div class="achievement-desc">${t.description}</div>
352
- <div class="achievement-xp">+${t.xp} XP</div>
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(e), setTimeout(() => {
356
- const s = e.getBoundingClientRect();
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
- e.style.animation = "slideOut 0.3s ease-in forwards", setTimeout(() => e.remove(), 300);
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(t, e) {
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">${e}</div>
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(t, e, s, i = "#ffd700", a = "24px") {
386
+ showFloatingText(e, t, s, i = "#ffd700", a = "24px") {
387
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);
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(t, e, s) {
394
- this.showFloatingText(`${t}× COMBO!`, e, s, "#ff6b6b", "32px"), this.particleSystem.fire(e, s, 20);
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(t, e) {
400
- this.showFloatingText("CLOSE CALL!", t, e, "#45b7d1", "20px"), this.particleSystem.sparkle(t, e, 15, "#45b7d1");
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(t, e, s) {
406
- this.showFloatingText(`LUCKY ${t}×!`, e, s, "#ffd700", "36px"), this.particleSystem.starBurst(e, s, 12), this.particleSystem.confetti(e, s, 25);
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(t, e = "normal") {
411
+ showNotification(e, t = "normal") {
412
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");
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(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";
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
- 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 = `
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;">${t.score}</div>
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(e);
430
+ `, document.body.appendChild(t);
431
431
  }
432
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());
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
- * Confetti explosion (for achievements, level ups)
441
+ * Register a custom sprite image
442
+ * @param {string} key
443
+ * @param {string} url
442
444
  */
443
- confetti(t, e, s = 50) {
444
- const i = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#f7dc6f", "#c06c84", "#6c5ce7"];
445
- for (let a = 0; a < s; 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: 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
- * Sparkle effect (for score milestones, combos)
464
- */
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;
468
- this.particles.push({
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
- * Coin shower (for collecting coins)
486
- */
487
- coinShower(t, e, s = 10) {
488
- for (let i = 0; i < s; i++)
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();
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
- * Fire particles (for streaks, combos)
465
+ * Emit particles with custom config
466
+ * @param {Object} config
505
467
  */
506
- fire(t, e, s = 15) {
507
- const i = ["#ff6b00", "#ff8800", "#ffaa00", "#ff4400"];
508
- for (let a = 0; a < s; 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: i[Math.floor(Math.random() * i.length)],
515
- size: Math.random() * 6 + 3,
516
- life: 1,
517
- decay: 0.025,
518
- gravity: -0.2,
519
- // Upward
520
- type: "fire"
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
- * Star burst (for "perfect" moments)
526
- */
527
- starBurst(t, e, s = 8) {
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, 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
- });
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 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);
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 = 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();
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
- _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;
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 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);
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 t = window.AudioContext || window.webkitAudioContext;
588
- this.audioContext = new t();
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(t, e) {
605
- this.customSounds[t] = e, this.audioContext && this.loadSound(t, e);
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(t) {
612
- const e = Object.entries(t).map(([s, i]) => this.loadSound(s, i));
613
- await Promise.allSettled(e);
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(t, e) {
631
+ async loadSound(e, t) {
619
632
  try {
620
633
  if (!this.audioContext)
621
634
  return;
622
- const i = await (await fetch(e)).arrayBuffer(), a = await this.audioContext.decodeAudioData(i);
623
- this.assets.set(t, a);
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 '${t}':`, s);
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(t) {
645
+ async play(e) {
633
646
  if (this.muted) return;
634
- if (this.initAudio(), this.assets.has(t)) {
635
- this._playBuffer(this.assets.get(t));
647
+ if (this.initAudio(), this.assets.has(e)) {
648
+ this._playBuffer(this.assets.get(e));
636
649
  return;
637
650
  }
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));
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 e = {
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[t] && e[t]();
664
+ t[e] && t[e]();
652
665
  }
653
- _playBuffer(t) {
654
- const e = this.audioContext.createBufferSource();
655
- e.buffer = t, e.connect(this.audioContext.destination), e.start(0);
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(t, e, s = "sine", i = 0.3) {
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 = 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);
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(t = !1) {
668
- if (!t && this.assets.has("jump")) return this.play("jump");
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(t = !1) {
672
- if (!t && this.assets.has("score")) return this.play("score");
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(t = !1) {
676
- if (!t && this.assets.has("gameover")) return this.play("gameover");
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(t = !1) {
680
- if (!t && this.assets.has("click")) return this.play("click");
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(t = !1) {
684
- if (!t && this.assets.has("success")) return this.play("success");
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(t = !1) {
688
- if (!t && this.assets.has("error")) return this.play("error");
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 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();
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", (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();
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
- u as DataService,
719
- y as GameUI,
720
- v as ParticleSystem,
721
- m as RewardSystem,
722
- f as SoundManager,
723
- g as default
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
  };