@gamebob/axon-surge 0.16.0 → 0.18.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.
Files changed (78) hide show
  1. package/dist/axon-surge.js +1943 -1325
  2. package/dist/config/GameConfig.d.ts +27 -1
  3. package/dist/config/GameConfig.d.ts.map +1 -1
  4. package/dist/content/ContentRegistry.d.ts +25 -0
  5. package/dist/content/ContentRegistry.d.ts.map +1 -0
  6. package/dist/content/ContentTypes.d.ts +67 -0
  7. package/dist/content/ContentTypes.d.ts.map +1 -0
  8. package/dist/content/DefaultContentRegistry.d.ts +3 -0
  9. package/dist/content/DefaultContentRegistry.d.ts.map +1 -0
  10. package/dist/content/DefinitionRegistry.d.ts +22 -0
  11. package/dist/content/DefinitionRegistry.d.ts.map +1 -0
  12. package/dist/content/EnemyBehavior.d.ts +70 -0
  13. package/dist/content/EnemyBehavior.d.ts.map +1 -0
  14. package/dist/content/EnemyBehaviors.d.ts +4 -0
  15. package/dist/content/EnemyBehaviors.d.ts.map +1 -0
  16. package/dist/content/EnemyDefinitions.d.ts +4 -0
  17. package/dist/content/EnemyDefinitions.d.ts.map +1 -0
  18. package/dist/core/GameEngine.d.ts +5 -0
  19. package/dist/core/GameEngine.d.ts.map +1 -1
  20. package/dist/director/InteractiveTutorial.d.ts +6 -0
  21. package/dist/director/InteractiveTutorial.d.ts.map +1 -1
  22. package/dist/director/SpawnerPatterns.d.ts +10 -7
  23. package/dist/director/SpawnerPatterns.d.ts.map +1 -1
  24. package/dist/director/WaveDirector.d.ts +5 -1
  25. package/dist/director/WaveDirector.d.ts.map +1 -1
  26. package/dist/entities/CoreEntity.d.ts +3 -1
  27. package/dist/entities/CoreEntity.d.ts.map +1 -1
  28. package/dist/entities/EnemyEntity.d.ts +7 -0
  29. package/dist/entities/EnemyEntity.d.ts.map +1 -1
  30. package/dist/entities/ExperienceOrb.d.ts +26 -3
  31. package/dist/entities/ExperienceOrb.d.ts.map +1 -1
  32. package/dist/factory/EntityFactory.d.ts +5 -2
  33. package/dist/factory/EntityFactory.d.ts.map +1 -1
  34. package/dist/input/InputManager.d.ts +3 -1
  35. package/dist/input/InputManager.d.ts.map +1 -1
  36. package/dist/physics/EffectClusterer.d.ts +18 -0
  37. package/dist/physics/EffectClusterer.d.ts.map +1 -0
  38. package/dist/physics/PhysicsEngine.d.ts +5 -1
  39. package/dist/physics/PhysicsEngine.d.ts.map +1 -1
  40. package/dist/physics/handlers/ChainReactionHelper.d.ts +2 -2
  41. package/dist/physics/handlers/ChainReactionHelper.d.ts.map +1 -1
  42. package/dist/physics/handlers/CoreCollisionHandler.d.ts +3 -1
  43. package/dist/physics/handlers/CoreCollisionHandler.d.ts.map +1 -1
  44. package/dist/physics/handlers/ShieldCollisionHandler.d.ts +3 -1
  45. package/dist/physics/handlers/ShieldCollisionHandler.d.ts.map +1 -1
  46. package/dist/physics/handlers/ThrownCollisionHandler.d.ts +3 -1
  47. package/dist/physics/handlers/ThrownCollisionHandler.d.ts.map +1 -1
  48. package/dist/physics/handlers/ToxicPoolPhysicsHandler.d.ts +2 -1
  49. package/dist/physics/handlers/ToxicPoolPhysicsHandler.d.ts.map +1 -1
  50. package/dist/physics/handlers/VortexPhysicsHandler.d.ts +3 -2
  51. package/dist/physics/handlers/VortexPhysicsHandler.d.ts.map +1 -1
  52. package/dist/powerups/PowerUpRegistry.d.ts +10 -3
  53. package/dist/powerups/PowerUpRegistry.d.ts.map +1 -1
  54. package/dist/powerups/PowerUpTypes.d.ts +6 -1
  55. package/dist/powerups/PowerUpTypes.d.ts.map +1 -1
  56. package/dist/render/CanvasRenderer.d.ts +6 -2
  57. package/dist/render/CanvasRenderer.d.ts.map +1 -1
  58. package/dist/render/EnemyShapeRenderer.d.ts +17 -0
  59. package/dist/render/EnemyShapeRenderer.d.ts.map +1 -0
  60. package/dist/render/TutorialArrowRenderer.d.ts.map +1 -1
  61. package/dist/strategies/FragmentClusterStrategy.d.ts.map +1 -1
  62. package/dist/strategies/GravityParasiteStrategy.d.ts.map +1 -1
  63. package/dist/strategies/HeavyShieldStrategy.d.ts.map +1 -1
  64. package/dist/strategies/ImpactStrategy.d.ts +4 -0
  65. package/dist/strategies/ImpactStrategy.d.ts.map +1 -1
  66. package/dist/strategies/PiercingCellStrategy.d.ts.map +1 -1
  67. package/dist/strategies/ToxicSporeStrategy.d.ts.map +1 -1
  68. package/dist/styles.css +1 -1
  69. package/dist/theme/GameColors.d.ts +19 -0
  70. package/dist/theme/GameColors.d.ts.map +1 -0
  71. package/dist/theme/ThemeManager.d.ts.map +1 -1
  72. package/dist/types/game.d.ts +5 -1
  73. package/dist/types/game.d.ts.map +1 -1
  74. package/dist/ui/HUDOverlay.d.ts +2 -6
  75. package/dist/ui/HUDOverlay.d.ts.map +1 -1
  76. package/dist/ui/ProgressBar.d.ts +8 -4
  77. package/dist/ui/ProgressBar.d.ts.map +1 -1
  78. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- class W {
1
+ class it {
2
2
  listeners = /* @__PURE__ */ new Map();
3
3
  on(t, e) {
4
4
  return this.listeners.has(t) || this.listeners.set(t, /* @__PURE__ */ new Set()), this.listeners.get(t).add(e), () => this.off(t, e);
@@ -15,7 +15,7 @@ class W {
15
15
  this.listeners.clear();
16
16
  }
17
17
  }
18
- class Y {
18
+ class st {
19
19
  animationFrameId = null;
20
20
  lastTime = 0;
21
21
  nextFrameTime = 0;
@@ -49,13 +49,17 @@ class Y {
49
49
  this.lastTime = t, this.nextFrameTime += e, this.nextFrameTime < t - e && (this.nextFrameTime = t + e), this.updateCallback(i), this.renderCallback(), this.animationFrameId = requestAnimationFrame(this.tick);
50
50
  }
51
51
  }
52
- const G = {
52
+ const X = {
53
53
  core: {
54
54
  maxIntegrity: 3,
55
55
  invulnerabilityDuration: 1.5,
56
56
  radius: 32,
57
57
  vampirismHealProgressRate: 0,
58
- hasOffscreenIndicators: !1
58
+ hasOffscreenIndicators: !1,
59
+ autoDeflectFragments: !0,
60
+ fragmentDeflectionLookahead: 0.32,
61
+ fragmentDeflectionSpeed: 900,
62
+ fragmentDeflectionCooldown: 0.45
59
63
  },
60
64
  toxicPool: {
61
65
  baseRadius: 40,
@@ -84,7 +88,10 @@ const G = {
84
88
  },
85
89
  cluster: {
86
90
  fragmentCount: 6,
87
- hasHoming: !1
91
+ hasHoming: !1,
92
+ explosionParticleCount: 18,
93
+ maxExplosionShockwaves: 12,
94
+ maxExplosionFloatingTexts: 8
88
95
  },
89
96
  piercer: {
90
97
  terminalAoERadius: 0,
@@ -96,16 +103,35 @@ const G = {
96
103
  psionicRadius: 70,
97
104
  corePickupRadius: 120,
98
105
  absorptionSpeed: 680
106
+ },
107
+ experience: {
108
+ mergeDistance: 34,
109
+ magneticAttractionDistance: 190,
110
+ magneticAttractionStrength: 900,
111
+ baseRadius: 5,
112
+ radiusGrowthPerLevel: 2.5,
113
+ maxRadius: 24,
114
+ mergeDuration: 0.65,
115
+ // Runs produce large XP volumes; keep colour tiers readable before
116
+ // a cluster reaches red.
117
+ blueThreshold: 4,
118
+ yellowThreshold: 32,
119
+ redThreshold: 256
120
+ },
121
+ effectAggregation: {
122
+ toxicPoolMergePadding: 12,
123
+ gravityVortexMergePadding: 24,
124
+ shockwaveMergeDistance: 120
99
125
  }
100
126
  };
101
- class A {
127
+ class N {
102
128
  static instance;
103
129
  config;
104
130
  constructor() {
105
- this.config = JSON.parse(JSON.stringify(G));
131
+ this.config = JSON.parse(JSON.stringify(X));
106
132
  }
107
133
  static getInstance() {
108
- return A.instance || (A.instance = new A()), A.instance;
134
+ return N.instance || (N.instance = new N()), N.instance;
109
135
  }
110
136
  getConfig() {
111
137
  return this.config;
@@ -123,10 +149,15 @@ class A {
123
149
  this.config.core.maxIntegrity += t;
124
150
  }
125
151
  resetToDefault() {
126
- this.config = JSON.parse(JSON.stringify(G));
152
+ this.config = JSON.parse(JSON.stringify(X));
127
153
  }
128
154
  }
129
- class Z {
155
+ class at {
156
+ constructor(t, e, i) {
157
+ this.configManager = i;
158
+ const s = this.configManager.getConfig().core;
159
+ this.position = { x: t, y: e }, this.radius = s.radius, this.integrity = s.maxIntegrity, this.maxIntegrity = s.maxIntegrity, this.maxInvulnerabilityDuration = s.invulnerabilityDuration, this.organicSeed = Math.random() * 100;
160
+ }
130
161
  position;
131
162
  radius;
132
163
  integrity;
@@ -138,16 +169,12 @@ class Z {
138
169
  customColor = { r: 0, g: 240, b: 255 };
139
170
  powerUpCount = 0;
140
171
  collectedPowerUpColors = [];
141
- constructor(t, e) {
142
- const i = A.getInstance().getConfig().core;
143
- this.position = { x: t, y: e }, this.radius = i.radius, this.integrity = i.maxIntegrity, this.maxIntegrity = i.maxIntegrity, this.maxInvulnerabilityDuration = i.invulnerabilityDuration, this.organicSeed = Math.random() * 100;
144
- }
145
172
  absorbPowerUpColor(t, e = 0.35) {
146
173
  this.powerUpCount++, this.collectedPowerUpColors.push(t);
147
174
  const i = t.replace("#", "");
148
175
  if (i.length === 6) {
149
- const s = parseInt(i.substring(0, 2), 16), a = parseInt(i.substring(2, 4), 16), l = parseInt(i.substring(4, 6), 16);
150
- this.customColor.r = Math.round(this.customColor.r * (1 - e) + s * e), this.customColor.g = Math.round(this.customColor.g * (1 - e) + a * e), this.customColor.b = Math.round(this.customColor.b * (1 - e) + l * e);
176
+ const s = parseInt(i.substring(0, 2), 16), a = parseInt(i.substring(2, 4), 16), n = parseInt(i.substring(4, 6), 16);
177
+ this.customColor.r = Math.round(this.customColor.r * (1 - e) + s * e), this.customColor.g = Math.round(this.customColor.g * (1 - e) + a * e), this.customColor.b = Math.round(this.customColor.b * (1 - e) + n * e);
151
178
  }
152
179
  }
153
180
  getHitboxRadius() {
@@ -166,7 +193,7 @@ class Z {
166
193
  }
167
194
  repairProgress = 0;
168
195
  addKillRepair() {
169
- const t = A.getInstance().getConfig().core.vampirismHealProgressRate;
196
+ const t = this.configManager.getConfig().core.vampirismHealProgressRate;
170
197
  return t <= 0 ? !1 : (this.repairProgress += t, this.repairProgress >= 1 ? (this.integrity += 1, this.integrity > this.maxIntegrity && (this.maxIntegrity = this.integrity), this.repairProgress = 0, !0) : !1);
171
198
  }
172
199
  heal(t = 1) {
@@ -179,30 +206,42 @@ class Z {
179
206
  return this.invulnerabilityTimer > 0 ? !1 : this.integrity > 0 ? (this.integrity -= 1, this.invulnerabilityTimer = this.maxInvulnerabilityDuration, !0) : !1;
180
207
  }
181
208
  reset(t, e) {
182
- const i = A.getInstance().getConfig().core;
209
+ const i = this.configManager.getConfig().core;
183
210
  this.position = { x: t, y: e }, this.radius = i.radius, this.maxIntegrity = i.maxIntegrity, this.integrity = this.maxIntegrity, this.maxInvulnerabilityDuration = i.invulnerabilityDuration, this.pulsePhase = 0, this.invulnerabilityTimer = 0, this.repairProgress = 0, this.powerUpCount = 0, this.collectedPowerUpColors = [], this.customColor = { r: 0, g: 240, b: 255 };
184
211
  }
185
212
  }
186
- class U {
213
+ const J = {
214
+ rendererId: "circle",
215
+ rotatesWithVelocity: !0,
216
+ canBeChainReactionTarget: !0,
217
+ synergyKey: (p) => p.color
218
+ };
219
+ class F {
187
220
  id;
188
221
  position = { x: 0, y: 0 };
189
222
  velocity = { x: 0, y: 0 };
190
223
  radius = 20;
191
- type = "piercer";
224
+ type = "";
192
225
  active = !1;
193
226
  isGrabbed = !1;
194
227
  isThrown = !1;
195
228
  wasManipulated = !1;
229
+ isGrabbable = !0;
196
230
  rotation = 0;
197
- color = "#ff2a5f";
231
+ color = "#ffffff";
198
232
  strategy = null;
199
- hitRadiusPadding = 1.2;
233
+ behavior = J;
234
+ synergyKey = "";
235
+ /** Collision padding is deliberately small; visual area effects define their own radius. */
236
+ hitRadiusPadding = 0.2;
200
237
  maxSpeed = 200;
201
238
  isStationaryShield = !1;
202
239
  shieldAngle = 0;
203
240
  launchDelay = 0;
204
241
  remainingHomingKills = 0;
205
242
  experienceDropPending = !1;
243
+ psionicDeflectionCooldown = 0;
244
+ psionicDeflectionTimer = 0;
206
245
  isDying = !1;
207
246
  deathType = "none";
208
247
  deathTimer = 0;
@@ -222,7 +261,7 @@ class U {
222
261
  }
223
262
  update(t) {
224
263
  if (this.active) {
225
- if (this.isDying) {
264
+ if (this.psionicDeflectionCooldown = Math.max(0, this.psionicDeflectionCooldown - t), this.psionicDeflectionTimer = Math.max(0, this.psionicDeflectionTimer - t), this.isDying) {
226
265
  this.deathTimer += t, this.deathTimer >= this.maxDeathTimer && (this.active = !1, this.isDying = !1);
227
266
  return;
228
267
  }
@@ -234,15 +273,15 @@ class U {
234
273
  this.velocity.x *= i, this.velocity.y *= i;
235
274
  }
236
275
  }
237
- this.position.x += this.velocity.x * t, this.position.y += this.velocity.y * t, this.type !== "heavy" && (this.velocity.x !== 0 || this.velocity.y !== 0) && (this.rotation = Math.atan2(this.velocity.y, this.velocity.x));
276
+ this.position.x += this.velocity.x * t, this.position.y += this.velocity.y * t, this.behavior.rotatesWithVelocity && (this.velocity.x !== 0 || this.velocity.y !== 0) && (this.rotation = Math.atan2(this.velocity.y, this.velocity.x));
238
277
  }
239
278
  }
240
279
  }
241
280
  reset() {
242
- this.active = !1, this.isGrabbed = !1, this.isThrown = !1, this.wasManipulated = !1, this.isDying = !1, this.deathType = "none", this.deathTimer = 0, this.sliceAngle = 0, this.isStationaryShield = !1, this.shieldAngle = 0, this.launchDelay = 0, this.experienceDropPending = !1, this.position = { x: 0, y: 0 }, this.velocity = { x: 0, y: 0 }, this.rotation = 0, this.strategy = null, this.maxSpeed = 200;
281
+ this.active = !1, this.isGrabbed = !1, this.isThrown = !1, this.wasManipulated = !1, this.isGrabbable = !0, this.behavior = J, this.synergyKey = "", this.isDying = !1, this.deathType = "none", this.deathTimer = 0, this.sliceAngle = 0, this.isStationaryShield = !1, this.shieldAngle = 0, this.launchDelay = 0, this.experienceDropPending = !1, this.psionicDeflectionCooldown = 0, this.psionicDeflectionTimer = 0, this.position = { x: 0, y: 0 }, this.velocity = { x: 0, y: 0 }, this.rotation = 0, this.strategy = null, this.maxSpeed = 200, this.hitRadiusPadding = 0.2;
243
282
  }
244
283
  }
245
- class H {
284
+ class j {
246
285
  id;
247
286
  definition;
248
287
  position = { x: 0, y: 0 };
@@ -259,7 +298,7 @@ class H {
259
298
  this.active && !this.isGrabbed && !this.isHovered && (this.orbitAngle += t * 0.25, this.position.x = e.x + Math.cos(this.orbitAngle) * this.orbitDistance, this.position.y = e.y + Math.sin(this.orbitAngle) * this.orbitDistance);
260
299
  }
261
300
  }
262
- class $ {
301
+ class ot {
263
302
  pointerPosition = { x: 0, y: 0 };
264
303
  isPointerDown = !1;
265
304
  grabbedEnemy = null;
@@ -267,21 +306,22 @@ class $ {
267
306
  dragStart = null;
268
307
  pointerHistory = [];
269
308
  canvas;
270
- constructor(t) {
271
- this.canvas = t, this.bindEvents();
309
+ configManager;
310
+ constructor(t, e) {
311
+ this.canvas = t, this.configManager = e, this.bindEvents();
272
312
  }
273
313
  updatePointerPos(t, e) {
274
- const i = this.canvas.getBoundingClientRect(), s = i.width > 0 ? this.canvas.width / i.width : 1, a = i.height > 0 ? this.canvas.height / i.height : 1, l = performance.now() / 1e3;
314
+ const i = this.canvas.getBoundingClientRect(), s = i.width > 0 ? this.canvas.width / i.width : 1, a = i.height > 0 ? this.canvas.height / i.height : 1, n = performance.now() / 1e3;
275
315
  this.pointerPosition = {
276
316
  x: (t - i.left) * s,
277
317
  y: (e - i.top) * a
278
318
  }, this.pointerHistory.push({
279
319
  x: this.pointerPosition.x,
280
320
  y: this.pointerPosition.y,
281
- time: l
321
+ time: n
282
322
  });
283
- const n = l - 0.08;
284
- this.pointerHistory = this.pointerHistory.filter((o) => o.time >= n), this.grabbedEnemy && (this.grabbedEnemy.position = { ...this.pointerPosition });
323
+ const o = n - 0.08;
324
+ this.pointerHistory = this.pointerHistory.filter((r) => r.time >= o), this.grabbedEnemy && (this.grabbedEnemy.position = { ...this.pointerPosition });
285
325
  }
286
326
  bindEvents() {
287
327
  this.canvas.addEventListener("mousemove", (t) => {
@@ -320,9 +360,9 @@ class $ {
320
360
  if (this.grabbedEnemy) return this.grabbedEnemy;
321
361
  const i = e ? 50 : 20;
322
362
  for (const s of t) {
323
- if (!s.active || s.isGrabbed || s.isThrown || s.wasManipulated || s.launchDelay > 0) continue;
324
- const a = this.pointerPosition.x - s.position.x, l = this.pointerPosition.y - s.position.y;
325
- if (Math.hypot(a, l) <= s.getHitRadius() + i)
363
+ if (!s.active || !s.isGrabbable || s.isGrabbed || s.isThrown || s.wasManipulated || s.launchDelay > 0) continue;
364
+ const a = this.pointerPosition.x - s.position.x, n = this.pointerPosition.y - s.position.y;
365
+ if (Math.hypot(a, n) <= s.getHitRadius() + i)
326
366
  return s.isGrabbed = !0, this.grabbedEnemy = s, this.dragStart = { ...this.pointerPosition }, this.pointerHistory = [], s;
327
367
  }
328
368
  return null;
@@ -332,8 +372,8 @@ class $ {
332
372
  const i = e ? 50 : 20;
333
373
  for (const s of t) {
334
374
  if (!s.active || s.isGrabbed) continue;
335
- const a = this.pointerPosition.x - s.position.x, l = this.pointerPosition.y - s.position.y;
336
- if (Math.hypot(a, l) <= s.radius * 1.5 + i)
375
+ const a = this.pointerPosition.x - s.position.x, n = this.pointerPosition.y - s.position.y;
376
+ if (Math.hypot(a, n) <= s.radius * 1.5 + i)
337
377
  return s.isGrabbed = !0, this.grabbedOrb = s, s;
338
378
  }
339
379
  return null;
@@ -342,37 +382,31 @@ class $ {
342
382
  if (!this.grabbedEnemy) return null;
343
383
  const t = this.grabbedEnemy;
344
384
  t.isGrabbed = !1, t.isThrown = !0, t.wasManipulated = !0;
345
- const i = performance.now() / 1e3 - 0.08, s = this.pointerHistory.filter((o) => o.time >= i);
346
- let a = 0, l = 0;
385
+ const i = performance.now() / 1e3 - 0.08, s = this.pointerHistory.filter((r) => r.time >= i);
386
+ let a = 0, n = 0;
347
387
  if (s.length >= 2) {
348
- const o = s[0], r = s[s.length - 1], c = Math.max(0.01, r.time - o.time);
349
- a = (r.x - o.x) / c, l = (r.y - o.y) / c;
388
+ const r = s[0], c = s[s.length - 1], l = Math.max(0.01, c.time - r.time);
389
+ a = (c.x - r.x) / l, n = (c.y - r.y) / l;
350
390
  }
351
- const n = Math.hypot(a, l);
352
- if (n > 30) {
353
- const r = Math.min(n * 1.15, 2500), c = Math.atan2(l, a);
391
+ const o = Math.hypot(a, n);
392
+ if (o > 30) {
393
+ const c = Math.min(o * 1.15, 2500), l = Math.atan2(n, a);
354
394
  t.velocity = {
355
- x: Math.cos(c) * r,
356
- y: Math.sin(c) * r
395
+ x: Math.cos(l) * c,
396
+ y: Math.sin(l) * c
357
397
  };
358
398
  } else if (this.dragStart) {
359
- const o = t.position.x - this.dragStart.x, r = t.position.y - this.dragStart.y, c = Math.hypot(o, r);
360
- if (c > 5) {
361
- const h = Math.atan2(r, o), p = Math.min(c * 4, 600);
399
+ const r = t.position.x - this.dragStart.x, c = t.position.y - this.dragStart.y, l = Math.hypot(r, c);
400
+ if (l > 5) {
401
+ const h = Math.atan2(c, r), u = Math.min(l * 4, 600);
362
402
  t.velocity = {
363
- x: Math.cos(h) * p,
364
- y: Math.sin(h) * p
403
+ x: Math.cos(h) * u,
404
+ y: Math.sin(h) * u
365
405
  };
366
406
  } else
367
407
  t.velocity = { x: 0, y: 0 }, t.isThrown = !1;
368
408
  }
369
- if (t.type === "heavy")
370
- t.shieldAngle = t.rotation, t.velocity = { x: 0, y: 0 }, t.isStationaryShield = !0, t.isThrown = !1;
371
- else if (t.type === "piercer") {
372
- const o = A.getInstance().getConfig().piercer;
373
- o.hasHoming && (t.remainingHomingKills = o.homingTargets);
374
- }
375
- return this.grabbedEnemy = null, this.dragStart = null, this.pointerHistory = [], t;
409
+ return t.behavior.onRelease?.(t, { config: this.configManager.getConfig() }), this.grabbedEnemy = null, this.dragStart = null, this.pointerHistory = [], t;
376
410
  }
377
411
  forceRelease() {
378
412
  this.grabbedEnemy && (this.grabbedEnemy.isGrabbed = !1, this.grabbedEnemy.isThrown = !1, this.grabbedEnemy.wasManipulated = !1), this.grabbedOrb && (this.grabbedOrb.isGrabbed = !1), this.grabbedEnemy = null, this.grabbedOrb = null, this.dragStart = null, this.pointerHistory = [];
@@ -381,7 +415,7 @@ class $ {
381
415
  this.grabbedEnemy = null, this.grabbedOrb = null, this.dragStart = null, this.pointerHistory = [], this.isPointerDown = !1;
382
416
  }
383
417
  }
384
- class z {
418
+ class nt {
385
419
  cellSize;
386
420
  grid = /* @__PURE__ */ new Map();
387
421
  constructor(t = 100) {
@@ -392,23 +426,23 @@ class z {
392
426
  }
393
427
  insert(t) {
394
428
  const e = Math.floor((t.position.x - t.radius) / this.cellSize), i = Math.floor((t.position.x + t.radius) / this.cellSize), s = Math.floor((t.position.y - t.radius) / this.cellSize), a = Math.floor((t.position.y + t.radius) / this.cellSize);
395
- for (let l = e; l <= i; l++)
396
- for (let n = s; n <= a; n++) {
397
- const o = `${l}:${n}`;
398
- this.grid.has(o) || this.grid.set(o, /* @__PURE__ */ new Set()), this.grid.get(o).add(t);
429
+ for (let n = e; n <= i; n++)
430
+ for (let o = s; o <= a; o++) {
431
+ const r = `${n}:${o}`;
432
+ this.grid.has(r) || this.grid.set(r, /* @__PURE__ */ new Set()), this.grid.get(r).add(t);
399
433
  }
400
434
  }
401
435
  getNearby(t, e) {
402
- const i = /* @__PURE__ */ new Set(), s = Math.floor((t.x - e) / this.cellSize), a = Math.floor((t.x + e) / this.cellSize), l = Math.floor((t.y - e) / this.cellSize), n = Math.floor((t.y + e) / this.cellSize);
403
- for (let o = s; o <= a; o++)
404
- for (let r = l; r <= n; r++) {
405
- const c = `${o}:${r}`, h = this.grid.get(c);
406
- h && h.forEach((p) => i.add(p));
436
+ const i = /* @__PURE__ */ new Set(), s = Math.floor((t.x - e) / this.cellSize), a = Math.floor((t.x + e) / this.cellSize), n = Math.floor((t.y - e) / this.cellSize), o = Math.floor((t.y + e) / this.cellSize);
437
+ for (let r = s; r <= a; r++)
438
+ for (let c = n; c <= o; c++) {
439
+ const l = `${r}:${c}`, h = this.grid.get(l);
440
+ h && h.forEach((u) => i.add(u));
407
441
  }
408
442
  return i;
409
443
  }
410
444
  }
411
- class J {
445
+ class rt {
412
446
  shakeTime = 0;
413
447
  shakeDuration = 0.14;
414
448
  shakeIntensity = 0;
@@ -446,7 +480,7 @@ class J {
446
480
  };
447
481
  }
448
482
  }
449
- const D = {
483
+ const G = {
450
484
  es: {
451
485
  title: "AXON SURGE",
452
486
  startButton: "INICIAR ENLACE",
@@ -1294,7 +1328,7 @@ const D = {
1294
1328
  pu_core_ring_title: "核心过载",
1295
1329
  pu_core_ring_desc: "+1 生命环"
1296
1330
  }
1297
- }, V = [
1331
+ }, tt = [
1298
1332
  "es",
1299
1333
  "en",
1300
1334
  "fr",
@@ -1311,29 +1345,29 @@ const D = {
1311
1345
  "ko",
1312
1346
  "zh"
1313
1347
  ];
1314
- function w(E) {
1315
- return V.includes(E);
1348
+ function B(p) {
1349
+ return tt.includes(p);
1316
1350
  }
1317
- const B = "axon_surge_language";
1318
- class X {
1351
+ const q = "axon_surge_language";
1352
+ class ct {
1319
1353
  currentLanguage;
1320
1354
  constructor(t) {
1321
1355
  this.currentLanguage = this.detectLanguage(t);
1322
1356
  }
1323
1357
  detectLanguage(t) {
1324
1358
  try {
1325
- const e = localStorage.getItem(B);
1326
- if (e && w(e))
1359
+ const e = localStorage.getItem(q);
1360
+ if (e && B(e))
1327
1361
  return e;
1328
1362
  } catch {
1329
1363
  }
1330
- if (t && w(t))
1364
+ if (t && B(t))
1331
1365
  return t;
1332
1366
  try {
1333
1367
  const e = (document.documentElement.lang || document.body?.getAttribute("lang") || document.documentElement.getAttribute("data-lang") || "").toLowerCase();
1334
1368
  if (e) {
1335
1369
  const i = e.split("-")[0];
1336
- if (i && w(i))
1370
+ if (i && B(i))
1337
1371
  return i;
1338
1372
  }
1339
1373
  } catch {
@@ -1342,7 +1376,7 @@ class X {
1342
1376
  const e = (navigator.language || navigator.languages && navigator.languages[0] || "").toLowerCase();
1343
1377
  if (e) {
1344
1378
  const i = e.split("-")[0];
1345
- if (i && w(i))
1379
+ if (i && B(i))
1346
1380
  return i;
1347
1381
  }
1348
1382
  } catch {
@@ -1350,10 +1384,10 @@ class X {
1350
1384
  return "en";
1351
1385
  }
1352
1386
  setLanguage(t) {
1353
- if (D[t]) {
1387
+ if (G[t]) {
1354
1388
  this.currentLanguage = t;
1355
1389
  try {
1356
- localStorage.setItem(B, t);
1390
+ localStorage.setItem(q, t);
1357
1391
  } catch {
1358
1392
  }
1359
1393
  }
@@ -1362,37 +1396,50 @@ class X {
1362
1396
  return this.currentLanguage;
1363
1397
  }
1364
1398
  translate(t, e) {
1365
- let s = (D[this.currentLanguage] || D.en)[t] || D.en[t] || String(t);
1366
- return e && Object.entries(e).forEach(([a, l]) => {
1367
- s = s.replace(`{${a}}`, String(l));
1399
+ let s = (G[this.currentLanguage] || G.en)[t] || G.en[t] || String(t);
1400
+ return e && Object.entries(e).forEach(([a, n]) => {
1401
+ s = s.replace(`{${a}}`, String(n));
1368
1402
  }), s;
1369
1403
  }
1370
1404
  }
1371
- class j {
1372
- static update(t, e, i) {
1373
- const s = A.getInstance().getConfig();
1405
+ class V {
1406
+ static killEnemy(t, e, i) {
1407
+ !t.active || t.isDying || (t.experienceDropPending = !0, i.shockwaves.push({
1408
+ id: Math.random().toString(),
1409
+ position: { ...t.position },
1410
+ radius: 10,
1411
+ maxRadius: 180,
1412
+ color: t.color,
1413
+ alpha: 1,
1414
+ lifetime: 0,
1415
+ maxLifetime: 0.5
1416
+ }), t.behavior.onChainReaction ? t.behavior.onChainReaction({ ...i, enemy: t, angle: e }) : t.triggerDissolveDeath(!0));
1417
+ }
1418
+ }
1419
+ class lt {
1420
+ static update(t, e, i, s) {
1374
1421
  for (let a = t.length - 1; a >= 0; a--) {
1375
- const l = t[a];
1376
- if (l) {
1377
- l.duration += i;
1378
- for (const n of e) {
1379
- if (!n.active || n.isGrabbed || n.isDying || n.isStationaryShield || n.launchDelay > 0 || n.isThrown && n.type === "piercer") continue;
1380
- const o = l.position.x - n.position.x, r = l.position.y - n.position.y, c = o * o + r * r, h = Math.sqrt(c);
1381
- if (h > 2 && c <= l.radius * l.radius) {
1382
- const p = Math.pow(1 - h / l.radius, 0.7), d = l.pullForce * p, _ = Math.atan2(r, o);
1383
- n.velocity.x += Math.cos(_) * d * i, n.velocity.y += Math.sin(_) * d * i, n.position.x += Math.cos(_) * d * i * 0.8, n.position.y += Math.sin(_) * d * i * 0.8;
1422
+ const n = t[a];
1423
+ if (n) {
1424
+ n.duration += i;
1425
+ for (const o of e) {
1426
+ if (!o.active || o.isGrabbed || o.isDying || o.isStationaryShield || o.launchDelay > 0 || o.isThrown && o.behavior.canInteractWithHazard?.(o, "gravity-vortex") === !1) continue;
1427
+ const r = n.position.x - o.position.x, c = n.position.y - o.position.y, l = r * r + c * c, h = Math.sqrt(l);
1428
+ if (h > 2 && l <= n.radius * n.radius) {
1429
+ const u = Math.pow(1 - h / n.radius, 0.7), d = n.pullForce * u, g = Math.atan2(c, r);
1430
+ o.velocity.x += Math.cos(g) * d * i, o.velocity.y += Math.sin(g) * d * i, o.position.x += Math.cos(g) * d * i * 0.8, o.position.y += Math.sin(g) * d * i * 0.8;
1384
1431
  }
1385
1432
  }
1386
- if (l.duration >= l.maxDuration) {
1433
+ if (n.duration >= n.maxDuration) {
1387
1434
  if (s.gravityVortex.hasImplosionLaunch)
1388
- for (const n of e) {
1389
- if (!n.active || n.isDying) continue;
1390
- const o = n.position.x - l.position.x, r = n.position.y - l.position.y;
1391
- if (o * o + r * r <= l.radius * l.radius) {
1392
- const h = Math.atan2(r, o), p = 1200;
1393
- n.isThrown = !0, n.wasManipulated = !0, n.velocity = {
1394
- x: Math.cos(h) * p,
1395
- y: Math.sin(h) * p
1435
+ for (const o of e) {
1436
+ if (!o.active || o.isDying) continue;
1437
+ const r = o.position.x - n.position.x, c = o.position.y - n.position.y;
1438
+ if (r * r + c * c <= n.radius * n.radius) {
1439
+ const h = Math.atan2(c, r), u = 1200;
1440
+ o.isThrown = !0, o.wasManipulated = !0, o.velocity = {
1441
+ x: Math.cos(h) * u,
1442
+ y: Math.sin(h) * u
1396
1443
  };
1397
1444
  }
1398
1445
  }
@@ -1402,33 +1449,48 @@ class j {
1402
1449
  }
1403
1450
  }
1404
1451
  }
1405
- class q {
1406
- static update(t, e, i, s, a, l, n, o, r, c) {
1407
- for (const h of e) {
1408
- if (!h.active || h.isGrabbed || h.isDying || h.launchDelay > 0) continue;
1409
- const p = t.position.x - h.position.x, d = t.position.y - h.position.y;
1410
- if (Math.hypot(p, d) <= t.getHitboxRadius() + h.radius) {
1411
- if (h.active = !1, t.takeDamage()) {
1412
- n.emit("onCoreDamaged", { currentIntegrity: t.integrity }), i.push({
1452
+ const E = {
1453
+ piercer: "#ff2a5f",
1454
+ spore: "#39ff14",
1455
+ cluster: "#ffd700",
1456
+ parasite: "#9d4edd",
1457
+ heavy: "#c8d0d8",
1458
+ micro: "#ffd700",
1459
+ core: "#00f0ff",
1460
+ coreGlow: "rgba(0, 240, 255, 0.85)",
1461
+ coreAccent: "#ff00dd",
1462
+ experience: "#ffffff",
1463
+ experienceBlue: "#38bdf8",
1464
+ experienceYellow: "#fbbf24",
1465
+ experienceRed: "#ef4444"
1466
+ };
1467
+ class ht {
1468
+ static update(t, e, i, s, a, n, o, r, c, l, h) {
1469
+ for (const u of e) {
1470
+ if (!u.active || u.isGrabbed || u.isDying || u.launchDelay > 0 || this.tryAutoDeflectFragment(t, u, i, s, n, h)) continue;
1471
+ const d = t.position.x - u.position.x, g = t.position.y - u.position.y;
1472
+ if (Math.hypot(d, g) <= t.getHitboxRadius() + u.radius) {
1473
+ if (u.active = !1, t.takeDamage()) {
1474
+ o.emit("onCoreDamaged", { currentIntegrity: t.integrity }), i.push({
1413
1475
  id: Math.random().toString(),
1414
1476
  position: { ...t.position },
1415
1477
  radius: 20,
1416
- maxRadius: Math.max(r, c) * 1.3,
1417
- color: "#00f0ff",
1478
+ maxRadius: Math.max(c, l) * 1.3,
1479
+ color: E.core,
1418
1480
  alpha: 1,
1419
1481
  lifetime: 0,
1420
1482
  maxLifetime: 0.8
1421
- }), l.triggerScreenShake({ x: p, y: d }, 25), l.triggerHitstop(0.15), l.triggerFlash(0.6);
1422
- for (const u of e)
1423
- if (u.active && !u.isGrabbed && u.launchDelay <= 0) {
1424
- u.triggerDissolveDeath();
1425
- for (let g = 0; g < 8; g++) {
1426
- const f = Math.random() * Math.PI * 2, b = 100 + Math.random() * 200;
1483
+ }), n.triggerScreenShake({ x: d, y: g }, 25), n.triggerHitstop(0.15), n.triggerFlash(0.6);
1484
+ for (const _ of e)
1485
+ if (_.active && !_.isGrabbed && _.launchDelay <= 0) {
1486
+ _.triggerDissolveDeath();
1487
+ for (let x = 0; x < 8; x++) {
1488
+ const y = Math.random() * Math.PI * 2, m = 100 + Math.random() * 200;
1427
1489
  s.push({
1428
1490
  id: Math.random().toString(),
1429
- position: { ...u.position },
1430
- velocity: { x: Math.cos(f) * b, y: Math.sin(f) * b },
1431
- color: "#00f0ff",
1491
+ position: { ..._.position },
1492
+ velocity: { x: Math.cos(y) * m, y: Math.sin(y) * m },
1493
+ color: E.core,
1432
1494
  radius: 3 + Math.random() * 3,
1433
1495
  alpha: 1,
1434
1496
  lifetime: 0,
@@ -1441,697 +1503,405 @@ class q {
1441
1503
  text: "CORE OVERLOAD PULSE!",
1442
1504
  position: { ...t.position },
1443
1505
  velocity: { x: 0, y: -90 },
1444
- color: "#00f0ff",
1506
+ color: E.core,
1445
1507
  alpha: 1,
1446
1508
  lifetime: 0,
1447
1509
  maxLifetime: 1.5,
1448
1510
  size: 32
1449
- }), t.integrity <= 0 && n.emit("onGameOver", { score: o.value, wave: 1 });
1511
+ }), t.integrity <= 0 && o.emit("onGameOver", { score: r.value, wave: 1 });
1450
1512
  }
1451
1513
  break;
1452
1514
  }
1453
1515
  }
1454
1516
  }
1455
- }
1456
- class Q {
1457
- execute(t) {
1458
- const { thrownEnemy: e, targetEnemy: i, shockwaves: s, particles: a, floatingTexts: l } = t;
1459
- i.experienceDropPending = !0, i.active = !1, s.push({
1517
+ static tryAutoDeflectFragment(t, e, i, s, a, n) {
1518
+ const o = n.core;
1519
+ if (!o.autoDeflectFragments || !e.behavior.autoDeflectFromCore || e.psionicDeflectionCooldown > 0)
1520
+ return !1;
1521
+ const r = t.position.x - e.position.x, c = t.position.y - e.position.y, l = Math.hypot(r, c);
1522
+ if (l <= 0) return !1;
1523
+ const h = Math.hypot(e.velocity.x, e.velocity.y), u = (e.velocity.x * r + e.velocity.y * c) / l;
1524
+ if (h <= 0 || u <= 0) return !1;
1525
+ const d = t.getHitboxRadius() + e.radius;
1526
+ if (Math.max(0, l - d) / u > o.fragmentDeflectionLookahead) return !1;
1527
+ const f = Math.random() * Math.PI * 2, _ = Math.max(o.fragmentDeflectionSpeed, h * 1.08);
1528
+ e.velocity = {
1529
+ x: Math.cos(f) * _,
1530
+ y: Math.sin(f) * _
1531
+ }, e.rotation = f, e.psionicDeflectionCooldown = o.fragmentDeflectionCooldown, e.psionicDeflectionTimer = 0.24, i.push({
1460
1532
  id: Math.random().toString(),
1461
- position: { ...i.position },
1462
- radius: 10,
1463
- maxRadius: 180,
1464
- color: "#ff2a5f",
1465
- alpha: 1,
1533
+ position: { ...e.position },
1534
+ radius: 4,
1535
+ maxRadius: 52,
1536
+ color: E.coreAccent,
1537
+ alpha: 0.9,
1466
1538
  lifetime: 0,
1467
- maxLifetime: 0.5
1539
+ maxLifetime: 0.24
1468
1540
  });
1469
- for (let n = 0; n < 30; n++) {
1470
- const o = Math.random() * Math.PI * 2, r = 180 + Math.random() * 320;
1471
- a.push({
1541
+ for (let x = 0; x < 6; x++) {
1542
+ const y = f + Math.PI + (Math.random() - 0.5) * 1.2, m = 120 + Math.random() * 180;
1543
+ s.push({
1472
1544
  id: Math.random().toString(),
1473
- position: { ...i.position },
1474
- velocity: { x: Math.cos(o) * r, y: Math.sin(o) * r },
1475
- color: "#ff2a5f",
1476
- radius: 3 + Math.random() * 4,
1545
+ position: { ...e.position },
1546
+ velocity: { x: Math.cos(y) * m, y: Math.sin(y) * m },
1547
+ color: E.coreAccent,
1548
+ radius: 2 + Math.random() * 2,
1477
1549
  alpha: 1,
1478
1550
  lifetime: 0,
1479
- maxLifetime: 0.5
1551
+ maxLifetime: 0.3
1480
1552
  });
1481
1553
  }
1482
- l.push({
1483
- id: Math.random().toString(),
1484
- text: "PIERCING SLICE",
1485
- position: { ...i.position },
1486
- velocity: { x: 0, y: -50 },
1487
- color: "#ff2a5f",
1488
- alpha: 1,
1489
- lifetime: 0,
1490
- maxLifetime: 0.8,
1491
- size: 20
1492
- });
1554
+ return a.triggerFlash(0.12), !0;
1493
1555
  }
1494
1556
  }
1495
- class tt {
1496
- execute(t) {
1497
- const { thrownEnemy: e, pools: i, shockwaves: s, particles: a, floatingTexts: l, eventBus: n } = t, o = e.position, r = A.getInstance().getConfig();
1498
- i.push({
1499
- id: Math.random().toString(),
1500
- position: { ...o },
1501
- radius: Math.round(r.toxicPool.baseRadius * 2),
1502
- duration: 0,
1503
- maxDuration: r.toxicPool.baseDuration * 1.5,
1504
- damage: r.toxicPool.damage
1505
- }), s.push({
1506
- id: Math.random().toString(),
1507
- position: { ...o },
1508
- radius: 10,
1509
- maxRadius: Math.round(r.toxicPool.baseRadius * 2.2),
1510
- color: "#39ff14",
1511
- alpha: 1,
1512
- lifetime: 0,
1513
- maxLifetime: 0.6
1514
- });
1515
- for (let c = 0; c < 45; c++) {
1516
- const h = Math.random() * Math.PI * 2, p = 120 + Math.random() * 260;
1517
- a.push({
1518
- id: Math.random().toString(),
1519
- position: { ...o },
1520
- velocity: { x: Math.cos(h) * p, y: Math.sin(h) * p },
1521
- color: "#39ff14",
1522
- radius: 4 + Math.random() * 5,
1523
- alpha: 1,
1524
- lifetime: 0,
1525
- maxLifetime: 0.9
1557
+ class ut {
1558
+ static update(t, e, i, s, a, n, o, r, c, l, h, u, d, g, b, f) {
1559
+ const _ = (m, v) => {
1560
+ V.killEnemy(m, v, {
1561
+ enemies: t,
1562
+ pools: e,
1563
+ vortices: i,
1564
+ shockwaves: s,
1565
+ particles: a,
1566
+ floatingTexts: n,
1567
+ eventBus: r,
1568
+ juice: o,
1569
+ spatialHash: d,
1570
+ factory: g,
1571
+ config: b,
1572
+ width: l,
1573
+ height: h,
1574
+ currentThrowState: u,
1575
+ onKill: f ?? (() => {
1576
+ }),
1577
+ killEnemy: _
1526
1578
  });
1527
- }
1528
- l.push({
1579
+ }, x = () => ({
1580
+ enemies: t,
1581
+ pools: e,
1582
+ vortices: i,
1583
+ shockwaves: s,
1584
+ particles: a,
1585
+ floatingTexts: n,
1586
+ eventBus: r,
1587
+ juice: o,
1588
+ spatialHash: d,
1589
+ factory: g,
1590
+ config: b,
1591
+ width: l,
1592
+ height: h,
1593
+ currentThrowState: u,
1594
+ onKill: f ?? (() => {
1595
+ }),
1596
+ killEnemy: _
1597
+ });
1598
+ let y = !1;
1599
+ for (const m of t.slice())
1600
+ if (!(!m.active || !m.isThrown || m.isDying || m.launchDelay > 0)) {
1601
+ if (y = !0, m.position.x < -120 || m.position.x > l + 120 || m.position.y < -120 || m.position.y > h + 120) {
1602
+ m.behavior.onOutOfBounds?.(m, x()), m.active = !1, m.isThrown = !1;
1603
+ continue;
1604
+ }
1605
+ for (const v of d.getNearby(m.position, m.getHitRadius() + 80)) {
1606
+ if (v === m || !v.active || v.isGrabbed || v.isDying || v.launchDelay > 0 || v.isStationaryShield) continue;
1607
+ const A = v.position.x - m.position.x, S = v.position.y - m.position.y, L = m.getHitRadius() + v.getHitRadius();
1608
+ if (A * A + S * S > L * L) continue;
1609
+ u.pos = { x: m.position.x, y: m.position.y };
1610
+ const T = Math.atan2(m.velocity.y, m.velocity.x), I = { ...x(), thrownEnemy: m, targetEnemy: v, impactAngle: T };
1611
+ if (m.behavior.onCollision?.(I) ?? !1)
1612
+ break;
1613
+ if (m.strategy) {
1614
+ m.strategy.execute({
1615
+ thrownEnemy: m,
1616
+ targetEnemy: v,
1617
+ allEnemies: t,
1618
+ pools: e,
1619
+ vortices: i,
1620
+ shockwaves: s,
1621
+ particles: a,
1622
+ floatingTexts: n,
1623
+ eventBus: r,
1624
+ width: l,
1625
+ height: h,
1626
+ factory: g,
1627
+ config: b
1628
+ }), m.active && !m.isDying && m.triggerDissolveDeath(!0), v.active && !v.isDying && v.triggerDissolveDeath(!0), u.kills += 2, f?.();
1629
+ break;
1630
+ }
1631
+ const $ = m.synergyKey === v.synergyKey, Z = $ ? 360 : 250;
1632
+ s.push({
1633
+ id: Math.random().toString(),
1634
+ position: { ...u.pos },
1635
+ radius: 15,
1636
+ maxRadius: Z,
1637
+ color: m.color,
1638
+ alpha: 1,
1639
+ lifetime: 0,
1640
+ maxLifetime: 0.55
1641
+ });
1642
+ for (let C = 0; C < 45; C++) {
1643
+ const H = Math.random() * Math.PI * 2, z = 200 + Math.random() * 450;
1644
+ a.push({
1645
+ id: Math.random().toString(),
1646
+ position: { ...u.pos },
1647
+ velocity: { x: Math.cos(H) * z, y: Math.sin(H) * z },
1648
+ color: m.color,
1649
+ radius: 4 + Math.random() * 6,
1650
+ alpha: 1,
1651
+ lifetime: 0,
1652
+ maxLifetime: 0.7
1653
+ });
1654
+ }
1655
+ $ ? (u.kills += 2, o.triggerHitstop(0.14), o.triggerFlash(0.5), o.triggerScreenShake({ x: A, y: S }, 16), _(m, T), _(v, T)) : (u.kills += 2, o.triggerHitstop(0.08), o.triggerFlash(0.35), o.triggerScreenShake({ x: A, y: S }, 10), _(m, T), _(v, T));
1656
+ for (const C of t.slice()) {
1657
+ if (C === m || C === v || !C.active || C.isGrabbed || C.isDying) continue;
1658
+ Math.hypot(C.position.x - u.pos.x, C.position.y - u.pos.y) <= Z && C.behavior.canBeChainReactionTarget && (_(C, T), u.kills++);
1659
+ }
1660
+ f?.();
1661
+ break;
1662
+ }
1663
+ }
1664
+ c.value = u.kills, !y && u.kills > 0 && (u.kills >= 2 && u.pos && n.push({
1529
1665
  id: Math.random().toString(),
1530
- text: "MASSIVE SPORE NEBULA",
1531
- position: { ...o },
1532
- velocity: { x: 0, y: -60 },
1533
- color: "#39ff14",
1666
+ text: `MULTIKILL x${u.kills}!`,
1667
+ position: { ...u.pos },
1668
+ velocity: { x: 0, y: -90 },
1669
+ color: E.coreAccent,
1534
1670
  alpha: 1,
1535
1671
  lifetime: 0,
1536
- maxLifetime: 1.2,
1537
- size: 26
1538
- }), e.experienceDropPending = !0, e.active = !1, n.emit("onPoolCreated", { position: o });
1672
+ maxLifetime: 1.4,
1673
+ size: 32
1674
+ }), r.emit("onComboUpdated", { combo: c.value }), u.kills = 0, u.pos = null);
1539
1675
  }
1540
1676
  }
1541
- class et {
1542
- execute(t) {
1543
- const { thrownEnemy: e, targetEnemy: i, allEnemies: s, shockwaves: a, particles: l, floatingTexts: n } = t, o = i.position;
1544
- e.experienceDropPending = !0, i.experienceDropPending = !0, e.active = !1, i.active = !1, a.push({
1545
- id: Math.random().toString(),
1546
- position: { ...o },
1547
- radius: 15,
1548
- maxRadius: 280,
1549
- color: "#ffd700",
1550
- alpha: 1,
1551
- lifetime: 0,
1552
- maxLifetime: 0.6
1553
- });
1554
- const r = A.getInstance().getConfig(), c = r.cluster.fragmentCount, h = r.cluster.hasHoming;
1555
- for (let p = 0; p < c; p++) {
1556
- const d = p * Math.PI * 2 / c, _ = 800 + Math.random() * 300, m = {
1557
- x: o.x + Math.cos(d) * 45,
1558
- y: o.y + Math.sin(d) * 45
1559
- };
1560
- let u = d;
1677
+ class pt {
1678
+ static update(t, e, i, s, a, n, o, r, c) {
1679
+ for (let l = t.length - 1; l >= 0; l--) {
1680
+ const h = t[l];
1561
1681
  if (h) {
1562
- let f = null, b = 1 / 0;
1563
- for (const y of s) {
1564
- if (y === e || y === i || !y.active || y.isGrabbed || y.isDying) continue;
1565
- const v = Math.hypot(y.position.x - m.x, y.position.y - m.y);
1566
- v < b && (b = v, f = y);
1682
+ h.duration += n;
1683
+ for (const u of e.slice()) {
1684
+ if (!u.active || u.isGrabbed || u.isDying || u.launchDelay > 0 || u.isThrown && u.behavior.canInteractWithHazard?.(u, "toxic-pool") === !1) continue;
1685
+ const d = u.position.x - h.position.x, g = u.position.y - h.position.y;
1686
+ d * d + g * g <= h.radius * h.radius && (V.killEnemy(u, 0, r), o.kills++, c && c());
1567
1687
  }
1568
- f && (u = Math.atan2(f.position.y - m.y, f.position.x - m.x));
1688
+ h.duration >= h.maxDuration && t.splice(l, 1);
1569
1689
  }
1570
- const g = C.createEnemy("micro", m, {
1571
- x: m.x + Math.cos(u) * 500,
1572
- y: m.y + Math.sin(u) * 500
1573
- });
1574
- g.isThrown = !0, g.wasManipulated = !0, g.velocity = {
1575
- x: Math.cos(u) * _,
1576
- y: Math.sin(u) * _
1577
- }, s.push(g);
1578
1690
  }
1579
- for (let p = 0; p < 35; p++) {
1580
- const d = Math.random() * Math.PI * 2, _ = 200 + Math.random() * 300;
1581
- l.push({
1582
- id: Math.random().toString(),
1583
- position: { ...o },
1584
- velocity: { x: Math.cos(d) * _, y: Math.sin(d) * _ },
1585
- color: "#ffd700",
1586
- radius: 4 + Math.random() * 4,
1587
- alpha: 1,
1588
- lifetime: 0,
1589
- maxLifetime: 0.6
1590
- });
1591
- }
1592
- n.push({
1593
- id: Math.random().toString(),
1594
- text: "CLUSTER DETONATION",
1595
- position: { ...o },
1596
- velocity: { x: 0, y: -60 },
1597
- color: "#ffd700",
1598
- alpha: 1,
1599
- lifetime: 0,
1600
- maxLifetime: 1,
1601
- size: 24
1602
- });
1603
1691
  }
1604
1692
  }
1605
- class it {
1606
- execute(t) {
1607
- const { thrownEnemy: e, targetEnemy: i, vortices: s, shockwaves: a } = t, l = A.getInstance().getConfig(), n = {
1608
- x: (e.position.x + i.position.x) / 2,
1609
- y: (e.position.y + i.position.y) / 2
1610
- };
1611
- s.push({
1612
- id: Math.random().toString(),
1613
- position: n,
1614
- radius: Math.round(l.gravityVortex.baseRadius * 2.2),
1615
- duration: 0,
1616
- maxDuration: l.gravityVortex.baseDuration * 1.5,
1617
- pullForce: l.gravityVortex.basePullForce * 1.6
1618
- }), a.push({
1619
- id: Math.random().toString(),
1620
- position: n,
1621
- radius: 20,
1622
- maxRadius: Math.round(l.gravityVortex.baseRadius * 2.2),
1623
- color: "#9d4edd",
1624
- alpha: 1,
1625
- lifetime: 0,
1626
- maxLifetime: 0.65
1627
- }), e.triggerDissolveDeath(!0), i.triggerDissolveDeath(!0);
1693
+ class dt {
1694
+ static update(t, e, i, s, a, n, o, r, c, l, h, u, d, g, b) {
1695
+ const f = g.shield.protectionArc;
1696
+ for (const _ of t.slice())
1697
+ if (!(!_.active || !_.isStationaryShield || _.isDying))
1698
+ for (const x of u.getNearby(_.position, _.getHitRadius() + 80)) {
1699
+ if (x === _ || !x.active || x.isGrabbed || x.isDying || x.launchDelay > 0) continue;
1700
+ const y = x.position.x - _.position.x, m = x.position.y - _.position.y;
1701
+ let v = Math.atan2(m, y) - _.rotation;
1702
+ for (; v < -Math.PI; ) v += Math.PI * 2;
1703
+ for (; v > Math.PI; ) v -= Math.PI * 2;
1704
+ if (Math.abs(v) > f / 2) continue;
1705
+ const A = _.getHitRadius() + x.getHitRadius();
1706
+ if (y * y + m * m <= A * A) {
1707
+ _.isStationaryShield = !1, _.strategy && _.strategy.execute({
1708
+ thrownEnemy: _,
1709
+ targetEnemy: x,
1710
+ allEnemies: t,
1711
+ pools: e,
1712
+ vortices: i,
1713
+ shockwaves: s,
1714
+ particles: a,
1715
+ floatingTexts: n,
1716
+ eventBus: r,
1717
+ width: l,
1718
+ height: h,
1719
+ factory: d,
1720
+ config: g
1721
+ }), c.kills += 2, o.triggerHitstop(0.12), o.triggerScreenShake({ x: y, y: m }, 18), b && b();
1722
+ break;
1723
+ }
1724
+ }
1628
1725
  }
1629
1726
  }
1630
- class st {
1631
- execute(t) {
1632
- const { thrownEnemy: e, targetEnemy: i, allEnemies: s, shockwaves: a, particles: l, floatingTexts: n } = t, o = i.position.x - e.position.x, r = i.position.y - e.position.y, c = Math.atan2(r, o), h = A.getInstance().getConfig().shield, p = h.blastSpeed || 1400, d = h.isFanBlast ? [0, -h.fanBlastAngle, h.fanBlastAngle] : [0];
1633
- for (const [_, m] of d.entries()) {
1634
- const u = c + m, g = _ === 0 ? e : C.createEnemy(
1635
- "heavy",
1636
- {
1637
- x: e.position.x + Math.cos(u) * 30,
1638
- y: e.position.y + Math.sin(u) * 30
1639
- },
1640
- { x: 0, y: 0 }
1641
- );
1642
- g.isStationaryShield = !1, g.isThrown = !0, g.wasManipulated = !0, g.shieldAngle = u, g.rotation = u, g.launchDelay = h.isFanBlast ? _ * h.fanBlastInterval : 0, g.velocity = {
1643
- x: Math.cos(u) * p,
1644
- y: Math.sin(u) * p
1645
- }, g !== e && s.push(g);
1646
- }
1647
- i.active = !1, i.triggerDissolveDeath(!0), n.push({
1648
- id: Math.random().toString(),
1649
- text: "ARC BLAST!",
1650
- position: { ...e.position },
1651
- velocity: { x: 0, y: -50 },
1652
- color: "#00f5ff",
1653
- alpha: 1,
1654
- lifetime: 0,
1655
- maxLifetime: 0.9,
1656
- size: 24
1727
+ class R {
1728
+ static merge(t, e, i, s) {
1729
+ R.mergeToxicPools(t, s.toxicPoolMergePadding), R.mergeVortices(e, s.gravityVortexMergePadding), R.mergeShockwaves(i, s.shockwaveMergeDistance);
1730
+ }
1731
+ static mergeToxicPools(t, e) {
1732
+ R.mergeNearby(t, (i, s) => R.distance(i.position.x, i.position.y, s.position.x, s.position.y) <= i.radius + s.radius + e, (i, s) => {
1733
+ const a = Math.max(0, i.maxDuration - i.duration), n = Math.max(0, s.maxDuration - s.duration);
1734
+ R.mergePositionByArea(i, s), i.radius = Math.hypot(i.radius, s.radius), i.duration = 0, i.maxDuration = Math.max(a, n), i.damage += s.damage, i.sourceCount = R.sourceCount(i) + R.sourceCount(s);
1657
1735
  });
1658
- for (let _ = 0; _ < 50; _++) {
1659
- const m = (Math.random() - 0.5) * 1.5, u = c + m, g = 300 + Math.random() * 500;
1660
- l.push({
1661
- id: Math.random().toString(),
1662
- position: { ...e.position },
1663
- velocity: { x: Math.cos(u) * g, y: Math.sin(u) * g },
1664
- color: "#00f5ff",
1665
- radius: 4 + Math.random() * 6,
1666
- alpha: 1,
1667
- lifetime: 0,
1668
- maxLifetime: 0.7
1669
- });
1670
- }
1671
- n.push({
1672
- id: Math.random().toString(),
1673
- text: "ARC BLAST!",
1674
- position: { ...e.position },
1675
- velocity: { x: 0, y: -50 },
1676
- color: "#00f5ff",
1677
- alpha: 1,
1678
- lifetime: 0,
1679
- maxLifetime: 0.9,
1680
- size: 24
1736
+ }
1737
+ static mergeVortices(t, e) {
1738
+ R.mergeNearby(t, (i, s) => R.distance(i.position.x, i.position.y, s.position.x, s.position.y) <= i.radius + s.radius + e, (i, s) => {
1739
+ const a = Math.max(0, i.maxDuration - i.duration), n = Math.max(0, s.maxDuration - s.duration);
1740
+ R.mergePositionByArea(i, s), i.radius = Math.hypot(i.radius, s.radius), i.duration = 0, i.maxDuration = Math.max(a, n), i.pullForce += s.pullForce, i.sourceCount = R.sourceCount(i) + R.sourceCount(s);
1681
1741
  });
1682
1742
  }
1683
- }
1684
- class C {
1685
- static createEnemy(t, e, i) {
1686
- const s = `enemy_${Date.now()}_${Math.random()}`, a = new U(s);
1687
- return this.configureEnemy(a, t, e, i), a;
1743
+ static mergeShockwaves(t, e) {
1744
+ R.mergeNearby(t, (i, s) => i.color !== s.color ? !1 : R.distance(i.position.x, i.position.y, s.position.x, s.position.y) <= e, (i, s) => {
1745
+ const a = i.maxRadius > 0 ? i.radius / i.maxRadius : 0, n = s.maxRadius > 0 ? s.radius / s.maxRadius : 0, o = R.sourceCount(i), r = R.sourceCount(s);
1746
+ i.position = {
1747
+ x: (i.position.x * o + s.position.x * r) / (o + r),
1748
+ y: (i.position.y * o + s.position.y * r) / (o + r)
1749
+ }, i.maxRadius = Math.hypot(i.maxRadius, s.maxRadius), i.radius = i.maxRadius * Math.max(a, n), i.lifetime = Math.max(i.lifetime, s.lifetime), i.maxLifetime = Math.max(i.maxLifetime, s.maxLifetime), i.alpha = Math.min(1, Math.max(i.alpha, s.alpha) + 0.08), i.sourceCount = o + r;
1750
+ });
1688
1751
  }
1689
- static configureEnemy(t, e, i, s) {
1690
- t.type = e, t.position = { ...i }, t.active = !0, t.isGrabbed = !1, t.isThrown = !1;
1691
- const a = s.x - i.x, l = s.y - i.y, n = Math.atan2(l, a);
1692
- let o = 70, r = 100, c = 20, h = "#ff2a5f", p = null;
1693
- e === "piercer" ? (o = 95, r = 140, c = 18, h = "#ff2a5f", p = new Q()) : e === "spore" ? (o = 65, r = 90, c = 24, h = "#39ff14", p = new tt()) : e === "cluster" ? (o = 50, r = 75, c = 26, h = "#ffd700", p = new et()) : e === "parasite" ? (o = 80, r = 110, c = 22, h = "#9d4edd", p = new it()) : e === "heavy" ? (o = 35, r = 50, c = 34, h = "#00f5ff", p = new st(), t.rotation = Math.random() * Math.PI * 2) : e === "micro" && (o = 130, r = 160, c = 12, h = "#ffd700"), t.radius = c, t.color = h, t.strategy = p, t.maxSpeed = r, t.velocity = {
1694
- x: Math.cos(n) * o,
1695
- y: Math.sin(n) * o
1752
+ static mergeNearby(t, e, i) {
1753
+ for (let s = 0; s < t.length; s++) {
1754
+ const a = t[s];
1755
+ if (a)
1756
+ for (let n = t.length - 1; n > s; n--) {
1757
+ const o = t[n];
1758
+ !o || !e(a, o) || (i(a, o), t.splice(n, 1));
1759
+ }
1760
+ }
1761
+ }
1762
+ static mergePositionByArea(t, e) {
1763
+ const i = Math.max(1, t.radius * t.radius), s = Math.max(1, e.radius * e.radius), a = i + s;
1764
+ t.position = {
1765
+ x: (t.position.x * i + e.position.x * s) / a,
1766
+ y: (t.position.y * i + e.position.y * s) / a
1696
1767
  };
1697
1768
  }
1698
- }
1699
- class P {
1700
- static killEnemy(t, e, i, s, a, l) {
1701
- if (!t.active || t.isDying) return;
1702
- const n = A.getInstance().getConfig();
1703
- if (t.experienceDropPending = !0, a.push({
1704
- id: Math.random().toString(),
1705
- position: { ...t.position },
1706
- radius: 10,
1707
- maxRadius: 180,
1708
- color: t.color,
1709
- alpha: 1,
1710
- lifetime: 0,
1711
- maxLifetime: 0.5
1712
- }), t.type === "spore")
1713
- t.triggerDissolveDeath(!0), i.push({
1714
- id: Math.random().toString(),
1715
- position: { ...t.position },
1716
- radius: n.toxicPool.baseRadius,
1717
- duration: 0,
1718
- maxDuration: n.toxicPool.baseDuration,
1719
- damage: n.toxicPool.damage
1720
- });
1721
- else if (t.type === "cluster") {
1722
- t.triggerDissolveDeath(!0);
1723
- const o = n.cluster.fragmentCount, r = n.cluster.hasHoming;
1724
- for (let c = 0; c < o; c++) {
1725
- const h = c * Math.PI * 2 / o, p = 750 + Math.random() * 250, d = {
1726
- x: t.position.x + Math.cos(h) * 40,
1727
- y: t.position.y + Math.sin(h) * 40
1728
- };
1729
- let _ = h;
1730
- if (r) {
1731
- let u = null, g = 1 / 0;
1732
- for (const f of l) {
1733
- if (f === t || !f.active || f.isGrabbed || f.isDying) continue;
1734
- const b = Math.hypot(f.position.x - d.x, f.position.y - d.y);
1735
- b < g && (g = b, u = f);
1736
- }
1737
- u && (_ = Math.atan2(u.position.y - d.y, u.position.x - d.x));
1738
- }
1739
- const m = C.createEnemy("micro", d, {
1740
- x: d.x + Math.cos(_) * 500,
1741
- y: d.y + Math.sin(_) * 500
1742
- });
1743
- m.isThrown = !0, m.wasManipulated = !0, m.velocity = {
1744
- x: Math.cos(_) * p,
1745
- y: Math.sin(_) * p
1746
- }, l.push(m);
1747
- }
1748
- } else t.type === "parasite" ? (t.triggerDissolveDeath(!0), s.push({
1749
- id: Math.random().toString(),
1750
- position: { ...t.position },
1751
- radius: n.gravityVortex.baseRadius,
1752
- duration: 0,
1753
- maxDuration: n.gravityVortex.baseDuration,
1754
- pullForce: n.gravityVortex.basePullForce
1755
- })) : t.type === "piercer" ? t.triggerSliceDeath(e, !0) : t.triggerDissolveDeath(!0);
1769
+ static sourceCount(t) {
1770
+ return Math.max(1, t.sourceCount ?? 1);
1771
+ }
1772
+ static distance(t, e, i, s) {
1773
+ return Math.hypot(i - t, s - e);
1756
1774
  }
1757
1775
  }
1758
- class at {
1759
- static update(t, e, i, s, a, l, n, o, r, c, h, p, d, _) {
1760
- let m = !1;
1761
- for (const u of t)
1762
- if (!(!u.active || !u.isThrown || u.isDying || u.launchDelay > 0)) {
1763
- if (m = !0, u.position.x < -120 || u.position.x > c + 120 || u.position.y < -120 || u.position.y > h + 120) {
1764
- if (u.type === "heavy") {
1765
- s.push({
1766
- id: Math.random().toString(),
1767
- position: { ...u.position },
1768
- radius: 20,
1769
- maxRadius: 350,
1770
- color: "#00f5ff",
1771
- alpha: 1,
1772
- lifetime: 0,
1773
- maxLifetime: 0.6
1774
- });
1775
- for (let g = 0; g < 40; g++) {
1776
- const f = Math.random() * Math.PI * 2, b = 250 + Math.random() * 450;
1777
- a.push({
1778
- id: Math.random().toString(),
1779
- position: { ...u.position },
1780
- velocity: { x: Math.cos(f) * b, y: Math.sin(f) * b },
1781
- color: "#00f5ff",
1782
- radius: 4 + Math.random() * 5,
1783
- alpha: 1,
1784
- lifetime: 0,
1785
- maxLifetime: 0.6
1786
- });
1787
- }
1788
- } else if (u.type === "piercer") {
1789
- const g = A.getInstance().getConfig().piercer;
1790
- if (g.terminalAoERadius > 0) {
1791
- const f = g.terminalAoERadius;
1792
- s.push({
1793
- id: Math.random().toString(),
1794
- position: { ...u.position },
1795
- radius: 20,
1796
- maxRadius: f,
1797
- color: "#ff2a5f",
1798
- alpha: 1,
1799
- lifetime: 0,
1800
- maxLifetime: 0.6
1801
- });
1802
- for (const b of d.getNearby(u.position, f)) {
1803
- if (!b.active || b.isGrabbed || b.isDying) continue;
1804
- const y = b.position.x - u.position.x, v = b.position.y - u.position.y;
1805
- y * y + v * v <= f * f && P.killEnemy(b, Math.atan2(v, y), e, i, s, t);
1806
- }
1807
- }
1776
+ class gt {
1777
+ constructor(t, e, i, s) {
1778
+ this.factory = i, this.configManager = s, this.spatialHash = new nt(100), this.eventBus = t, this.i18n = e;
1779
+ }
1780
+ spatialHash;
1781
+ eventBus;
1782
+ i18n;
1783
+ throwState = { kills: 0, pos: null };
1784
+ update(t, e, i, s, a, n, o, r, c, l, h, u, d, g) {
1785
+ const b = this.configManager.getConfig();
1786
+ let f;
1787
+ const _ = (y, m) => V.killEnemy(y, m, f);
1788
+ f = {
1789
+ enemies: i,
1790
+ pools: s,
1791
+ vortices: a,
1792
+ shockwaves: n,
1793
+ particles: o,
1794
+ floatingTexts: r,
1795
+ eventBus: this.eventBus,
1796
+ juice: c,
1797
+ spatialHash: this.spatialHash,
1798
+ factory: this.factory,
1799
+ config: b,
1800
+ width: u,
1801
+ height: d,
1802
+ currentThrowState: this.throwState,
1803
+ onKill: g ?? (() => {
1804
+ }),
1805
+ killEnemy: _
1806
+ };
1807
+ for (const y of i)
1808
+ if (y.active && !y.isDying) {
1809
+ if (y.behavior.onThrownUpdate?.(y, {
1810
+ dt: t,
1811
+ enemies: i,
1812
+ config: b
1813
+ }), y.isStationaryShield) {
1814
+ let m = null, v = 1 / 0;
1815
+ for (const A of i) {
1816
+ if (A === y || !A.active || A.isGrabbed || A.isDying || A.isStationaryShield) continue;
1817
+ const S = A.position.x - y.position.x, L = A.position.y - y.position.y, T = S * S + L * L;
1818
+ T < v && (v = T, m = A);
1819
+ }
1820
+ if (m) {
1821
+ let S = Math.atan2(m.position.y - y.position.y, m.position.x - y.position.x) - y.rotation;
1822
+ for (; S < -Math.PI; ) S += Math.PI * 2;
1823
+ for (; S > Math.PI; ) S -= Math.PI * 2;
1824
+ const L = 5 * t;
1825
+ y.rotation += Math.sign(S) * Math.min(Math.abs(S), L);
1808
1826
  }
1809
- u.active = !1, u.isThrown = !1;
1810
- continue;
1811
1827
  }
1812
- for (const g of d.getNearby(u.position, u.getHitRadius() + 80)) {
1813
- if (g === u || !g.active || g.isGrabbed || g.isDying || g.launchDelay > 0) continue;
1814
- const f = g.position.x - u.position.x, b = g.position.y - u.position.y, y = u.getHitRadius() + g.getHitRadius();
1815
- if (f * f + b * b <= y * y) {
1816
- p.pos = { x: u.position.x, y: u.position.y };
1817
- const v = Math.atan2(u.velocity.y, u.velocity.x);
1818
- if (u.type === "heavy") {
1819
- p.kills++, P.killEnemy(g, u.rotation, e, i, s, t), n.triggerHitstop(0.04), n.triggerScreenShake({ x: f, y: b }, 8);
1820
- for (let M = 0; M < 8; M++) {
1821
- const T = u.rotation + (Math.random() - 0.5), x = 200 + Math.random() * 350;
1822
- a.push({
1823
- id: Math.random().toString(),
1824
- position: { ...g.position },
1825
- velocity: { x: Math.cos(T) * x, y: Math.sin(T) * x },
1826
- color: "#00f5ff",
1827
- radius: 3 + Math.random() * 4,
1828
- alpha: 1,
1829
- lifetime: 0,
1830
- maxLifetime: 0.4
1831
- });
1832
- }
1833
- _ && _();
1834
- } else if (u.type === "piercer") {
1835
- p.kills++, P.killEnemy(g, v, e, i, s, t), n.triggerHitstop(0.05), n.triggerScreenShake({ x: f, y: b }, 7), s.push({
1836
- id: Math.random().toString(),
1837
- position: { ...g.position },
1838
- radius: 10,
1839
- maxRadius: 180,
1840
- color: "#ff2a5f",
1841
- alpha: 1,
1842
- lifetime: 0,
1843
- maxLifetime: 0.45
1844
- });
1845
- for (let M = 0; M < 16; M++) {
1846
- const T = Math.random() * Math.PI * 2, x = 150 + Math.random() * 300;
1847
- a.push({
1848
- id: Math.random().toString(),
1849
- position: { ...g.position },
1850
- velocity: { x: Math.cos(T) * x, y: Math.sin(T) * x },
1851
- color: "#ff2a5f",
1852
- radius: 3 + Math.random() * 3,
1853
- alpha: 1,
1854
- lifetime: 0,
1855
- maxLifetime: 0.4
1856
- });
1857
- }
1858
- if (u.remainingHomingKills > 0) {
1859
- u.remainingHomingKills--;
1860
- let M = null, T = 1 / 0;
1861
- for (const x of t) {
1862
- if (x === u || x === g || !x.active || x.isGrabbed || x.isDying) continue;
1863
- const L = x.position.x - u.position.x, R = x.position.y - u.position.y, I = Math.hypot(L, R);
1864
- I < T && (T = I, M = x);
1865
- }
1866
- if (M) {
1867
- const x = Math.atan2(M.position.y - u.position.y, M.position.x - u.position.x), L = Math.hypot(u.velocity.x, u.velocity.y) || 1200;
1868
- u.rotation = x, u.velocity = {
1869
- x: Math.cos(x) * L,
1870
- y: Math.sin(x) * L
1871
- };
1872
- }
1873
- }
1874
- _ && _();
1875
- } else {
1876
- const M = u.type === g.type, T = M ? 360 : 250;
1877
- s.push({
1878
- id: Math.random().toString(),
1879
- position: { ...p.pos },
1880
- radius: 15,
1881
- maxRadius: T,
1882
- color: u.color,
1883
- alpha: 1,
1884
- lifetime: 0,
1885
- maxLifetime: 0.55
1886
- });
1887
- for (let x = 0; x < 45; x++) {
1888
- const L = Math.random() * Math.PI * 2, R = 200 + Math.random() * 450;
1889
- a.push({
1890
- id: Math.random().toString(),
1891
- position: { ...p.pos },
1892
- velocity: { x: Math.cos(L) * R, y: Math.sin(L) * R },
1893
- color: u.color,
1894
- radius: 4 + Math.random() * 6,
1895
- alpha: 1,
1896
- lifetime: 0,
1897
- maxLifetime: 0.7
1898
- });
1899
- }
1900
- if (u.strategy) {
1901
- u.strategy.execute({
1902
- thrownEnemy: u,
1903
- targetEnemy: g,
1904
- allEnemies: t,
1905
- pools: e,
1906
- vortices: i,
1907
- shockwaves: s,
1908
- particles: a,
1909
- floatingTexts: l,
1910
- eventBus: o,
1911
- width: c,
1912
- height: h
1913
- }), p.kills += 2, _ && _();
1914
- break;
1915
- }
1916
- if (M) {
1917
- p.kills += 2, n.triggerHitstop(0.14), n.triggerFlash(0.5), n.triggerScreenShake({ x: f, y: b }, 16), l.push({
1918
- id: Math.random().toString(),
1919
- text: "COLOR SYNERGY!",
1920
- position: { ...p.pos },
1921
- velocity: { x: 0, y: -80 },
1922
- color: u.color,
1923
- alpha: 1,
1924
- lifetime: 0,
1925
- maxLifetime: 1.2,
1926
- size: 26
1927
- });
1928
- const x = A.getInstance().getConfig();
1929
- u.type === "spore" ? e.push({
1930
- id: Math.random().toString(),
1931
- position: { ...p.pos },
1932
- radius: Math.round(x.toxicPool.baseRadius * 2),
1933
- duration: 0,
1934
- maxDuration: x.toxicPool.synergyDuration,
1935
- damage: x.toxicPool.damage * 2
1936
- }) : u.type === "parasite" && i.push({
1937
- id: Math.random().toString(),
1938
- position: { ...p.pos },
1939
- radius: Math.round(x.gravityVortex.baseRadius * 1.8),
1940
- duration: 0,
1941
- maxDuration: x.gravityVortex.synergyDuration,
1942
- pullForce: x.gravityVortex.synergyPullForce
1943
- }), P.killEnemy(u, v, e, i, s, t), P.killEnemy(g, v, e, i, s, t);
1944
- } else
1945
- p.kills += 2, n.triggerHitstop(0.08), n.triggerFlash(0.35), n.triggerScreenShake({ x: f, y: b }, 10), P.killEnemy(u, v, e, i, s, t), P.killEnemy(g, v, e, i, s, t);
1946
- for (const x of t) {
1947
- if (x === u || x === g || !x.active || x.isGrabbed || x.isDying) continue;
1948
- const L = x.position.x - p.pos.x, R = x.position.y - p.pos.y;
1949
- Math.hypot(L, R) <= T && x.type !== "micro" && (P.killEnemy(x, v, e, i, s, t), p.kills++);
1950
- }
1951
- _ && _();
1952
- break;
1953
- }
1954
- }
1955
- }
1956
- }
1957
- r.value = p.kills, !m && p.kills > 0 && (p.kills >= 2 && p.pos && l.push({
1958
- id: Math.random().toString(),
1959
- text: `MULTIKILL x${p.kills}!`,
1960
- position: { ...p.pos },
1961
- velocity: { x: 0, y: -90 },
1962
- color: "#ff00dd",
1963
- alpha: 1,
1964
- lifetime: 0,
1965
- maxLifetime: 1.4,
1966
- size: 32
1967
- }), o.emit("onComboUpdated", { combo: r.value }), p.kills = 0, p.pos = null);
1968
- }
1969
- }
1970
- class ot {
1971
- static update(t, e, i, s, a, l, n, o) {
1972
- for (let r = t.length - 1; r >= 0; r--) {
1973
- const c = t[r];
1974
- if (c) {
1975
- c.duration += l;
1976
- for (const h of e) {
1977
- if (!h.active || h.isGrabbed || h.isDying || h.launchDelay > 0 || h.isThrown && h.type === "piercer") continue;
1978
- const p = h.position.x - c.position.x, d = h.position.y - c.position.y;
1979
- p * p + d * d <= c.radius * c.radius && (P.killEnemy(h, 0, t, i, s, e), n.kills++, o && o());
1980
- }
1981
- c.duration >= c.maxDuration && t.splice(r, 1);
1982
- }
1983
- }
1984
- }
1985
- }
1986
- class nt {
1987
- static update(t, e, i, s, a, l, n, o, r, c, h, p, d) {
1988
- const _ = A.getInstance().getConfig().shield.protectionArc;
1989
- for (const m of t)
1990
- if (!(!m.active || !m.isStationaryShield || m.isDying))
1991
- for (const u of p.getNearby(m.position, m.getHitRadius() + 80)) {
1992
- if (u === m || !u.active || u.isGrabbed || u.isDying || u.launchDelay > 0) continue;
1993
- const g = u.position.x - m.position.x, f = u.position.y - m.position.y;
1994
- let b = Math.atan2(f, g) - m.rotation;
1995
- for (; b < -Math.PI; ) b += Math.PI * 2;
1996
- for (; b > Math.PI; ) b -= Math.PI * 2;
1997
- if (Math.abs(b) > _ / 2) continue;
1998
- const y = m.getHitRadius() + u.getHitRadius();
1999
- if (g * g + f * f <= y * y) {
2000
- m.isStationaryShield = !1, m.strategy && m.strategy.execute({
2001
- thrownEnemy: m,
2002
- targetEnemy: u,
2003
- allEnemies: t,
2004
- pools: e,
2005
- vortices: i,
2006
- shockwaves: s,
2007
- particles: a,
2008
- floatingTexts: l,
2009
- eventBus: o,
2010
- width: c,
2011
- height: h
2012
- }), r.kills += 2, n.triggerHitstop(0.12), n.triggerScreenShake({ x: g, y: f }, 18), d && d();
2013
- break;
2014
- }
2015
- }
2016
- }
2017
- }
2018
- class rt {
2019
- spatialHash;
2020
- eventBus;
2021
- i18n;
2022
- throwState = { kills: 0, pos: null };
2023
- constructor(t, e) {
2024
- this.spatialHash = new z(100), this.eventBus = t, this.i18n = e;
2025
- }
2026
- update(t, e, i, s, a, l, n, o, r, c, h, p, d, _) {
2027
- const m = A.getInstance().getConfig().piercer;
2028
- for (const g of i)
2029
- if (g.active && !g.isDying) {
2030
- if (g.isThrown && g.type === "piercer" && m.hasHoming && g.remainingHomingKills > 0) {
2031
- let f = null, b = 1 / 0;
2032
- for (const y of i) {
2033
- if (y === g || !y.active || y.isGrabbed || y.isDying) continue;
2034
- const v = y.position.x - g.position.x, M = y.position.y - g.position.y, T = v * v + M * M;
2035
- T < b && (b = T, f = y);
2036
- }
2037
- if (f) {
2038
- const y = Math.atan2(f.position.y - g.position.y, f.position.x - g.position.x), v = Math.max(Math.hypot(g.velocity.x, g.velocity.y), 1e3), M = 25 * t, T = Math.atan2(g.velocity.y, g.velocity.x);
2039
- let x = y - T;
2040
- for (; x < -Math.PI; ) x += Math.PI * 2;
2041
- for (; x > Math.PI; ) x -= Math.PI * 2;
2042
- const L = T + Math.sign(x) * Math.min(Math.abs(x), M);
2043
- g.rotation = L, g.velocity = {
2044
- x: Math.cos(L) * v,
2045
- y: Math.sin(L) * v
2046
- };
2047
- }
2048
- }
2049
- if (g.isStationaryShield) {
2050
- let f = null, b = 1 / 0;
2051
- for (const y of i) {
2052
- if (y === g || !y.active || y.isGrabbed || y.isDying || y.isStationaryShield) continue;
2053
- const v = y.position.x - g.position.x, M = y.position.y - g.position.y, T = v * v + M * M;
2054
- T < b && (b = T, f = y);
2055
- }
2056
- if (f) {
2057
- let v = Math.atan2(f.position.y - g.position.y, f.position.x - g.position.x) - g.rotation;
2058
- for (; v < -Math.PI; ) v += Math.PI * 2;
2059
- for (; v > Math.PI; ) v -= Math.PI * 2;
2060
- const M = 5 * t;
2061
- g.rotation += Math.sign(v) * Math.min(Math.abs(v), M);
2062
- }
2063
- }
2064
- g.update(t);
2065
- } else g.active && g.isDying && g.update(t);
2066
- for (let g = l.length - 1; g >= 0; g--) {
2067
- const f = l[g];
2068
- if (!f) continue;
2069
- f.lifetime += t;
2070
- const b = f.lifetime / f.maxLifetime;
2071
- f.radius = f.maxRadius * b, f.alpha = Math.max(0, 1 - b), f.lifetime >= f.maxLifetime && l.splice(g, 1);
1828
+ y.update(t);
1829
+ } else y.active && y.isDying && y.update(t);
1830
+ for (let y = n.length - 1; y >= 0; y--) {
1831
+ const m = n[y];
1832
+ if (!m) continue;
1833
+ m.lifetime += t;
1834
+ const v = m.lifetime / m.maxLifetime;
1835
+ m.radius = m.maxRadius * v, m.alpha = Math.max(0, 1 - v), m.lifetime >= m.maxLifetime && n.splice(y, 1);
2072
1836
  }
2073
- j.update(a, i, t), this.spatialHash.clear();
2074
- for (const g of i)
2075
- g.active && !g.isDying && g.launchDelay <= 0 && this.spatialHash.insert(g);
2076
- q.update(
1837
+ lt.update(a, i, t, b), this.spatialHash.clear();
1838
+ for (const y of i)
1839
+ y.active && !y.isDying && y.launchDelay <= 0 && this.spatialHash.insert(y);
1840
+ ht.update(
2077
1841
  e,
2078
1842
  i,
2079
- l,
2080
1843
  n,
2081
1844
  o,
2082
1845
  r,
1846
+ c,
2083
1847
  this.eventBus,
2084
1848
  h,
2085
- p,
2086
- d
2087
- ), at.update(
1849
+ u,
1850
+ d,
1851
+ b
1852
+ ), ut.update(
2088
1853
  i,
2089
1854
  s,
2090
1855
  a,
2091
- l,
2092
1856
  n,
2093
1857
  o,
2094
1858
  r,
2095
- this.eventBus,
2096
1859
  c,
2097
- p,
1860
+ this.eventBus,
1861
+ l,
1862
+ u,
2098
1863
  d,
2099
1864
  this.throwState,
2100
1865
  this.spatialHash,
2101
- _
2102
- ), ot.update(
1866
+ this.factory,
1867
+ b,
1868
+ g
1869
+ ), pt.update(
2103
1870
  s,
2104
1871
  i,
2105
1872
  a,
2106
- l,
2107
1873
  n,
1874
+ o,
2108
1875
  t,
2109
1876
  this.throwState,
2110
- _
2111
- ), nt.update(
1877
+ f,
1878
+ g
1879
+ ), dt.update(
2112
1880
  i,
2113
1881
  s,
2114
1882
  a,
2115
- l,
2116
1883
  n,
2117
1884
  o,
2118
1885
  r,
1886
+ c,
2119
1887
  this.eventBus,
2120
1888
  this.throwState,
2121
- p,
1889
+ u,
2122
1890
  d,
2123
1891
  this.spatialHash,
2124
- _
2125
- );
2126
- const u = 32;
2127
- l.length > u && l.splice(0, l.length - u);
1892
+ this.factory,
1893
+ b,
1894
+ g
1895
+ ), R.merge(s, a, n, b.effectAggregation);
1896
+ const x = 32;
1897
+ n.length > x && n.splice(0, n.length - x);
2128
1898
  }
2129
1899
  }
2130
- class lt {
1900
+ class mt {
2131
1901
  pool = [];
2132
1902
  constructor(t = 500) {
2133
1903
  for (let e = 0; e < t; e++) {
2134
- const i = new U(`pool_enemy_${e}`);
1904
+ const i = new F(`pool_enemy_${e}`);
2135
1905
  this.pool.push(i);
2136
1906
  }
2137
1907
  }
@@ -2139,7 +1909,7 @@ class lt {
2139
1909
  for (const e of this.pool)
2140
1910
  if (!e.active)
2141
1911
  return e.reset(), e;
2142
- const t = new U(`fallback_enemy_${Date.now()}_${Math.random()}`);
1912
+ const t = new F(`fallback_enemy_${Date.now()}_${Math.random()}`);
2143
1913
  return this.pool.push(t), t;
2144
1914
  }
2145
1915
  resetAll() {
@@ -2147,185 +1917,295 @@ class lt {
2147
1917
  t.reset();
2148
1918
  }
2149
1919
  }
2150
- class O {
1920
+ class w {
1921
+ static algorithms = {
1922
+ circle: (t, e, i, s, a, n) => w.spawnCircleOfDeath(t, e, i, s, a, n),
1923
+ dense: (t, e, i, s, a, n) => w.spawnDenseCluster(t, e, i, a, n, s),
1924
+ sine: (t, e, i, s, a, n) => w.spawnSineSnake(t, e, i, a, n, s),
1925
+ spiral: (t, e, i, s, a, n) => w.spawnDoubleSpiral(t, e, i, a, n, s),
1926
+ marching: (t, e, i, s, a, n) => w.spawnMarchingWall(t, e, i, a, n, s),
1927
+ crossfire: (t, e, i, s, a, n) => w.spawnCrossfireStreams(t, e, i, a, n, s)
1928
+ };
1929
+ static spawn(t, e, i, s, a, n, o) {
1930
+ return this.algorithms[i]?.(t, e, s, a, n, o) ?? [];
1931
+ }
2151
1932
  static getOffscreenPosition(t, e) {
2152
1933
  const i = Math.floor(Math.random() * 4), s = 70;
2153
1934
  return i === 0 ? { x: Math.random() * t, y: -s } : i === 1 ? { x: t + s, y: Math.random() * e } : i === 2 ? { x: Math.random() * t, y: e + s } : { x: -s, y: Math.random() * e };
2154
1935
  }
2155
- static spawnCircleOfDeath(t, e, i, s) {
2156
- const a = [], n = Math.max(i, s) * 0.55 + 80;
2157
- for (let o = 0; o < 18; o++) {
2158
- const r = o * Math.PI * 2 / 18, c = {
2159
- x: e.x + Math.cos(r) * n,
2160
- y: e.y + Math.sin(r) * n
2161
- }, h = t.get(), p = ["piercer", "spore", "cluster", "parasite"], d = p[o % p.length];
2162
- C.configureEnemy(h, d, c, e), h.velocity = {
2163
- x: Math.cos(r + Math.PI) * 55,
2164
- y: Math.sin(r + Math.PI) * 55
2165
- }, a.push(h);
1936
+ static spawnCircleOfDeath(t, e, i, s, a, n) {
1937
+ const o = [], c = Math.max(a, n) * 0.55 + 80;
1938
+ for (let l = 0; l < 18; l++) {
1939
+ const h = l * Math.PI * 2 / 18, u = {
1940
+ x: s.x + Math.cos(h) * c,
1941
+ y: s.y + Math.sin(h) * c
1942
+ }, d = e.get(), g = i[l % i.length];
1943
+ t.configureEnemy(d, g, u, s), d.velocity = {
1944
+ x: Math.cos(h + Math.PI) * 55,
1945
+ y: Math.sin(h + Math.PI) * 55
1946
+ }, o.push(d);
2166
1947
  }
2167
- return a;
2168
- }
2169
- static spawnDenseCluster(t, e, i, s) {
2170
- const a = [], n = Math.floor(Math.random() * 4);
2171
- let o = { x: -60, y: -60 };
2172
- n === 0 ? o = { x: Math.random() * e, y: -80 } : n === 1 ? o = { x: e + 80, y: Math.random() * i } : n === 2 ? o = { x: Math.random() * e, y: i + 80 } : o = { x: -80, y: Math.random() * i };
2173
- for (let r = 0; r < 14; r++) {
2174
- const c = {
1948
+ return o;
1949
+ }
1950
+ static spawnDenseCluster(t, e, i, s, a, n) {
1951
+ const o = [], c = Math.floor(Math.random() * 4);
1952
+ let l = { x: -60, y: -60 };
1953
+ c === 0 ? l = { x: Math.random() * s, y: -80 } : c === 1 ? l = { x: s + 80, y: Math.random() * a } : c === 2 ? l = { x: Math.random() * s, y: a + 80 } : l = { x: -80, y: Math.random() * a };
1954
+ for (let h = 0; h < 14; h++) {
1955
+ const u = {
2175
1956
  x: (Math.random() - 0.5) * 60,
2176
1957
  y: (Math.random() - 0.5) * 60
2177
- }, h = { x: o.x + c.x, y: o.y + c.y }, p = t.get();
2178
- C.configureEnemy(p, "spore", h, s);
2179
- const d = s.x - h.x, _ = s.y - h.y, m = Math.atan2(_, d);
2180
- p.velocity = {
2181
- x: Math.cos(m) * (50 + Math.random() * 20),
2182
- y: Math.sin(m) * (50 + Math.random() * 20)
2183
- }, a.push(p);
1958
+ }, d = { x: l.x + u.x, y: l.y + u.y }, g = e.get();
1959
+ t.configureEnemy(g, i[0], d, n);
1960
+ const b = n.x - d.x, f = n.y - d.y, _ = Math.atan2(f, b);
1961
+ g.velocity = {
1962
+ x: Math.cos(_) * (50 + Math.random() * 20),
1963
+ y: Math.sin(_) * (50 + Math.random() * 20)
1964
+ }, o.push(g);
2184
1965
  }
2185
- return a;
2186
- }
2187
- static spawnSineSnake(t, e, i, s) {
2188
- const a = [], n = i * 0.2 + Math.random() * i * 0.6;
2189
- for (let o = 0; o < 12; o++) {
2190
- const r = {
2191
- x: -60 - o * 40,
2192
- y: n + Math.sin(o * 0.45) * 70
2193
- }, c = t.get();
2194
- C.configureEnemy(c, "cluster", r, s), c.velocity = { x: 75, y: Math.cos(o * 0.45) * 30 }, a.push(c);
1966
+ return o;
1967
+ }
1968
+ static spawnSineSnake(t, e, i, s, a, n) {
1969
+ const o = [], c = a * 0.2 + Math.random() * a * 0.6;
1970
+ for (let l = 0; l < 12; l++) {
1971
+ const h = {
1972
+ x: -60 - l * 40,
1973
+ y: c + Math.sin(l * 0.45) * 70
1974
+ }, u = e.get();
1975
+ t.configureEnemy(u, i[0], h, n), u.velocity = { x: 75, y: Math.cos(l * 0.45) * 30 }, o.push(u);
2195
1976
  }
2196
- return a;
2197
- }
2198
- static spawnDoubleSpiral(t, e, i, s) {
2199
- const a = [];
2200
- for (let n = 0; n < 14; n++) {
2201
- const o = n / 14, r = o * Math.PI * 4, c = 120 + o * 450, h = {
2202
- x: e + Math.cos(r) * c,
2203
- y: -60 + Math.sin(r) * c
2204
- }, p = t.get();
2205
- C.configureEnemy(p, "parasite", h, s), a.push(p);
1977
+ return o;
1978
+ }
1979
+ static spawnDoubleSpiral(t, e, i, s, a, n) {
1980
+ const o = [];
1981
+ for (let c = 0; c < 14; c++) {
1982
+ const l = c / 14, h = l * Math.PI * 4, u = 120 + l * 450, d = {
1983
+ x: s + Math.cos(h) * u,
1984
+ y: -60 + Math.sin(h) * u
1985
+ }, g = e.get();
1986
+ t.configureEnemy(g, i[0], d, n), o.push(g);
2206
1987
  }
2207
- return a;
2208
- }
2209
- static spawnMarchingWall(t, e, i, s) {
2210
- const a = [], n = Math.random() > 0.5;
2211
- for (let o = 0; o < 12; o++) {
2212
- const r = o / 11 * i, c = {
2213
- x: n ? -60 : e + 60,
2214
- y: r
2215
- }, h = t.get();
2216
- C.configureEnemy(h, "piercer", c, s), h.velocity = {
2217
- x: n ? 70 : -70,
1988
+ return o;
1989
+ }
1990
+ static spawnMarchingWall(t, e, i, s, a, n) {
1991
+ const o = [], c = Math.random() > 0.5;
1992
+ for (let l = 0; l < 12; l++) {
1993
+ const h = l / 11 * a, u = {
1994
+ x: c ? -60 : s + 60,
1995
+ y: h
1996
+ }, d = e.get();
1997
+ t.configureEnemy(d, i[0], u, n), d.velocity = {
1998
+ x: c ? 70 : -70,
2218
1999
  y: 0
2219
- }, a.push(h);
2000
+ }, o.push(d);
2220
2001
  }
2221
- return a;
2222
- }
2223
- static spawnCrossfireStreams(t, e, i, s) {
2224
- const a = [];
2225
- for (let n = 0; n < 10; n++) {
2226
- const o = { x: -50 - n * 35, y: -50 - n * 35 }, r = t.get();
2227
- C.configureEnemy(r, "spore", o, s), a.push(r);
2228
- const c = { x: e + 50 + n * 35, y: i + 50 + n * 35 }, h = t.get();
2229
- C.configureEnemy(h, "cluster", c, s), a.push(h);
2002
+ return o;
2003
+ }
2004
+ static spawnCrossfireStreams(t, e, i, s, a, n) {
2005
+ const o = [];
2006
+ for (let c = 0; c < 10; c++) {
2007
+ const l = { x: -50 - c * 35, y: -50 - c * 35 }, h = e.get();
2008
+ t.configureEnemy(h, i[0], l, n), o.push(h);
2009
+ const u = { x: s + 50 + c * 35, y: a + 50 + c * 35 }, d = e.get();
2010
+ t.configureEnemy(d, i[1 % i.length], u, n), o.push(d);
2230
2011
  }
2231
- return a;
2012
+ return o;
2232
2013
  }
2233
2014
  }
2234
- class ct {
2015
+ class _t {
2016
+ constructor(t, e) {
2017
+ this.factory = t, this.content = e, this.pool = new mt(500);
2018
+ }
2235
2019
  wave = 1;
2236
2020
  pool;
2237
2021
  spawnTimer = 0;
2238
2022
  waveTimer = 0;
2239
2023
  patternCooldown = 0;
2240
2024
  initialGracePeriod = 6;
2241
- constructor() {
2242
- this.pool = new lt(500);
2243
- }
2244
2025
  reset() {
2245
2026
  this.wave = 1, this.spawnTimer = 0, this.waveTimer = 0, this.patternCooldown = 0;
2246
2027
  }
2247
2028
  update(t, e, i, s, a) {
2248
2029
  this.waveTimer += t, this.spawnTimer += t, this.patternCooldown += t, this.applyFlockingBehaviors(e, t, a);
2249
- const l = e.filter((r) => r.active).length, n = Math.max(0.9, 2.2 - (this.wave - 1) * 0.18);
2250
- if (this.waveTimer > this.initialGracePeriod && this.patternCooldown > Math.max(12 - this.wave, 6) && l < 35) {
2030
+ const n = e.filter((c) => c.active).length, o = Math.max(0.9, 2.2 - (this.wave - 1) * 0.18);
2031
+ if (this.waveTimer > this.initialGracePeriod && this.patternCooldown > Math.max(12 - this.wave, 6) && n < 35) {
2251
2032
  this.patternCooldown = 0;
2252
- const r = this.spawnPatternFormation(i, s, a);
2253
- for (const c of r)
2254
- e.push(c);
2033
+ const c = this.spawnPatternFormation(i, s, a);
2034
+ for (const l of c)
2035
+ e.push(l);
2255
2036
  }
2256
- const o = Math.min(18 + this.wave * 4, 80);
2257
- return this.spawnTimer >= n && l < o ? (this.spawnTimer = 0, this.spawnSingleEnemy(i, s, a)) : null;
2037
+ const r = Math.min(18 + this.wave * 4, 80);
2038
+ return this.spawnTimer >= o && n < r ? (this.spawnTimer = 0, this.spawnSingleEnemy(i, s, a)) : null;
2258
2039
  }
2259
2040
  spawnSingleEnemy(t, e, i) {
2260
- const s = O.getOffscreenPosition(t, e), a = Math.random();
2261
- let l = "piercer";
2262
- a > 0.75 ? l = "heavy" : a > 0.55 ? l = "parasite" : a > 0.35 ? l = "cluster" : a > 0.15 && (l = "spore");
2263
- const n = this.pool.get();
2264
- return C.configureEnemy(n, l, s, i), n;
2041
+ const s = w.getOffscreenPosition(t, e), a = this.content.enemies.values().filter((l) => (l.spawnWeight ?? 0) > 0), n = a.reduce((l, h) => l + (h.spawnWeight ?? 0), 0);
2042
+ let o = Math.random() * n, r = a[0]?.id ?? "";
2043
+ for (const l of a)
2044
+ if (o -= l.spawnWeight ?? 0, o <= 0) {
2045
+ r = l.id;
2046
+ break;
2047
+ }
2048
+ const c = this.pool.get();
2049
+ return this.factory.configureEnemy(c, r, s, i), c;
2265
2050
  }
2266
2051
  spawnPatternFormation(t, e, i) {
2267
- const s = Math.random();
2268
- return s < 0.25 ? O.spawnCircleOfDeath(this.pool, i, t, e) : s < 0.5 ? O.spawnDenseCluster(this.pool, t, e, i) : s < 0.75 ? O.spawnSineSnake(this.pool, t, e, i) : O.spawnMarchingWall(this.pool, t, e, i);
2052
+ const s = this.content.spawnerPatterns.values(), a = s.reduce((r, c) => r + c.weight, 0);
2053
+ let n = Math.random() * a;
2054
+ const o = s.find((r) => (n -= r.weight, n <= 0));
2055
+ return o ? w.spawn(this.factory, this.pool, o.algorithmId, o.enemyIds, i, t, e) : [];
2269
2056
  }
2270
2057
  applyFlockingBehaviors(t, e, i) {
2271
2058
  for (let a = 0; a < t.length; a++) {
2272
- const l = t[a];
2273
- if (!l.active || l.isGrabbed || l.isThrown || l.isDying) continue;
2274
- let n = 0, o = 0, r = 0;
2059
+ const n = t[a];
2060
+ if (!n.active || n.isGrabbed || n.isThrown || n.isDying) continue;
2061
+ let o = 0, r = 0, c = 0;
2275
2062
  for (let d = 0; d < t.length; d++) {
2276
2063
  if (a === d) continue;
2277
- const _ = t[d];
2278
- if (!_.active || _.isGrabbed || _.isDying) continue;
2279
- const m = l.position.x - _.position.x, u = l.position.y - _.position.y, g = Math.hypot(m, u);
2280
- g > 0 && g < 70 && (n += m / g, o += u / g, r++);
2064
+ const g = t[d];
2065
+ if (!g.active || g.isGrabbed || g.isDying) continue;
2066
+ const b = n.position.x - g.position.x, f = n.position.y - g.position.y, _ = Math.hypot(b, f);
2067
+ _ > 0 && _ < 70 && (o += b / _, r += f / _, c++);
2281
2068
  }
2282
- r > 0 && (n /= r, o /= r, l.velocity.x += n * 60 * e, l.velocity.y += o * 60 * e);
2283
- const c = i.x - l.position.x, h = i.y - l.position.y, p = Math.hypot(c, h);
2284
- if (p > 0) {
2069
+ c > 0 && (o /= c, r /= c, n.velocity.x += o * 60 * e, n.velocity.y += r * 60 * e);
2070
+ const l = i.x - n.position.x, h = i.y - n.position.y, u = Math.hypot(l, h);
2071
+ if (u > 0) {
2285
2072
  const d = 80 + Math.min(this.wave * 4, 40);
2286
- l.velocity.x += c / p * d * e, l.velocity.y += h / p * d * e;
2073
+ n.velocity.x += l / u * d * e, n.velocity.y += h / u * d * e;
2287
2074
  }
2288
- if (l.maxSpeed > 0) {
2289
- const d = Math.hypot(l.velocity.x, l.velocity.y);
2290
- if (d > l.maxSpeed) {
2291
- const _ = l.maxSpeed / d;
2292
- l.velocity.x *= _, l.velocity.y *= _;
2075
+ if (n.maxSpeed > 0) {
2076
+ const d = Math.hypot(n.velocity.x, n.velocity.y);
2077
+ if (d > n.maxSpeed) {
2078
+ const g = n.maxSpeed / d;
2079
+ n.velocity.x *= g, n.velocity.y *= g;
2293
2080
  }
2294
2081
  }
2295
2082
  }
2296
2083
  }
2297
2084
  }
2298
- class ht {
2085
+ const ft = {
2086
+ mergeDistance: 34,
2087
+ magneticAttractionDistance: 190,
2088
+ magneticAttractionStrength: 900,
2089
+ baseRadius: 5,
2090
+ radiusGrowthPerLevel: 2.5,
2091
+ maxRadius: 24,
2092
+ mergeDuration: 0.65,
2093
+ blueThreshold: 4,
2094
+ yellowThreshold: 32,
2095
+ redThreshold: 256
2096
+ };
2097
+ class k {
2299
2098
  id;
2300
2099
  position;
2301
2100
  velocity;
2302
2101
  value;
2303
- radius = 5;
2102
+ radius;
2103
+ color;
2304
2104
  active = !0;
2305
2105
  isAbsorbing = !1;
2306
- constructor(t, e = 1) {
2307
- this.id = `xp_${Date.now()}_${Math.random()}`, this.position = { ...t }, this.value = e;
2308
- const i = Math.random() * Math.PI * 2, s = 25 + Math.random() * 45;
2106
+ isMerging = !1;
2107
+ visualScale = 1;
2108
+ mergePulse = 0;
2109
+ absorbedByCore = !1;
2110
+ config;
2111
+ mergeTarget = null;
2112
+ mergeStartPosition = null;
2113
+ mergeElapsed = 0;
2114
+ constructor(t, e = 1, i = ft) {
2115
+ this.id = `xp_${Date.now()}_${Math.random()}`, this.position = { ...t }, this.value = Math.max(1, e), this.config = i, this.radius = i.baseRadius, this.color = E.experience, this.updateAppearance();
2116
+ const s = Math.random() * Math.PI * 2, a = 25 + Math.random() * 45;
2309
2117
  this.velocity = {
2310
- x: Math.cos(i) * s,
2311
- y: Math.sin(i) * s
2118
+ x: Math.cos(s) * a,
2119
+ y: Math.sin(s) * a
2312
2120
  };
2313
2121
  }
2122
+ /**
2123
+ * Fuses nearby, loose orbs into a single cluster. A spatial hash keeps this
2124
+ * bounded when many enemies die in the same area instead of comparing every
2125
+ * orb with every other orb.
2126
+ */
2127
+ static mergeNearby(t, e) {
2128
+ if (t.length < 2 || e <= 0) return;
2129
+ const i = e, s = /* @__PURE__ */ new Map();
2130
+ for (const a of t) {
2131
+ if (!a.active || a.isAbsorbing || a.isMerging) continue;
2132
+ const n = k.getCellKey(a.position, i), o = s.get(n);
2133
+ o ? o.push(a) : s.set(n, [a]);
2134
+ }
2135
+ for (const a of t) {
2136
+ if (!a.active || a.isAbsorbing || a.isMerging) continue;
2137
+ const n = Math.floor(a.position.x / i), o = Math.floor(a.position.y / i);
2138
+ for (let r = -1; r <= 1; r++)
2139
+ for (let c = -1; c <= 1; c++) {
2140
+ const l = s.get(`${n + r}:${o + c}`);
2141
+ if (l)
2142
+ for (const h of l) {
2143
+ if (h === a || !h.active || h.isAbsorbing || h.isMerging) continue;
2144
+ const u = h.position.x - a.position.x, d = h.position.y - a.position.y, g = Math.hypot(u, d), b = Math.min(12, Math.max(0, Math.max(a.radius, h.radius) - a.config.baseRadius));
2145
+ g <= e + b && a.mergeWith(h);
2146
+ }
2147
+ }
2148
+ }
2149
+ }
2150
+ /** Applies a gentle pairwise pull before fusion so the player can read the cluster forming. */
2151
+ static attractNearby(t, e, i) {
2152
+ const s = e.magneticAttractionDistance;
2153
+ if (t.length < 2 || s <= 0 || e.magneticAttractionStrength <= 0) return;
2154
+ const a = s, n = /* @__PURE__ */ new Map();
2155
+ for (const o of t) {
2156
+ if (!o.active || o.isAbsorbing || o.isMerging) continue;
2157
+ const r = k.getCellKey(o.position, a), c = n.get(r);
2158
+ c ? c.push(o) : n.set(r, [o]);
2159
+ }
2160
+ for (const o of t) {
2161
+ if (!o.active || o.isAbsorbing || o.isMerging) continue;
2162
+ const r = Math.floor(o.position.x / a), c = Math.floor(o.position.y / a);
2163
+ for (let l = -1; l <= 1; l++)
2164
+ for (let h = -1; h <= 1; h++) {
2165
+ const u = n.get(`${r + l}:${c + h}`);
2166
+ if (u)
2167
+ for (const d of u) {
2168
+ if (d === o || !d.active || d.isAbsorbing || d.isMerging || d.id <= o.id)
2169
+ continue;
2170
+ const g = d.position.x - o.position.x, b = d.position.y - o.position.y, f = Math.hypot(g, b);
2171
+ if (f <= 0 || f > s) continue;
2172
+ const _ = 1 - f / s, y = e.magneticAttractionStrength * _ * _ * i, m = g / f, v = b / f;
2173
+ o.velocity.x += m * y, o.velocity.y += v * y, d.velocity.x -= m * y, d.velocity.y -= v * y;
2174
+ }
2175
+ }
2176
+ }
2177
+ }
2314
2178
  beginAbsorption() {
2315
2179
  this.active && (this.isAbsorbing = !0);
2316
2180
  }
2181
+ mergeWith(t) {
2182
+ if (!this.active || !t.active || this.isAbsorbing || t.isAbsorbing || this === t)
2183
+ return;
2184
+ const e = this.value + t.value, i = this.value / e, s = t.value / e;
2185
+ this.position = {
2186
+ x: this.position.x * i + t.position.x * s,
2187
+ y: this.position.y * i + t.position.y * s
2188
+ }, this.velocity = {
2189
+ x: this.velocity.x * i + t.velocity.x * s,
2190
+ y: this.velocity.y * i + t.velocity.y * s
2191
+ }, this.value = e, this.updateAppearance(), this.mergePulse = Math.min(1, this.mergePulse + 0.35), t.isMerging = !0, t.mergeTarget = this, t.mergeStartPosition = { ...t.position }, t.mergeElapsed = 0, t.visualScale = 1, t.isAbsorbing = !1, t.velocity = { x: 0, y: 0 };
2192
+ }
2317
2193
  update(t, e, i, s) {
2318
2194
  if (this.active) {
2319
- if (this.isAbsorbing) {
2320
- const a = e.x - this.position.x, l = e.y - this.position.y, n = Math.hypot(a, l);
2321
- if (n <= i + this.radius) {
2322
- this.position = { ...e }, this.active = !1;
2195
+ if (this.isMerging) {
2196
+ this.updateMergeAnimation(t);
2197
+ return;
2198
+ }
2199
+ if (this.mergePulse = Math.max(0, this.mergePulse - t * 3.5), this.isAbsorbing) {
2200
+ const a = e.x - this.position.x, n = e.y - this.position.y, o = Math.hypot(a, n);
2201
+ if (o <= i + this.radius) {
2202
+ this.position = { ...e }, this.absorbedByCore = !0, this.active = !1;
2323
2203
  return;
2324
2204
  }
2325
- const o = Math.min(s + n * 0.7, 1600);
2205
+ const r = Math.min(s + o * 0.7, 1600);
2326
2206
  this.velocity = {
2327
- x: a / n * o,
2328
- y: l / n * o
2207
+ x: a / o * r,
2208
+ y: n / o * r
2329
2209
  };
2330
2210
  } else {
2331
2211
  const a = Math.pow(0.04, t);
@@ -2334,22 +2214,45 @@ class ht {
2334
2214
  this.position.x += this.velocity.x * t, this.position.y += this.velocity.y * t;
2335
2215
  }
2336
2216
  }
2217
+ updateAppearance() {
2218
+ const e = Math.log2(this.value) * this.config.radiusGrowthPerLevel;
2219
+ this.radius = Math.min(this.config.maxRadius, this.config.baseRadius + e), this.color = this.getTierColor();
2220
+ }
2221
+ getTierColor() {
2222
+ return this.value >= this.config.redThreshold ? E.experienceRed : this.value >= this.config.yellowThreshold ? E.experienceYellow : this.value >= this.config.blueThreshold ? E.experienceBlue : E.experience;
2223
+ }
2224
+ updateMergeAnimation(t) {
2225
+ const e = this.mergeTarget, i = this.mergeStartPosition;
2226
+ if (!e || !e.active || !i) {
2227
+ this.active = !1, this.isMerging = !1;
2228
+ return;
2229
+ }
2230
+ this.mergeElapsed += t;
2231
+ const s = Math.min(1, this.mergeElapsed / this.config.mergeDuration), a = 1 - Math.pow(1 - s, 3);
2232
+ this.position = {
2233
+ x: i.x + (e.position.x - i.x) * a,
2234
+ y: i.y + (e.position.y - i.y) * a
2235
+ }, this.visualScale = 1 - a * 0.85, s >= 1 && (this.active = !1, this.isMerging = !1, this.mergeTarget = null, this.mergeStartPosition = null, this.visualScale = 1);
2236
+ }
2237
+ static getCellKey(t, e) {
2238
+ return `${Math.floor(t.x / e)}:${Math.floor(t.y / e)}`;
2239
+ }
2337
2240
  }
2338
- const N = {
2241
+ const U = {
2339
2242
  bioluminescent: {
2340
2243
  bgInner: "#081220",
2341
2244
  bgMid: "#050b16",
2342
2245
  bgOuter: "#020408",
2343
2246
  nebulaCyan: "rgba(0, 240, 255, 0.08)",
2344
2247
  nebulaMagenta: "rgba(255, 0, 221, 0.07)",
2345
- piercerColor: "#ff2a5f",
2346
- sporeColor: "#39ff14",
2347
- clusterColor: "#ffd700",
2348
- parasiteColor: "#9d4edd",
2349
- microColor: "#ffd700",
2350
- corePrimary: "#00f0ff",
2351
- coreGlow: "rgba(0, 240, 255, 0.85)",
2352
- tendrilMagenta: "#ff00dd",
2248
+ piercerColor: E.piercer,
2249
+ sporeColor: E.spore,
2250
+ clusterColor: E.cluster,
2251
+ parasiteColor: E.parasite,
2252
+ microColor: E.micro,
2253
+ corePrimary: E.core,
2254
+ coreGlow: E.coreGlow,
2255
+ tendrilMagenta: E.coreAccent,
2353
2256
  tendrilCyan: "#00ffff",
2354
2257
  effectHighlight: "#ffffff",
2355
2258
  effectInk: "#ffffff",
@@ -2402,22 +2305,22 @@ const N = {
2402
2305
  vortexCore: "#274760",
2403
2306
  vortexMid: "#577c9e"
2404
2307
  }
2405
- }, k = "axon_surge_theme";
2406
- class pt {
2308
+ }, K = "axon_surge_theme";
2309
+ class yt {
2407
2310
  currentTheme = "dark";
2408
2311
  isExplicit = !1;
2409
2312
  targetElement;
2410
2313
  constructor(t, e) {
2411
2314
  e !== void 0 && (this.targetElement = e);
2412
2315
  try {
2413
- const i = localStorage.getItem(k);
2414
- if (i && N[i]) {
2316
+ const i = localStorage.getItem(K);
2317
+ if (i && U[i]) {
2415
2318
  this.currentTheme = i, this.isExplicit = !0, this.applyThemeToDOM();
2416
2319
  return;
2417
2320
  }
2418
2321
  } catch {
2419
2322
  }
2420
- if (t && N[t]) {
2323
+ if (t && U[t]) {
2421
2324
  this.currentTheme = t, this.isExplicit = !0, this.applyThemeToDOM();
2422
2325
  return;
2423
2326
  }
@@ -2427,10 +2330,10 @@ class pt {
2427
2330
  this.targetElement = t, this.isExplicit ? this.applyThemeToDOM() : this.currentTheme = this.detectParentOrSystemTheme();
2428
2331
  }
2429
2332
  setTheme(t) {
2430
- if (N[t]) {
2333
+ if (U[t]) {
2431
2334
  this.currentTheme = t, this.isExplicit = !0, this.applyThemeToDOM();
2432
2335
  try {
2433
- localStorage.setItem(k, t);
2336
+ localStorage.setItem(K, t);
2434
2337
  } catch {
2435
2338
  }
2436
2339
  }
@@ -2438,7 +2341,7 @@ class pt {
2438
2341
  clearExplicitTheme() {
2439
2342
  this.isExplicit = !1;
2440
2343
  try {
2441
- localStorage.removeItem(k);
2344
+ localStorage.removeItem(K);
2442
2345
  } catch {
2443
2346
  }
2444
2347
  this.removeThemeClasses(), this.currentTheme = this.detectParentOrSystemTheme();
@@ -2447,7 +2350,7 @@ class pt {
2447
2350
  return this.isExplicit || (this.currentTheme = this.detectParentOrSystemTheme()), this.currentTheme;
2448
2351
  }
2449
2352
  getPalette() {
2450
- return N[this.getTheme()] || N.dark;
2353
+ return U[this.getTheme()] || U.dark;
2451
2354
  }
2452
2355
  detectParentOrSystemTheme() {
2453
2356
  try {
@@ -2485,83 +2388,83 @@ class pt {
2485
2388
  this.targetElement.classList.add(t);
2486
2389
  }
2487
2390
  }
2488
- class ut {
2391
+ class xt {
2489
2392
  maxDistance = 800;
2490
2393
  maxThickness = 18;
2491
2394
  minThickness = 5;
2492
2395
  maxLength = 110;
2493
2396
  minLength = 50;
2494
2397
  render(t, e, i, s, a) {
2495
- for (const l of e) {
2496
- if (!l.active || l.isGrabbed || l.isDying) continue;
2497
- const n = l.position.x, o = l.position.y;
2498
- if (n >= 0 && n <= i && o >= 0 && o <= s) continue;
2499
- const r = Math.min(i, Math.max(0, n)), c = Math.min(s, Math.max(0, o)), h = Math.hypot(n - r, o - c);
2398
+ for (const n of e) {
2399
+ if (!n.active || n.isGrabbed || n.isDying) continue;
2400
+ const o = n.position.x, r = n.position.y;
2401
+ if (o >= 0 && o <= i && r >= 0 && r <= s) continue;
2402
+ const c = Math.min(i, Math.max(0, o)), l = Math.min(s, Math.max(0, r)), h = Math.hypot(o - c, r - l);
2500
2403
  if (h <= 0 || h > this.maxDistance) continue;
2501
- const p = Math.min(1, Math.max(0, h / this.maxDistance)), d = this.minThickness + (this.maxThickness - this.minThickness) * (1 - p), _ = this.minLength + (this.maxLength - this.minLength) * (1 - p), m = 0.55 + (1 - p) * 0.35;
2502
- if (t.save(), t.globalAlpha = m, t.shadowColor = l.color, t.shadowBlur = 14, t.strokeStyle = l.color, t.lineWidth = d, t.lineCap = "round", t.beginPath(), r === 0 || r === i) {
2503
- const u = Math.max(0, c - _ / 2), g = Math.min(s, c + _ / 2);
2504
- t.moveTo(r, u), t.lineTo(r, g);
2404
+ const u = Math.min(1, Math.max(0, h / this.maxDistance)), d = this.minThickness + (this.maxThickness - this.minThickness) * (1 - u), g = this.minLength + (this.maxLength - this.minLength) * (1 - u), b = 0.55 + (1 - u) * 0.35;
2405
+ if (t.save(), t.globalAlpha = b, t.shadowColor = n.color, t.shadowBlur = 14, t.strokeStyle = n.color, t.lineWidth = d, t.lineCap = "round", t.beginPath(), c === 0 || c === i) {
2406
+ const f = Math.max(0, l - g / 2), _ = Math.min(s, l + g / 2);
2407
+ t.moveTo(c, f), t.lineTo(c, _);
2505
2408
  } else {
2506
- const u = Math.max(0, r - _ / 2), g = Math.min(i, r + _ / 2);
2507
- t.moveTo(u, c), t.lineTo(g, c);
2409
+ const f = Math.max(0, c - g / 2), _ = Math.min(i, c + g / 2);
2410
+ t.moveTo(f, l), t.lineTo(_, l);
2508
2411
  }
2509
- t.stroke(), t.strokeStyle = a.effectHighlight, t.lineWidth = Math.max(1.8, d * 0.28), t.globalAlpha = m * 0.9, t.stroke(), t.restore();
2412
+ t.stroke(), t.strokeStyle = a.effectHighlight, t.lineWidth = Math.max(1.8, d * 0.28), t.globalAlpha = b * 0.9, t.stroke(), t.restore();
2510
2413
  }
2511
2414
  }
2512
2415
  }
2513
- class dt {
2514
- render(t, e, i, s, a, l, n = !1) {
2515
- const o = Math.max(1, a), r = Math.min(1, (o - 1) / 8), c = this.wrapHue(190 + (o - 1) * 34 + Math.sin(s * 0.1) * 5), h = this.wrapHue(c + 42), p = this.wrapHue(c + 150), d = e / 2, _ = i / 2, m = Math.max(e, i) * 0.82, u = t.createRadialGradient(d, _, 20, d, _, m);
2516
- n ? (u.addColorStop(0, `hsl(${c}, 42%, 91%)`), u.addColorStop(0.42, `hsl(${h}, 42%, 86%)`), u.addColorStop(0.78, `hsl(${p}, 38%, 81%)`), u.addColorStop(1, l.bgOuter)) : (u.addColorStop(0, `hsl(${c}, 42%, 11%)`), u.addColorStop(0.42, `hsl(${h}, 48%, 8%)`), u.addColorStop(0.78, `hsl(${p}, 42%, 5%)`), u.addColorStop(1, `hsl(${p}, 48%, 2.5%)`)), t.fillStyle = u, t.fillRect(0, 0, e, i);
2517
- const g = Math.min(e, i) * 0.055;
2416
+ class bt {
2417
+ render(t, e, i, s, a, n, o = !1) {
2418
+ const r = Math.max(1, a), c = Math.min(1, (r - 1) / 8), l = this.wrapHue(190 + (r - 1) * 34 + Math.sin(s * 0.1) * 5), h = this.wrapHue(l + 42), u = this.wrapHue(l + 150), d = e / 2, g = i / 2, b = Math.max(e, i) * 0.82, f = t.createRadialGradient(d, g, 20, d, g, b);
2419
+ o ? (f.addColorStop(0, `hsl(${l}, 42%, 91%)`), f.addColorStop(0.42, `hsl(${h}, 42%, 86%)`), f.addColorStop(0.78, `hsl(${u}, 38%, 81%)`), f.addColorStop(1, n.bgOuter)) : (f.addColorStop(0, `hsl(${l}, 42%, 11%)`), f.addColorStop(0.42, `hsl(${h}, 48%, 8%)`), f.addColorStop(0.78, `hsl(${u}, 42%, 5%)`), f.addColorStop(1, `hsl(${u}, 48%, 2.5%)`)), t.fillStyle = f, t.fillRect(0, 0, e, i);
2420
+ const _ = Math.min(e, i) * 0.055;
2518
2421
  this.drawNebula(
2519
2422
  t,
2520
- e * 0.22 + Math.sin(s * 0.18) * g,
2521
- i * 0.28 + Math.cos(s * 0.15) * g,
2423
+ e * 0.22 + Math.sin(s * 0.18) * _,
2424
+ i * 0.28 + Math.cos(s * 0.15) * _,
2522
2425
  Math.max(e, i) * 0.42,
2523
- c,
2524
- n ? 0.27 : 0.23,
2525
- n
2426
+ l,
2427
+ o ? 0.27 : 0.23,
2428
+ o
2526
2429
  ), this.drawNebula(
2527
2430
  t,
2528
- e * 0.8 + Math.cos(s * 0.14) * g,
2529
- i * 0.7 + Math.sin(s * 0.17) * g,
2431
+ e * 0.8 + Math.cos(s * 0.14) * _,
2432
+ i * 0.7 + Math.sin(s * 0.17) * _,
2530
2433
  Math.max(e, i) * 0.48,
2531
2434
  h,
2532
- n ? 0.23 : 0.2,
2533
- n
2435
+ o ? 0.23 : 0.2,
2436
+ o
2534
2437
  ), this.drawNebula(
2535
2438
  t,
2536
- e * 0.86 + Math.sin(s * 0.11) * g * 0.7,
2537
- i * 0.16 + Math.cos(s * 0.13) * g * 0.7,
2439
+ e * 0.86 + Math.sin(s * 0.11) * _ * 0.7,
2440
+ i * 0.16 + Math.cos(s * 0.13) * _ * 0.7,
2538
2441
  Math.max(e, i) * 0.3,
2539
- p,
2540
- n ? 0.17 : 0.14,
2541
- n
2542
- ), this.drawSoftFlows(t, e, i, s, c, r, n), this.drawParticleField(t, e, i, s, c, h, o, n);
2543
- const f = t.createRadialGradient(d, _, Math.min(e, i) * 0.28, d, _, m);
2544
- f.addColorStop(0, "rgba(0, 0, 0, 0)"), f.addColorStop(1, n ? "rgba(40, 60, 90, 0.09)" : "rgba(0, 0, 0, 0.2)"), t.fillStyle = f, t.fillRect(0, 0, e, i);
2545
- }
2546
- drawNebula(t, e, i, s, a, l, n) {
2547
- const o = t.createRadialGradient(e, i, 0, e, i, s), r = n ? 52 : 60;
2548
- o.addColorStop(0, `hsla(${a}, ${n ? 88 : 80}%, ${r}%, ${l})`), o.addColorStop(0.38, `hsla(${a}, ${n ? 82 : 75}%, ${r - (n ? 7 : 12)}%, ${l * 0.5})`), o.addColorStop(1, "rgba(0, 0, 0, 0)"), t.fillStyle = o, t.beginPath(), t.arc(e, i, s, 0, Math.PI * 2), t.fill();
2549
- }
2550
- drawSoftFlows(t, e, i, s, a, l, n) {
2442
+ u,
2443
+ o ? 0.17 : 0.14,
2444
+ o
2445
+ ), this.drawSoftFlows(t, e, i, s, l, c, o), this.drawParticleField(t, e, i, s, l, h, r, o);
2446
+ const x = t.createRadialGradient(d, g, Math.min(e, i) * 0.28, d, g, b);
2447
+ x.addColorStop(0, "rgba(0, 0, 0, 0)"), x.addColorStop(1, o ? "rgba(40, 60, 90, 0.09)" : "rgba(0, 0, 0, 0.2)"), t.fillStyle = x, t.fillRect(0, 0, e, i);
2448
+ }
2449
+ drawNebula(t, e, i, s, a, n, o) {
2450
+ const r = t.createRadialGradient(e, i, 0, e, i, s), c = o ? 52 : 60;
2451
+ r.addColorStop(0, `hsla(${a}, ${o ? 88 : 80}%, ${c}%, ${n})`), r.addColorStop(0.38, `hsla(${a}, ${o ? 82 : 75}%, ${c - (o ? 7 : 12)}%, ${n * 0.5})`), r.addColorStop(1, "rgba(0, 0, 0, 0)"), t.fillStyle = r, t.beginPath(), t.arc(e, i, s, 0, Math.PI * 2), t.fill();
2452
+ }
2453
+ drawSoftFlows(t, e, i, s, a, n, o) {
2551
2454
  t.save();
2552
- const o = Math.min(e, i), r = o * 0.035;
2553
- t.globalAlpha = n ? 0.15 : 0.12, t.lineWidth = Math.max(1.3, o * 17e-4), t.lineCap = "round", t.shadowBlur = o * 0.02, t.shadowColor = `hsla(${a}, 85%, ${n ? 52 : 62}%, 0.28)`;
2554
- for (let c = 0; c < 3; c++) {
2555
- const h = (c - 1) * o * 0.075 + Math.sin(s * 0.06 + c) * r, p = c * 28 + l * 18;
2556
- t.strokeStyle = `hsla(${this.wrapHue(a + p)}, ${n ? 78 : 74}%, ${n ? 47 : 62}%, ${0.52 - c * 0.08})`, t.beginPath(), t.moveTo(-o * 0.12, i * 0.2 + h), t.bezierCurveTo(e * 0.2, i * 0.02 + h, e * 0.47, i * 0.2 - h, e * 0.73, i * 0.07 + h), t.bezierCurveTo(e * 0.94, -o * 0.02 + h, e * 1.04, i * 0.16 + h, e * 1.1, i * 0.36 + h), t.stroke(), t.beginPath(), t.moveTo(-o * 0.1, i * 0.8 - h), t.bezierCurveTo(e * 0.2, i * 0.98 - h, e * 0.48, i * 0.8 + h, e * 0.75, i * 0.93 - h), t.bezierCurveTo(e * 0.94, i * 1.02 - h, e * 1.04, i * 0.84 - h, e * 1.1, i * 0.65 - h), t.stroke();
2455
+ const r = Math.min(e, i), c = r * 0.035;
2456
+ t.globalAlpha = o ? 0.15 : 0.12, t.lineWidth = Math.max(1.3, r * 17e-4), t.lineCap = "round", t.shadowBlur = r * 0.02, t.shadowColor = `hsla(${a}, 85%, ${o ? 52 : 62}%, 0.28)`;
2457
+ for (let l = 0; l < 3; l++) {
2458
+ const h = (l - 1) * r * 0.075 + Math.sin(s * 0.06 + l) * c, u = l * 28 + n * 18;
2459
+ t.strokeStyle = `hsla(${this.wrapHue(a + u)}, ${o ? 78 : 74}%, ${o ? 47 : 62}%, ${0.52 - l * 0.08})`, t.beginPath(), t.moveTo(-r * 0.12, i * 0.2 + h), t.bezierCurveTo(e * 0.2, i * 0.02 + h, e * 0.47, i * 0.2 - h, e * 0.73, i * 0.07 + h), t.bezierCurveTo(e * 0.94, -r * 0.02 + h, e * 1.04, i * 0.16 + h, e * 1.1, i * 0.36 + h), t.stroke(), t.beginPath(), t.moveTo(-r * 0.1, i * 0.8 - h), t.bezierCurveTo(e * 0.2, i * 0.98 - h, e * 0.48, i * 0.8 + h, e * 0.75, i * 0.93 - h), t.bezierCurveTo(e * 0.94, i * 1.02 - h, e * 1.04, i * 0.84 - h, e * 1.1, i * 0.65 - h), t.stroke();
2557
2460
  }
2558
2461
  t.restore();
2559
2462
  }
2560
- drawParticleField(t, e, i, s, a, l, n, o) {
2561
- const r = 44 + Math.min(24, n * 2), c = e / 2, h = i / 2, p = Math.min(e, i) * 0.17;
2562
- for (let d = 0; d < r; d++) {
2563
- const _ = d + n * 17, m = this.unitNoise(_ * 1.7) * e, u = this.unitNoise(_ * 2.9 + 13) * i, g = Math.hypot(m - c, u - h), f = Math.min(1, Math.max(0, (g - p) / (Math.min(e, i) * 0.28))), b = 0.7 + Math.sin(s * (0.6 + d % 4 * 0.08) + _) * 0.3, y = (o ? 0.34 : 0.3) * f * b, v = 0.65 + d % 3 * 0.45;
2564
- t.fillStyle = `hsla(${d % 3 === 0 ? l : a}, ${o ? 82 : 78}%, ${o ? 40 : 70}%, ${y})`, t.beginPath(), t.arc(m, u, v, 0, Math.PI * 2), t.fill();
2463
+ drawParticleField(t, e, i, s, a, n, o, r) {
2464
+ const c = 44 + Math.min(24, o * 2), l = e / 2, h = i / 2, u = Math.min(e, i) * 0.17;
2465
+ for (let d = 0; d < c; d++) {
2466
+ const g = d + o * 17, b = this.unitNoise(g * 1.7) * e, f = this.unitNoise(g * 2.9 + 13) * i, _ = Math.hypot(b - l, f - h), x = Math.min(1, Math.max(0, (_ - u) / (Math.min(e, i) * 0.28))), y = 0.7 + Math.sin(s * (0.6 + d % 4 * 0.08) + g) * 0.3, m = (r ? 0.34 : 0.3) * x * y, v = 0.65 + d % 3 * 0.45;
2467
+ t.fillStyle = `hsla(${d % 3 === 0 ? n : a}, ${r ? 82 : 78}%, ${r ? 40 : 70}%, ${m})`, t.beginPath(), t.arc(b, f, v, 0, Math.PI * 2), t.fill();
2565
2468
  }
2566
2469
  }
2567
2470
  unitNoise(t) {
@@ -2571,24 +2474,24 @@ class dt {
2571
2474
  return (t % 360 + 360) % 360;
2572
2475
  }
2573
2476
  }
2574
- class gt {
2477
+ class Et {
2575
2478
  render(t, e, i) {
2576
2479
  if (!e.isActive) return;
2577
2480
  const s = e.getLauncher(), a = e.getTarget();
2578
2481
  if (!s || !a || !s.active || !a.active) return;
2579
- const l = e.getStep();
2580
- if (t.save(), l === 1) {
2581
- const n = Math.sin(i * 6) * 10, o = s.position.x, r = s.position.y - 65 + n;
2582
- t.shadowColor = "#00f0ff", t.shadowBlur = 20, t.fillStyle = "#00f0ff", t.beginPath(), t.moveTo(o, r + 20), t.lineTo(o - 16, r - 12), t.lineTo(o - 6, r - 12), t.lineTo(o - 6, r - 32), t.lineTo(o + 6, r - 32), t.lineTo(o + 6, r - 12), t.lineTo(o + 16, r - 12), t.closePath(), t.fill();
2583
- const c = s.radius * (1.6 + Math.sin(i * 8) * 0.3);
2584
- t.strokeStyle = "rgba(0, 240, 255, 0.8)", t.lineWidth = 3, t.beginPath(), t.arc(s.position.x, s.position.y, c, 0, Math.PI * 2), t.stroke();
2585
- } else if (l === 2) {
2586
- const n = a.position.x - s.position.x, o = a.position.y - s.position.y;
2587
- if (Math.hypot(n, o) > 10) {
2588
- const c = Math.atan2(o, n);
2589
- t.shadowColor = "#ff00dd", t.shadowBlur = 22, t.strokeStyle = "#ff00dd", t.lineWidth = 4, t.setLineDash([12, 8]), t.beginPath(), t.moveTo(s.position.x, s.position.y), t.lineTo(a.position.x, a.position.y), t.stroke(), t.setLineDash([]);
2590
- const h = a.position.x - Math.cos(c) * (a.radius + 10), p = a.position.y - Math.sin(c) * (a.radius + 10);
2591
- t.fillStyle = "#ff00dd", t.beginPath(), t.moveTo(h, p), t.lineTo(h - Math.cos(c - 0.4) * 24, p - Math.sin(c - 0.4) * 24), t.lineTo(h - Math.cos(c + 0.4) * 24, p - Math.sin(c + 0.4) * 24), t.closePath(), t.fill();
2482
+ const n = e.getStep();
2483
+ if (t.save(), n === 1) {
2484
+ const o = Math.sin(i * 6) * 10, r = s.position.x, c = s.position.y - 65 + o;
2485
+ t.shadowColor = E.core, t.shadowBlur = 20, t.fillStyle = E.core, t.beginPath(), t.moveTo(r, c + 20), t.lineTo(r - 16, c - 12), t.lineTo(r - 6, c - 12), t.lineTo(r - 6, c - 32), t.lineTo(r + 6, c - 32), t.lineTo(r + 6, c - 12), t.lineTo(r + 16, c - 12), t.closePath(), t.fill();
2486
+ const l = s.radius * (1.6 + Math.sin(i * 8) * 0.3);
2487
+ t.strokeStyle = "rgba(0, 240, 255, 0.8)", t.lineWidth = 3, t.beginPath(), t.arc(s.position.x, s.position.y, l, 0, Math.PI * 2), t.stroke();
2488
+ } else if (n === 2) {
2489
+ const o = a.position.x - s.position.x, r = a.position.y - s.position.y;
2490
+ if (Math.hypot(o, r) > 10) {
2491
+ const l = Math.atan2(r, o);
2492
+ t.shadowColor = E.coreAccent, t.shadowBlur = 22, t.strokeStyle = E.coreAccent, t.lineWidth = 4, t.setLineDash([12, 8]), t.beginPath(), t.moveTo(s.position.x, s.position.y), t.lineTo(a.position.x, a.position.y), t.stroke(), t.setLineDash([]);
2493
+ const h = a.position.x - Math.cos(l) * (a.radius + 10), u = a.position.y - Math.sin(l) * (a.radius + 10);
2494
+ t.fillStyle = E.coreAccent, t.beginPath(), t.moveTo(h, u), t.lineTo(h - Math.cos(l - 0.4) * 24, u - Math.sin(l - 0.4) * 24), t.lineTo(h - Math.cos(l + 0.4) * 24, u - Math.sin(l + 0.4) * 24), t.closePath(), t.fill();
2592
2495
  const d = a.radius * (1.8 + Math.cos(i * 10) * 0.3);
2593
2496
  t.strokeStyle = "rgba(255, 0, 221, 0.9)", t.lineWidth = 3.5, t.beginPath(), t.arc(a.position.x, a.position.y, d, 0, Math.PI * 2), t.stroke();
2594
2497
  }
@@ -2596,63 +2499,146 @@ class gt {
2596
2499
  t.restore();
2597
2500
  }
2598
2501
  }
2599
- class _t {
2502
+ class vt {
2503
+ constructor(t) {
2504
+ this.configManager = t;
2505
+ }
2506
+ draw(t, e, i, s, a) {
2507
+ t.shadowColor = a, t.shadowBlur = 16, t.fillStyle = a, (this.renderers[e.behavior.rendererId] ?? this.drawCircle)(t, e, i, s);
2508
+ }
2509
+ renderers = {
2510
+ piercer: (t, e, i, s) => this.drawPiercer(t, e, i, s),
2511
+ spore: (t, e, i, s) => this.drawSpore(t, e, i, s),
2512
+ cluster: (t, e, i) => this.drawCluster(t, e, i),
2513
+ parasite: (t, e, i, s) => this.drawParasite(t, e, i, s),
2514
+ heavy: (t, e, i, s) => this.drawHeavy(t, e, i, s),
2515
+ circle: (t, e) => this.drawCircle(t, e)
2516
+ };
2517
+ drawCircle(t, e) {
2518
+ t.beginPath(), t.arc(0, 0, e.radius, 0, Math.PI * 2), t.fill();
2519
+ }
2520
+ drawPiercer(t, e, i, s) {
2521
+ t.rotate(e.rotation), t.beginPath();
2522
+ const a = 12;
2523
+ for (let n = 0; n <= a; n++) {
2524
+ const o = n * Math.PI * 2 / a, r = e.radius * 1.6 + Math.sin(o * 3 + i * 4) * 3, c = e.radius * 0.6 + Math.cos(o * 2 + i * 3) * 2, l = Math.cos(o) * r, h = Math.sin(o) * c;
2525
+ n === 0 ? t.moveTo(l, h) : t.lineTo(l, h);
2526
+ }
2527
+ t.closePath(), t.fill(), t.fillStyle = s.effectHighlight, t.beginPath(), t.arc(4, 0, 4, 0, Math.PI * 2), t.fill();
2528
+ }
2529
+ drawSpore(t, e, i, s) {
2530
+ t.beginPath();
2531
+ const a = 8;
2532
+ for (let n = 0; n < a; n++) {
2533
+ const o = n * Math.PI * 2 / a, r = e.radius + Math.sin(o * 4 + i * 3 + e.position.x) * 4, c = Math.cos(o) * r, l = Math.sin(o) * r;
2534
+ n === 0 ? t.moveTo(c, l) : t.lineTo(c, l);
2535
+ }
2536
+ t.closePath(), t.fill(), t.fillStyle = s.effectHighlight, t.beginPath(), t.arc(-4, -4, 5, 0, Math.PI * 2), t.arc(5, 3, 4, 0, Math.PI * 2), t.fill();
2537
+ }
2538
+ drawCluster(t, e, i) {
2539
+ t.beginPath();
2540
+ const s = Math.sin(i * 3) * 2;
2541
+ t.arc(0, 0, e.radius * 0.55, 0, Math.PI * 2), t.arc(-9 + s, -7, e.radius * 0.42, 0, Math.PI * 2), t.arc(9 - s, -7, e.radius * 0.42, 0, Math.PI * 2), t.arc(0, 9 + s, e.radius * 0.45, 0, Math.PI * 2), t.fill();
2542
+ }
2543
+ drawParasite(t, e, i, s) {
2544
+ t.beginPath();
2545
+ const a = 14;
2546
+ for (let n = 0; n <= a; n++) {
2547
+ const o = n * Math.PI * 2 / a, r = e.radius + Math.sin(o * 5 - i * 5) * 3, c = Math.cos(o) * r, l = Math.sin(o) * r;
2548
+ n === 0 ? t.moveTo(c, l) : t.lineTo(c, l);
2549
+ }
2550
+ t.closePath(), t.fill(), t.fillStyle = s.bgOuter, t.beginPath(), t.arc(0, 0, e.radius * 0.45, 0, Math.PI * 2), t.fill();
2551
+ }
2552
+ drawHeavy(t, e, i, s) {
2553
+ t.rotate(e.rotation);
2554
+ const a = e.radius, n = a * 0.88, o = Math.sin(i * 3 + e.position.x * 0.01) * 1.5, r = e.color;
2555
+ t.save(), t.translate(-a * 0.12, o), t.fillStyle = r, t.shadowColor = r, t.shadowBlur = 20, t.beginPath();
2556
+ const c = 10;
2557
+ for (let f = 0; f <= c; f++) {
2558
+ const _ = f * Math.PI * 2 / c, x = 1 + Math.sin(_ * 3 + i * 2.4) * 0.08, y = Math.cos(_) * n * x, m = Math.sin(_) * n * x * 0.9;
2559
+ f === 0 ? t.moveTo(y, m) : t.lineTo(y, m);
2560
+ }
2561
+ t.closePath(), t.fill(), t.fillStyle = s.effectHighlight, t.shadowColor = s.effectHighlight, t.shadowBlur = 7, t.beginPath(), t.arc(-a * 0.28, -a * 0.2, a * 0.11, 0, Math.PI * 2), t.arc(a * 0.02, a * 0.18, a * 0.08, 0, Math.PI * 2), t.fill(), t.restore();
2562
+ const l = a * 0.22, h = this.configManager.getConfig().shield.protectionArc, u = a * 0.72, d = a * 0.38;
2563
+ t.save(), t.translate(l, 0), t.beginPath(), t.arc(0, 0, u, -h / 2, h / 2, !1);
2564
+ const g = Math.cos(h / 2) * d, b = Math.sin(h / 2) * d;
2565
+ t.lineTo(g, b), t.arc(0, 0, d, h / 2, -h / 2, !0), t.closePath(), t.fillStyle = e.color, t.shadowColor = e.color, t.shadowBlur = e.isStationaryShield ? 28 : 16, t.fill(), t.strokeStyle = s.effectHighlight, t.lineWidth = 2.5, t.beginPath(), t.arc(0, 0, u + Math.sin(i * 4) * 2, -h / 2 + 0.1, h / 2 - 0.1), t.stroke(), t.strokeStyle = s.effectHighlight, t.lineWidth = 1.5, t.shadowColor = s.effectHighlight, t.shadowBlur = 8, t.beginPath(), t.arc(0, 0, d * 0.72, -h * 0.42, h * 0.42), t.stroke();
2566
+ for (let f = -2; f <= 2; f++) {
2567
+ const _ = f * h / 6, x = Math.cos(_) * (d + 6), y = Math.sin(_) * (d + 6);
2568
+ t.fillStyle = s.effectHighlight, t.beginPath(), t.arc(x, y, 3, 0, Math.PI * 2), t.fill();
2569
+ }
2570
+ t.restore();
2571
+ }
2572
+ }
2573
+ const At = {
2574
+ common: E.spore,
2575
+ rare: E.parasite,
2576
+ epic: E.cluster,
2577
+ legendary: E.core
2578
+ };
2579
+ class Mt {
2600
2580
  canvas;
2601
2581
  ctx;
2602
2582
  themeManager;
2583
+ configManager;
2603
2584
  indicatorRenderer;
2604
2585
  bgRenderer;
2605
2586
  tutorialArrowRenderer;
2606
- constructor(t, e) {
2607
- this.canvas = t, this.ctx = t.getContext("2d"), this.themeManager = e, this.indicatorRenderer = new ut(), this.bgRenderer = new dt(), this.tutorialArrowRenderer = new gt();
2587
+ enemyShapeRenderer;
2588
+ constructor(t, e, i) {
2589
+ this.canvas = t, this.ctx = t.getContext("2d"), this.themeManager = e, this.indicatorRenderer = new xt(), this.bgRenderer = new bt(), this.tutorialArrowRenderer = new Et(), this.enemyShapeRenderer = new vt(i), this.configManager = i;
2608
2590
  }
2609
- render(t, e, i, s, a, l, n, o, r, c, h, p = 1, d) {
2591
+ render(t, e, i, s, a, n, o, r, c, l, h, u = 1, d = 0, g = 0, b) {
2610
2592
  if (!this.ctx) return;
2611
- const _ = this.themeManager.getPalette(), { width: m, height: u } = this.canvas, g = r.getShakeOffset();
2612
- this.ctx.save(), this.ctx.translate(g.x, g.y), this.bgRenderer.render(
2593
+ const f = this.themeManager.getPalette(), { width: _, height: x } = this.canvas, y = c.getShakeOffset();
2594
+ this.ctx.save(), this.ctx.translate(y.x, y.y), this.bgRenderer.render(
2613
2595
  this.ctx,
2614
- m,
2615
- u,
2616
- t.pulsePhase,
2617
- p,
2618
2596
  _,
2597
+ x,
2598
+ t.pulsePhase,
2599
+ u,
2600
+ f,
2619
2601
  this.themeManager.getTheme() === "light"
2620
- ), this.drawExperienceOrbs(i, t.position, t.pulsePhase, _), this.drawToxicPools(s, t.pulsePhase), this.drawGravityVortices(a, t.pulsePhase), this.drawShockwaves(l), this.drawCoreFilaments(t, _), this.drawParticles(n), A.getInstance().getConfig().core.hasOffscreenIndicators && this.indicatorRenderer.render(this.ctx, e, m, u, _), this.drawOrganicEnemies(e, t.pulsePhase, _), d && this.tutorialArrowRenderer.render(this.ctx, d, t.pulsePhase), c ? this.drawPsionicTendril(t.position, c.position, _) : t && this.drawHoverTendril(t.position, h, e, _), this.drawOrganicConsciousnessCore(t, _), this.drawFloatingTexts(o), r.flashAlpha > 0 && (this.ctx.fillStyle = `rgba(255, 255, 255, ${r.flashAlpha})`, this.ctx.fillRect(0, 0, m, u)), this.ctx.restore();
2602
+ ), this.drawExperienceOrbs(i, t.position, t.pulsePhase, f), this.drawToxicPools(s, t.pulsePhase), this.drawGravityVortices(a, t.pulsePhase), this.drawShockwaves(n), this.drawCoreFilaments(t, f), this.drawAutoDeflectionBeams(t.position, e, t.pulsePhase, f), this.drawParticles(o), this.configManager.getConfig().core.hasOffscreenIndicators && this.indicatorRenderer.render(this.ctx, e, _, x, f), this.drawOrganicEnemies(e, t.pulsePhase, f), b && this.tutorialArrowRenderer.render(this.ctx, b, t.pulsePhase), l ? this.drawPsionicTendril(t.position, l.position, f) : t && this.drawHoverTendril(t.position, h, e, f), this.drawOrganicConsciousnessCore(t, f, d, g), this.drawFloatingTexts(r), c.flashAlpha > 0 && (this.ctx.fillStyle = `rgba(255, 255, 255, ${c.flashAlpha})`, this.ctx.fillRect(0, 0, _, x)), this.ctx.restore();
2621
2603
  }
2622
2604
  drawExperienceOrbs(t, e, i, s) {
2623
2605
  if (this.ctx)
2624
2606
  for (const a of t) {
2625
2607
  if (!a.active) continue;
2626
- const l = a.radius + Math.sin(i * 7 + a.position.x * 0.01) * 0.8, { x: n, y: o } = a.position;
2627
- this.ctx.save(), this.ctx.shadowColor = s.corePrimary, this.ctx.shadowBlur = a.isAbsorbing ? 18 : 10, a.isAbsorbing && (this.ctx.globalAlpha = 0.35, this.ctx.strokeStyle = s.tendrilCyan, this.ctx.lineWidth = 1, this.ctx.setLineDash([4, 7]), this.ctx.beginPath(), this.ctx.moveTo(n, o), this.ctx.lineTo(e.x, e.y), this.ctx.stroke(), this.ctx.setLineDash([]), this.ctx.globalAlpha = 1);
2628
- const r = this.ctx.createRadialGradient(n - 1, o - 1, 1, n, o, l * 1.6);
2629
- r.addColorStop(0, s.effectHighlight), r.addColorStop(0.4, s.corePrimary), r.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = r, this.ctx.beginPath(), this.ctx.arc(n, o, l * 1.6, 0, Math.PI * 2), this.ctx.fill(), this.ctx.fillStyle = s.effectHighlight, this.ctx.beginPath(), this.ctx.arc(n, o, l * 0.65, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = s.tendrilCyan, this.ctx.lineWidth = 1, this.ctx.beginPath(), this.ctx.arc(n, o, l + 3, -i * 2, -i * 2 + Math.PI * 1.4), this.ctx.stroke(), this.ctx.restore();
2608
+ const n = Math.max(
2609
+ 1,
2610
+ a.radius * a.visualScale + Math.sin(i * 7 + a.position.x * 0.01) * (0.8 + a.mergePulse * 1.2)
2611
+ ), { x: o, y: r } = a.position;
2612
+ this.ctx.save(), this.ctx.shadowColor = a.color, this.ctx.shadowBlur = a.isAbsorbing ? 18 : Math.min(24, 8 + a.radius * 0.45 + a.mergePulse * 10), a.isAbsorbing && (this.ctx.globalAlpha = 0.35, this.ctx.strokeStyle = s.tendrilCyan, this.ctx.lineWidth = 1, this.ctx.setLineDash([4, 7]), this.ctx.beginPath(), this.ctx.moveTo(o, r), this.ctx.lineTo(e.x, e.y), this.ctx.stroke(), this.ctx.setLineDash([]), this.ctx.globalAlpha = 1);
2613
+ const c = this.ctx.createRadialGradient(o - 1, r - 1, 1, o, r, n * 1.6);
2614
+ c.addColorStop(0, s.effectHighlight), c.addColorStop(0.4, a.color), c.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = c, this.ctx.beginPath(), this.ctx.arc(o, r, n * 1.6, 0, Math.PI * 2), this.ctx.fill(), this.ctx.fillStyle = s.effectHighlight, this.ctx.beginPath(), this.ctx.arc(o, r, n * 0.65, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = a.color, this.ctx.lineWidth = Math.min(3, 1 + a.radius * 0.08), this.ctx.beginPath(), this.ctx.arc(o, r, n + 3, -i * 2, -i * 2 + Math.PI * 1.4), this.ctx.stroke(), this.ctx.restore();
2630
2615
  }
2631
2616
  }
2632
2617
  drawToxicPools(t, e) {
2633
- if (this.ctx)
2634
- for (const i of t) {
2635
- const s = i.duration / i.maxDuration, a = Math.min(0.7, (1 - s) * 0.7), { x: l, y: n } = i.position;
2636
- this.ctx.save();
2637
- const o = this.ctx.createRadialGradient(l, n, 10, l, n, i.radius * 1.2);
2638
- o.addColorStop(0, `rgba(57, 255, 20, ${a * 0.9})`), o.addColorStop(0.4, `rgba(16, 185, 129, ${a * 0.65})`), o.addColorStop(0.8, `rgba(5, 80, 45, ${a * 0.35})`), o.addColorStop(1, "rgba(2, 35, 15, 0)"), this.ctx.fillStyle = o, this.ctx.shadowColor = "#39ff14", this.ctx.shadowBlur = 30, this.ctx.beginPath();
2639
- const r = 70;
2640
- for (let c = 0; c <= r; c++) {
2641
- const h = c * Math.PI * 2 / r, p = Math.sin(h * 5 + e * 3) * 14, d = Math.cos(h * 3 - e * 2) * 9, _ = i.radius + p + d, m = l + Math.cos(h) * _, u = n + Math.sin(h) * _;
2642
- c === 0 ? this.ctx.moveTo(m, u) : this.ctx.lineTo(m, u);
2643
- }
2644
- this.ctx.closePath(), this.ctx.fill(), this.ctx.strokeStyle = `rgba(57, 255, 20, ${a * 0.8})`, this.ctx.lineWidth = 2.5, this.ctx.beginPath();
2645
- for (let c = 0; c <= r; c++) {
2646
- const h = c * Math.PI * 2 / r, p = Math.sin(h * 6 + e * 4) * 8, d = i.radius * 0.75 + p, _ = l + Math.cos(h) * d, m = n + Math.sin(h) * d;
2647
- c === 0 ? this.ctx.moveTo(_, m) : this.ctx.lineTo(_, m);
2648
- }
2649
- this.ctx.closePath(), this.ctx.stroke();
2650
- for (let c = 0; c < 6; c++) {
2651
- const h = c * Math.PI * 0.33 + e * 1.5, p = i.radius * 0.5 * (0.3 + Math.sin(e * 2 + c) * 0.3), d = l + Math.cos(h) * p, _ = n + Math.sin(h) * p, m = 3 + Math.sin(e * 3 + c) * 2;
2652
- this.ctx.fillStyle = `rgba(255, 255, 255, ${a * 0.8})`, this.ctx.beginPath(), this.ctx.arc(d, _, Math.max(1, m), 0, Math.PI * 2), this.ctx.fill();
2653
- }
2654
- this.ctx.restore();
2618
+ if (!this.ctx) return;
2619
+ const i = this.themeManager.getPalette();
2620
+ for (const s of t) {
2621
+ const a = s.duration / s.maxDuration, n = Math.min(0.7, (1 - a) * 0.7), { x: o, y: r } = s.position;
2622
+ this.ctx.save();
2623
+ const c = this.ctx.createRadialGradient(o, r, 10, o, r, s.radius * 1.2);
2624
+ c.addColorStop(0, this.colorWithAlpha(i.sporeColor, n * 0.9)), c.addColorStop(0.4, `rgba(16, 185, 129, ${n * 0.65})`), c.addColorStop(0.8, `rgba(5, 80, 45, ${n * 0.35})`), c.addColorStop(1, "rgba(2, 35, 15, 0)"), this.ctx.fillStyle = c, this.ctx.shadowColor = i.sporeColor, this.ctx.shadowBlur = 30, this.ctx.beginPath();
2625
+ const l = 70;
2626
+ for (let h = 0; h <= l; h++) {
2627
+ const u = h * Math.PI * 2 / l, d = Math.sin(u * 5 + e * 3) * 14, g = Math.cos(u * 3 - e * 2) * 9, b = s.radius + d + g, f = o + Math.cos(u) * b, _ = r + Math.sin(u) * b;
2628
+ h === 0 ? this.ctx.moveTo(f, _) : this.ctx.lineTo(f, _);
2629
+ }
2630
+ this.ctx.closePath(), this.ctx.fill(), this.ctx.strokeStyle = this.colorWithAlpha(i.sporeColor, n * 0.8), this.ctx.lineWidth = 2.5, this.ctx.beginPath();
2631
+ for (let h = 0; h <= l; h++) {
2632
+ const u = h * Math.PI * 2 / l, d = Math.sin(u * 6 + e * 4) * 8, g = s.radius * 0.75 + d, b = o + Math.cos(u) * g, f = r + Math.sin(u) * g;
2633
+ h === 0 ? this.ctx.moveTo(b, f) : this.ctx.lineTo(b, f);
2655
2634
  }
2635
+ this.ctx.closePath(), this.ctx.stroke();
2636
+ for (let h = 0; h < 6; h++) {
2637
+ const u = h * Math.PI * 0.33 + e * 1.5, d = s.radius * 0.5 * (0.3 + Math.sin(e * 2 + h) * 0.3), g = o + Math.cos(u) * d, b = r + Math.sin(u) * d, f = 3 + Math.sin(e * 3 + h) * 2;
2638
+ this.ctx.fillStyle = `rgba(255, 255, 255, ${n * 0.8})`, this.ctx.beginPath(), this.ctx.arc(g, b, Math.max(1, f), 0, Math.PI * 2), this.ctx.fill();
2639
+ }
2640
+ this.ctx.restore();
2641
+ }
2656
2642
  }
2657
2643
  drawShockwaves(t) {
2658
2644
  if (!this.ctx) return;
@@ -2676,13 +2662,13 @@ class _t {
2676
2662
  if (!this.ctx) return;
2677
2663
  const i = this.themeManager.getPalette();
2678
2664
  for (const s of t) {
2679
- const a = Math.min(0.85, (1 - s.duration / s.maxDuration) * 0.85), l = s.radius * (1 - s.duration / s.maxDuration * 0.1), { x: n, y: o } = s.position;
2665
+ const a = Math.min(0.85, (1 - s.duration / s.maxDuration) * 0.85), n = s.radius * (1 - s.duration / s.maxDuration * 0.1), { x: o, y: r } = s.position;
2680
2666
  this.ctx.save();
2681
- const r = this.ctx.createRadialGradient(n, o, 0, n, o, 60);
2682
- r.addColorStop(0, this.colorWithAlpha(i.vortexCore, a)), r.addColorStop(0.7, this.colorWithAlpha(i.vortexMid, a)), r.addColorStop(1, `rgba(157, 78, 221, ${a * 0.5})`), this.ctx.fillStyle = r, this.ctx.beginPath(), this.ctx.arc(n, o, 60, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = i.parasiteColor, this.ctx.lineWidth = 5, this.ctx.shadowColor = i.parasiteColor, this.ctx.shadowBlur = 32;
2683
- for (let c = 1; c <= 5; c++) {
2684
- const h = l * c / 5, p = e * c * 2.2;
2685
- this.ctx.beginPath(), this.ctx.arc(n, o, h, p, p + Math.PI * 1.5), this.ctx.stroke();
2667
+ const c = this.ctx.createRadialGradient(o, r, 0, o, r, 60);
2668
+ c.addColorStop(0, this.colorWithAlpha(i.vortexCore, a)), c.addColorStop(0.7, this.colorWithAlpha(i.vortexMid, a)), c.addColorStop(1, this.colorWithAlpha(i.parasiteColor, a * 0.5)), this.ctx.fillStyle = c, this.ctx.beginPath(), this.ctx.arc(o, r, 60, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = i.parasiteColor, this.ctx.lineWidth = 5, this.ctx.shadowColor = i.parasiteColor, this.ctx.shadowBlur = 32;
2669
+ for (let l = 1; l <= 5; l++) {
2670
+ const h = n * l / 5, u = e * l * 2.2;
2671
+ this.ctx.beginPath(), this.ctx.arc(o, r, h, u, u + Math.PI * 1.5), this.ctx.stroke();
2686
2672
  }
2687
2673
  this.ctx.restore();
2688
2674
  }
@@ -2691,10 +2677,10 @@ class _t {
2691
2677
  if (!this.ctx) return;
2692
2678
  const a = this.ctx.createRadialGradient(t / 2, e / 2, 40, t / 2, e / 2, Math.max(t, e));
2693
2679
  a.addColorStop(0, s.bgInner), a.addColorStop(0.5, s.bgMid), a.addColorStop(1, s.bgOuter), this.ctx.fillStyle = a, this.ctx.fillRect(0, 0, t, e);
2694
- const l = t * 0.35 + Math.sin(i * 0.3) * 60, n = e * 0.45 + Math.cos(i * 0.25) * 50, o = this.ctx.createRadialGradient(l, n, 20, l, n, 380);
2695
- o.addColorStop(0, s.nebulaCyan), o.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = o, this.ctx.beginPath(), this.ctx.arc(l, n, 380, 0, Math.PI * 2), this.ctx.fill();
2696
- const r = t * 0.68 + Math.cos(i * 0.35) * 50, c = e * 0.55 + Math.sin(i * 0.3) * 45, h = this.ctx.createRadialGradient(r, c, 20, r, c, 420);
2697
- h.addColorStop(0, s.nebulaMagenta), h.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = h, this.ctx.beginPath(), this.ctx.arc(r, c, 420, 0, Math.PI * 2), this.ctx.fill();
2680
+ const n = t * 0.35 + Math.sin(i * 0.3) * 60, o = e * 0.45 + Math.cos(i * 0.25) * 50, r = this.ctx.createRadialGradient(n, o, 20, n, o, 380);
2681
+ r.addColorStop(0, s.nebulaCyan), r.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = r, this.ctx.beginPath(), this.ctx.arc(n, o, 380, 0, Math.PI * 2), this.ctx.fill();
2682
+ const c = t * 0.68 + Math.cos(i * 0.35) * 50, l = e * 0.55 + Math.sin(i * 0.3) * 45, h = this.ctx.createRadialGradient(c, l, 20, c, l, 420);
2683
+ h.addColorStop(0, s.nebulaMagenta), h.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = h, this.ctx.beginPath(), this.ctx.arc(c, l, 420, 0, Math.PI * 2), this.ctx.fill();
2698
2684
  }
2699
2685
  drawCoreFilaments(t, e) {
2700
2686
  if (!this.ctx) return;
@@ -2703,68 +2689,93 @@ class _t {
2703
2689
  for (let s = 0; s < i; s++) {
2704
2690
  const a = t.collectedPowerUpColors[s] || e.nebulaCyan;
2705
2691
  this.ctx.strokeStyle = a;
2706
- const l = s * Math.PI * 2 / i + t.pulsePhase * 0.12, n = 85 + Math.sin(t.pulsePhase * 1.5 + s) * 25, o = t.position.x + Math.cos(l) * n, r = t.position.y + Math.sin(l) * n, c = t.position.x + Math.cos(l + 0.4) * 48, h = t.position.y + Math.sin(l + 0.4) * 48;
2707
- this.ctx.beginPath(), this.ctx.moveTo(t.position.x, t.position.y), this.ctx.quadraticCurveTo(c, h, o, r), this.ctx.stroke(), this.ctx.fillStyle = a, this.ctx.beginPath(), this.ctx.arc(o, r, 3.5, 0, Math.PI * 2), this.ctx.fill();
2692
+ const n = s * Math.PI * 2 / i + t.pulsePhase * 0.12, o = 85 + Math.sin(t.pulsePhase * 1.5 + s) * 25, r = t.position.x + Math.cos(n) * o, c = t.position.y + Math.sin(n) * o, l = t.position.x + Math.cos(n + 0.4) * 48, h = t.position.y + Math.sin(n + 0.4) * 48;
2693
+ this.ctx.beginPath(), this.ctx.moveTo(t.position.x, t.position.y), this.ctx.quadraticCurveTo(l, h, r, c), this.ctx.stroke(), this.ctx.fillStyle = a, this.ctx.beginPath(), this.ctx.arc(r, c, 3.5, 0, Math.PI * 2), this.ctx.fill();
2708
2694
  }
2709
2695
  }
2710
- drawOrganicConsciousnessCore(t, e) {
2696
+ drawAutoDeflectionBeams(t, e, i, s) {
2697
+ if (this.ctx)
2698
+ for (const a of e) {
2699
+ if (!a.active || a.psionicDeflectionTimer <= 0) continue;
2700
+ const n = Math.min(1, a.psionicDeflectionTimer / 0.24), o = a.position.x - t.x, r = a.position.y - t.y, c = Math.hypot(o, r);
2701
+ if (c <= 0) continue;
2702
+ const l = -r / c, h = o / c, u = Math.sin(i * 18 + a.position.x * 0.02) * 10, d = (t.x + a.position.x) / 2 + l * u, g = (t.y + a.position.y) / 2 + h * u;
2703
+ this.ctx.save(), this.ctx.globalAlpha = n, this.ctx.strokeStyle = s.tendrilMagenta, this.ctx.lineWidth = 6, this.ctx.shadowColor = s.tendrilMagenta, this.ctx.shadowBlur = 18, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y), this.ctx.quadraticCurveTo(d, g, a.position.x, a.position.y), this.ctx.stroke(), this.ctx.strokeStyle = s.effectHighlight, this.ctx.lineWidth = 1.8, this.ctx.shadowColor = s.tendrilCyan, this.ctx.shadowBlur = 10, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y), this.ctx.quadraticCurveTo(d, g, a.position.x, a.position.y), this.ctx.stroke(), this.ctx.restore();
2704
+ }
2705
+ }
2706
+ drawOrganicConsciousnessCore(t, e, i, s) {
2711
2707
  if (!this.ctx) return;
2712
- const { x: i, y: s } = t.position;
2708
+ const { x: a, y: n } = t.position;
2713
2709
  if (this.ctx.save(), t.invulnerabilityTimer > 0) {
2714
- const r = Math.floor(t.invulnerabilityTimer * 20) % 2 === 0;
2715
- this.ctx.globalAlpha = r ? 0.3 : 0.9;
2710
+ const x = Math.floor(t.invulnerabilityTimer * 20) % 2 === 0;
2711
+ this.ctx.globalAlpha = x ? 0.3 : 0.9;
2716
2712
  }
2717
- const a = t.getColorHex();
2718
- for (let r = 0; r < t.integrity; r++) {
2719
- const c = t.radius + 16 + r * 12;
2720
- this.ctx.strokeStyle = a, this.ctx.lineWidth = 2.2, this.ctx.shadowColor = a, this.ctx.shadowBlur = 12, this.ctx.beginPath();
2721
- const h = 60;
2722
- for (let p = 0; p <= h; p++) {
2723
- const d = p * Math.PI * 2 / h, _ = c + Math.sin(d * 4 + t.pulsePhase * 2 + r) * 3, m = i + Math.cos(d) * _, u = s + Math.sin(d) * _;
2724
- p === 0 ? this.ctx.moveTo(m, u) : this.ctx.lineTo(m, u);
2713
+ const o = t.getColorHex();
2714
+ for (let x = 0; x < t.integrity; x++) {
2715
+ const y = t.radius + 16 + x * 12;
2716
+ this.ctx.strokeStyle = o, this.ctx.lineWidth = 2.2, this.ctx.shadowColor = o, this.ctx.shadowBlur = 12, this.ctx.beginPath();
2717
+ const m = 60;
2718
+ for (let v = 0; v <= m; v++) {
2719
+ const A = v * Math.PI * 2 / m, S = y + Math.sin(A * 4 + t.pulsePhase * 2 + x) * 3, L = a + Math.cos(A) * S, T = n + Math.sin(A) * S;
2720
+ v === 0 ? this.ctx.moveTo(L, T) : this.ctx.lineTo(L, T);
2725
2721
  }
2726
2722
  this.ctx.closePath(), this.ctx.stroke();
2727
2723
  }
2728
2724
  if (t.repairProgress > 0) {
2729
- const r = t.integrity, c = t.radius + 16 + r * 12;
2730
- this.ctx.save(), this.ctx.globalAlpha = Math.max(0.15, t.repairProgress), this.ctx.strokeStyle = a, this.ctx.lineWidth = 2, this.ctx.shadowColor = a, this.ctx.shadowBlur = 8, this.ctx.setLineDash([6, 4]), this.ctx.beginPath();
2731
- const h = 60;
2732
- for (let p = 0; p <= h; p++) {
2733
- const d = p * Math.PI * 2 / h, _ = c + Math.sin(d * 4 + t.pulsePhase * 2 + r) * 3, m = i + Math.cos(d) * _, u = s + Math.sin(d) * _;
2734
- p === 0 ? this.ctx.moveTo(m, u) : this.ctx.lineTo(m, u);
2725
+ const x = t.integrity, y = t.radius + 16 + x * 12;
2726
+ this.ctx.save(), this.ctx.globalAlpha = Math.max(0.15, t.repairProgress), this.ctx.strokeStyle = o, this.ctx.lineWidth = 2, this.ctx.shadowColor = o, this.ctx.shadowBlur = 8, this.ctx.setLineDash([6, 4]), this.ctx.beginPath();
2727
+ const m = 60;
2728
+ for (let v = 0; v <= m; v++) {
2729
+ const A = v * Math.PI * 2 / m, S = y + Math.sin(A * 4 + t.pulsePhase * 2 + x) * 3, L = a + Math.cos(A) * S, T = n + Math.sin(A) * S;
2730
+ v === 0 ? this.ctx.moveTo(L, T) : this.ctx.lineTo(L, T);
2735
2731
  }
2736
2732
  this.ctx.closePath(), this.ctx.stroke(), this.ctx.restore();
2737
2733
  }
2738
- const l = this.ctx.createRadialGradient(i, s, 5, i, s, t.radius * 2.2);
2739
- l.addColorStop(0, a), l.addColorStop(0.5, e.nebulaMagenta), l.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = l, this.ctx.beginPath(), this.ctx.arc(i, s, t.radius * 2.2, 0, Math.PI * 2), this.ctx.fill(), this.ctx.save(), this.ctx.translate(i, s), this.ctx.strokeStyle = a, this.ctx.lineWidth = 3, this.ctx.fillStyle = a, this.ctx.shadowColor = a, this.ctx.shadowBlur = 20, this.ctx.beginPath();
2740
- const n = 80;
2741
- for (let r = 0; r <= n; r++) {
2742
- const c = r * Math.PI * 2 / n, h = t.getMembraneRadius(c), p = Math.cos(c) * h, d = Math.sin(c) * h;
2743
- r === 0 ? this.ctx.moveTo(p, d) : this.ctx.lineTo(p, d);
2734
+ const r = this.ctx.createRadialGradient(a, n, 5, a, n, t.radius * 2.2);
2735
+ r.addColorStop(0, o), r.addColorStop(0.5, e.nebulaMagenta), r.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = r, this.ctx.beginPath(), this.ctx.arc(a, n, t.radius * 2.2, 0, Math.PI * 2), this.ctx.fill(), this.ctx.save(), this.ctx.translate(a, n), this.ctx.strokeStyle = o, this.ctx.lineWidth = 3, this.ctx.fillStyle = o, this.ctx.shadowColor = o, this.ctx.shadowBlur = 20, this.ctx.beginPath();
2736
+ const c = 80;
2737
+ for (let x = 0; x <= c; x++) {
2738
+ const y = x * Math.PI * 2 / c, m = t.getMembraneRadius(y), v = Math.cos(y) * m, A = Math.sin(y) * m;
2739
+ x === 0 ? this.ctx.moveTo(v, A) : this.ctx.lineTo(v, A);
2744
2740
  }
2745
- this.ctx.closePath(), this.ctx.fill(), this.ctx.stroke();
2746
- const o = t.powerUpCount;
2747
- if (o > 0) {
2748
- this.ctx.strokeStyle = e.effectHighlight, this.ctx.lineWidth = 1.8, this.ctx.shadowColor = a, this.ctx.shadowBlur = 10;
2749
- for (let r = 0; r < o; r++) {
2750
- const c = r * Math.PI * 2 / o + t.pulsePhase * 0.4, h = t.radius * (0.45 + Math.sin(t.pulsePhase * 2 + r) * 0.15);
2741
+ this.ctx.closePath(), this.ctx.fillStyle = o, this.ctx.fill(), this.ctx.strokeStyle = o, this.ctx.lineWidth = 3, this.ctx.stroke();
2742
+ const l = t.powerUpCount;
2743
+ if (l > 0) {
2744
+ this.ctx.strokeStyle = e.effectHighlight, this.ctx.lineWidth = 1.8, this.ctx.shadowColor = o, this.ctx.shadowBlur = 10;
2745
+ for (let x = 0; x < l; x++) {
2746
+ const y = x * Math.PI * 2 / l + t.pulsePhase * 0.4, m = t.radius * (0.45 + Math.sin(t.pulsePhase * 2 + x) * 0.15);
2751
2747
  this.ctx.beginPath(), this.ctx.moveTo(0, 0);
2752
- const p = Math.cos(c + Math.sin(t.pulsePhase + r) * 0.4) * (h * 0.6), d = Math.sin(c + Math.cos(t.pulsePhase + r) * 0.4) * (h * 0.6), _ = Math.cos(c) * h, m = Math.sin(c) * h;
2753
- this.ctx.quadraticCurveTo(p, d, _, m), this.ctx.stroke(), this.ctx.fillStyle = a, this.ctx.beginPath(), this.ctx.arc(_, m, 2.5, 0, Math.PI * 2), this.ctx.fill();
2748
+ const v = Math.cos(y + Math.sin(t.pulsePhase + x) * 0.4) * (m * 0.6), A = Math.sin(y + Math.cos(t.pulsePhase + x) * 0.4) * (m * 0.6), S = Math.cos(y) * m, L = Math.sin(y) * m;
2749
+ this.ctx.quadraticCurveTo(v, A, S, L), this.ctx.stroke(), this.ctx.fillStyle = o, this.ctx.beginPath(), this.ctx.arc(S, L, 2.5, 0, Math.PI * 2), this.ctx.fill();
2754
2750
  }
2755
2751
  }
2756
- this.ctx.fillStyle = e.effectHighlight, this.ctx.beginPath(), this.ctx.arc(0, 0, t.radius * 0.3, 0, Math.PI * 2), this.ctx.fill(), this.ctx.restore(), this.ctx.restore();
2752
+ const h = Math.max(0, Math.min(1, i)), u = Math.max(0, Math.min(1, s)), g = t.radius * 0.42 * (1 + u * 0.1), b = g - h * g * 2, f = Math.sin(t.pulsePhase * 5) * 1.5, _ = this.ctx.createRadialGradient(0, -g * 0.35, 1, 0, 0, g);
2753
+ if (_.addColorStop(0, e.effectHighlight), _.addColorStop(0.7, this.colorWithAlpha(e.effectHighlight, 0.92)), _.addColorStop(1, this.colorWithAlpha(e.tendrilCyan, 0.55)), this.ctx.fillStyle = _, this.ctx.shadowColor = u > 0 ? o : e.effectHighlight, this.ctx.shadowBlur = 16 + u * 32, this.ctx.beginPath(), this.ctx.arc(0, 0, g, 0, Math.PI * 2), this.ctx.fill(), h > 0) {
2754
+ this.ctx.save(), this.ctx.beginPath(), this.ctx.arc(0, 0, g, 0, Math.PI * 2), this.ctx.clip();
2755
+ const x = this.ctx.createLinearGradient(0, b, 0, g);
2756
+ x.addColorStop(0, this.colorWithAlpha(o, 1)), x.addColorStop(0.7, this.colorWithAlpha(o, 0.9)), x.addColorStop(1, this.colorWithAlpha(e.nebulaMagenta, 0.78)), this.ctx.fillStyle = x, this.ctx.fillRect(-g, b, g * 2, g * 2), this.ctx.strokeStyle = e.effectHighlight, this.ctx.lineWidth = 2, this.ctx.shadowColor = o, this.ctx.shadowBlur = 10, this.ctx.beginPath(), this.ctx.moveTo(-g, b + f), this.ctx.quadraticCurveTo(0, b - 2 - f, g, b + f), this.ctx.stroke(), this.ctx.restore();
2757
+ }
2758
+ if (this.ctx.strokeStyle = this.colorWithAlpha(o, 0.8), this.ctx.lineWidth = 2.5, this.ctx.shadowColor = o, this.ctx.shadowBlur = 12 + u * 24, this.ctx.beginPath(), this.ctx.arc(0, 0, g + 4, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * h), this.ctx.stroke(), u > 0) {
2759
+ const x = 1 - u;
2760
+ this.ctx.save(), this.ctx.globalAlpha = u * 0.75, this.ctx.strokeStyle = e.effectHighlight, this.ctx.lineWidth = 2 + u * 2, this.ctx.shadowColor = o, this.ctx.shadowBlur = 18, this.ctx.beginPath(), this.ctx.arc(0, 0, g + 10 + x * 28, 0, Math.PI * 2), this.ctx.stroke();
2761
+ for (let y = 0; y < 8; y++) {
2762
+ const m = y * Math.PI * 2 / 8 + t.pulsePhase * 0.4, v = g + 8, A = v + 10 + x * 16;
2763
+ this.ctx.beginPath(), this.ctx.moveTo(Math.cos(m) * v, Math.sin(m) * v), this.ctx.lineTo(Math.cos(m) * A, Math.sin(m) * A), this.ctx.stroke();
2764
+ }
2765
+ this.ctx.restore();
2766
+ }
2767
+ this.ctx.restore(), this.ctx.restore();
2757
2768
  }
2758
2769
  drawPsionicTendril(t, e, i) {
2759
2770
  if (!this.ctx) return;
2760
2771
  this.ctx.save();
2761
2772
  const s = e.x - t.x, a = e.y - t.y;
2762
2773
  this.ctx.strokeStyle = i.tendrilMagenta, this.ctx.lineWidth = 9, this.ctx.shadowColor = i.tendrilMagenta, this.ctx.shadowBlur = 20, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y);
2763
- const l = (t.x + e.x) / 2 + Math.sin(Date.now() * 0.02) * 16, n = (t.y + e.y) / 2 + Math.cos(Date.now() * 0.02) * 16;
2764
- this.ctx.quadraticCurveTo(l, n, e.x, e.y), this.ctx.stroke(), this.ctx.strokeStyle = i.tendrilCyan, this.ctx.lineWidth = 3.5, this.ctx.shadowColor = i.tendrilCyan, this.ctx.shadowBlur = 12, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y), this.ctx.lineTo(e.x, e.y), this.ctx.stroke();
2765
- for (let o = 0; o < 6; o++) {
2766
- const r = Math.random(), c = t.x + s * r + (Math.random() - 0.5) * 24, h = t.y + a * r + (Math.random() - 0.5) * 24;
2767
- this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(c, h, 2.5 + Math.random() * 2.5, 0, Math.PI * 2), this.ctx.fill();
2774
+ const n = (t.x + e.x) / 2 + Math.sin(Date.now() * 0.02) * 16, o = (t.y + e.y) / 2 + Math.cos(Date.now() * 0.02) * 16;
2775
+ this.ctx.quadraticCurveTo(n, o, e.x, e.y), this.ctx.stroke(), this.ctx.strokeStyle = i.tendrilCyan, this.ctx.lineWidth = 3.5, this.ctx.shadowColor = i.tendrilCyan, this.ctx.shadowBlur = 12, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y), this.ctx.lineTo(e.x, e.y), this.ctx.stroke();
2776
+ for (let r = 0; r < 6; r++) {
2777
+ const c = Math.random(), l = t.x + s * c + (Math.random() - 0.5) * 24, h = t.y + a * c + (Math.random() - 0.5) * 24;
2778
+ this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(l, h, 2.5 + Math.random() * 2.5, 0, Math.PI * 2), this.ctx.fill();
2768
2779
  }
2769
2780
  this.ctx.restore();
2770
2781
  }
@@ -2772,8 +2783,8 @@ class _t {
2772
2783
  if (this.ctx)
2773
2784
  for (const a of i) {
2774
2785
  if (!a.active || a.isGrabbed || a.isThrown || a.wasManipulated || a.isDying) continue;
2775
- const l = e.x - a.position.x, n = e.y - a.position.y;
2776
- if (Math.hypot(l, n) <= a.getHitRadius()) {
2786
+ const n = e.x - a.position.x, o = e.y - a.position.y;
2787
+ if (Math.hypot(n, o) <= a.getHitRadius()) {
2777
2788
  this.ctx.strokeStyle = s.tendrilMagenta, this.ctx.lineWidth = 2.5, this.ctx.beginPath(), this.ctx.moveTo(t.x, t.y), this.ctx.lineTo(a.position.x, a.position.y), this.ctx.stroke();
2778
2789
  break;
2779
2790
  }
@@ -2783,73 +2794,31 @@ class _t {
2783
2794
  if (this.ctx)
2784
2795
  for (const s of t) {
2785
2796
  if (!s.active) continue;
2786
- let a = s.color;
2787
- s.type === "piercer" ? a = i.piercerColor : s.type === "spore" ? a = i.sporeColor : s.type === "cluster" ? a = i.clusterColor : s.type === "parasite" ? a = i.parasiteColor : s.type === "micro" && (a = i.microColor);
2788
- const { x: l, y: n } = s.position;
2789
- if (this.ctx.save(), this.ctx.translate(l, n), s.wasManipulated && !s.isDying) {
2790
- const o = s.radius * 1.5 + Math.sin(e * 6) * 4;
2791
- this.ctx.strokeStyle = i.tendrilMagenta, this.ctx.lineWidth = 2.5, this.ctx.shadowColor = i.tendrilMagenta, this.ctx.shadowBlur = 18, this.ctx.beginPath(), this.ctx.arc(0, 0, o, 0, Math.PI * 2), this.ctx.stroke();
2792
- const r = s.radius * 1.8 + Math.cos(e * 8) * 3;
2793
- this.ctx.strokeStyle = i.tendrilCyan, this.ctx.lineWidth = 1.5, this.ctx.beginPath(), this.ctx.arc(0, 0, r, 0, Math.PI * 2), this.ctx.stroke();
2797
+ const a = s.color, { x: n, y: o } = s.position;
2798
+ if (this.ctx.save(), this.ctx.translate(n, o), s.wasManipulated && !s.isDying) {
2799
+ const r = s.radius * 1.5 + Math.sin(e * 6) * 4;
2800
+ this.ctx.strokeStyle = i.tendrilMagenta, this.ctx.lineWidth = 2.5, this.ctx.shadowColor = i.tendrilMagenta, this.ctx.shadowBlur = 18, this.ctx.beginPath(), this.ctx.arc(0, 0, r, 0, Math.PI * 2), this.ctx.stroke();
2801
+ const c = s.radius * 1.8 + Math.cos(e * 8) * 3;
2802
+ this.ctx.strokeStyle = i.tendrilCyan, this.ctx.lineWidth = 1.5, this.ctx.beginPath(), this.ctx.arc(0, 0, c, 0, Math.PI * 2), this.ctx.stroke();
2794
2803
  }
2795
2804
  if (s.isDying) {
2796
- const o = s.deathTimer / s.maxDeathTimer, r = Math.max(0, 1 - o);
2797
- if (this.ctx.globalAlpha = r, s.deathType === "dissolve") {
2798
- const c = s.radius * (1 - o * 0.7);
2799
- this.ctx.fillStyle = i.sporeColor, this.ctx.shadowColor = i.sporeColor, this.ctx.shadowBlur = 20, this.ctx.beginPath(), this.ctx.arc(0, 0, c, 0, Math.PI * 2), this.ctx.fill();
2805
+ const r = s.deathTimer / s.maxDeathTimer, c = Math.max(0, 1 - r);
2806
+ if (this.ctx.globalAlpha = c, s.deathType === "dissolve") {
2807
+ const l = s.radius * (1 - r * 0.7);
2808
+ this.ctx.fillStyle = i.sporeColor, this.ctx.shadowColor = i.sporeColor, this.ctx.shadowBlur = 20, this.ctx.beginPath(), this.ctx.arc(0, 0, l, 0, Math.PI * 2), this.ctx.fill();
2800
2809
  for (let h = 0; h < 4; h++) {
2801
- const p = h * Math.PI * 0.5 + o * 4, d = c * 0.8 * o;
2802
- this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(Math.cos(p) * d, Math.sin(p) * d, 3, 0, Math.PI * 2), this.ctx.fill();
2810
+ const u = h * Math.PI * 0.5 + r * 4, d = l * 0.8 * r;
2811
+ this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(Math.cos(u) * d, Math.sin(u) * d, 3, 0, Math.PI * 2), this.ctx.fill();
2803
2812
  }
2804
2813
  this.ctx.restore();
2805
2814
  continue;
2806
2815
  } else if (s.deathType === "sliced") {
2807
- const c = o * 24, h = s.sliceAngle + Math.PI / 2, p = Math.cos(h) * c, d = Math.sin(h) * c;
2808
- this.ctx.shadowColor = "#ff2a5f", this.ctx.shadowBlur = 22, this.ctx.fillStyle = a, this.ctx.save(), this.ctx.translate(p, d), this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius * 0.8, 0, Math.PI), this.ctx.fill(), this.ctx.restore(), this.ctx.save(), this.ctx.translate(-p, -d), this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius * 0.8, Math.PI, Math.PI * 2), this.ctx.fill(), this.ctx.restore(), this.ctx.restore();
2816
+ const l = r * 24, h = s.sliceAngle + Math.PI / 2, u = Math.cos(h) * l, d = Math.sin(h) * l;
2817
+ this.ctx.shadowColor = i.piercerColor, this.ctx.shadowBlur = 22, this.ctx.fillStyle = a, this.ctx.save(), this.ctx.translate(u, d), this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius * 0.8, 0, Math.PI), this.ctx.fill(), this.ctx.restore(), this.ctx.save(), this.ctx.translate(-u, -d), this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius * 0.8, Math.PI, Math.PI * 2), this.ctx.fill(), this.ctx.restore(), this.ctx.restore();
2809
2818
  continue;
2810
2819
  }
2811
2820
  }
2812
- if (this.ctx.shadowColor = a, this.ctx.shadowBlur = 16, this.ctx.fillStyle = a, s.type === "piercer") {
2813
- this.ctx.rotate(s.rotation), this.ctx.beginPath();
2814
- const o = 12;
2815
- for (let r = 0; r <= o; r++) {
2816
- const c = r * Math.PI * 2 / o, h = s.radius * 1.6 + Math.sin(c * 3 + e * 4) * 3, p = s.radius * 0.6 + Math.cos(c * 2 + e * 3) * 2, d = Math.cos(c) * h, _ = Math.sin(c) * p;
2817
- r === 0 ? this.ctx.moveTo(d, _) : this.ctx.lineTo(d, _);
2818
- }
2819
- this.ctx.closePath(), this.ctx.fill(), this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(4, 0, 4, 0, Math.PI * 2), this.ctx.fill();
2820
- } else if (s.type === "spore") {
2821
- this.ctx.beginPath();
2822
- const o = 8;
2823
- for (let r = 0; r < o; r++) {
2824
- const c = r * Math.PI * 2 / o, h = s.radius + Math.sin(c * 4 + e * 3 + s.position.x) * 4, p = Math.cos(c) * h, d = Math.sin(c) * h;
2825
- r === 0 ? this.ctx.moveTo(p, d) : this.ctx.lineTo(p, d);
2826
- }
2827
- this.ctx.closePath(), this.ctx.fill(), this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(-4, -4, 5, 0, Math.PI * 2), this.ctx.arc(5, 3, 4, 0, Math.PI * 2), this.ctx.fill();
2828
- } else if (s.type === "cluster") {
2829
- this.ctx.beginPath();
2830
- const o = Math.sin(e * 3) * 2;
2831
- this.ctx.arc(0, 0, s.radius * 0.55, 0, Math.PI * 2), this.ctx.arc(-9 + o, -7, s.radius * 0.42, 0, Math.PI * 2), this.ctx.arc(9 - o, -7, s.radius * 0.42, 0, Math.PI * 2), this.ctx.arc(0, 9 + o, s.radius * 0.45, 0, Math.PI * 2), this.ctx.fill();
2832
- } else if (s.type === "parasite") {
2833
- this.ctx.beginPath();
2834
- const o = 14;
2835
- for (let r = 0; r <= o; r++) {
2836
- const c = r * Math.PI * 2 / o, h = s.radius + Math.sin(c * 5 - e * 5) * 3, p = Math.cos(c) * h, d = Math.sin(c) * h;
2837
- r === 0 ? this.ctx.moveTo(p, d) : this.ctx.lineTo(p, d);
2838
- }
2839
- this.ctx.closePath(), this.ctx.fill(), this.ctx.fillStyle = i.bgOuter, this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius * 0.45, 0, Math.PI * 2), this.ctx.fill();
2840
- } else if (s.type === "heavy") {
2841
- this.ctx.rotate(s.rotation);
2842
- const o = A.getInstance().getConfig().shield.protectionArc, r = s.radius * 1.35, c = s.radius * 0.75;
2843
- this.ctx.beginPath(), this.ctx.arc(0, 0, r, -o / 2, o / 2, !1);
2844
- const h = Math.cos(o / 2) * c, p = Math.sin(o / 2) * c;
2845
- this.ctx.lineTo(h, p), this.ctx.arc(0, 0, c, o / 2, -o / 2, !0), this.ctx.closePath(), this.ctx.fillStyle = s.isStationaryShield ? i.corePrimary : a, this.ctx.shadowColor = i.corePrimary, this.ctx.shadowBlur = s.isStationaryShield ? 28 : 16, this.ctx.fill(), this.ctx.strokeStyle = i.effectHighlight, this.ctx.lineWidth = 3, this.ctx.beginPath(), this.ctx.arc(0, 0, r + Math.sin(e * 4) * 2, -o / 2 + 0.1, o / 2 - 0.1), this.ctx.stroke();
2846
- for (let d = -2; d <= 2; d++) {
2847
- const _ = d * o / 6, m = Math.cos(_) * (c + 6), u = Math.sin(_) * (c + 6);
2848
- this.ctx.fillStyle = i.effectHighlight, this.ctx.beginPath(), this.ctx.arc(m, u, 3, 0, Math.PI * 2), this.ctx.fill();
2849
- }
2850
- } else
2851
- this.ctx.beginPath(), this.ctx.arc(0, 0, s.radius, 0, Math.PI * 2), this.ctx.fill();
2852
- this.ctx.restore();
2821
+ this.enemyShapeRenderer.draw(this.ctx, s, e, i, a), this.ctx.restore();
2853
2822
  }
2854
2823
  }
2855
2824
  drawParticles(t) {
@@ -2867,66 +2836,60 @@ class _t {
2867
2836
  }
2868
2837
  drawOrbitingModifierOrbs(t, e, i, s = !0, a = 1) {
2869
2838
  if (!this.ctx) return;
2870
- const l = this.themeManager.getPalette(), n = this.themeManager.getTheme() === "light";
2871
- for (const o of t) {
2872
- if (!o.active) continue;
2873
- const { x: r, y: c } = o.position, h = o.definition.color, p = o.definition.rarity, d = p === "legendary" ? "#ff00ff" : p === "epic" ? "#ffd700" : p === "rare" ? "#9d4edd" : "#00f5ff", _ = o.radius + Math.sin(e * 6 + o.orbitAngle) * 3;
2874
- if (this.ctx.save(), this.ctx.shadowColor = d, this.ctx.shadowBlur = o.isGrabbed ? 36 : p === "legendary" ? 28 : 16, p === "legendary" || p === "epic") {
2875
- const g = _ + 12, f = this.ctx.createRadialGradient(r, c, _, r, c, g);
2876
- f.addColorStop(0, d), f.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = f, this.ctx.beginPath(), this.ctx.arc(r, c, g, 0, Math.PI * 2), this.ctx.fill();
2839
+ const n = this.themeManager.getPalette(), o = this.themeManager.getTheme() === "light";
2840
+ for (const r of t) {
2841
+ if (!r.active) continue;
2842
+ const { x: c, y: l } = r.position, h = r.definition.color, u = r.definition.rarity, d = At[u], g = r.radius + Math.sin(e * 6 + r.orbitAngle) * 3;
2843
+ if (this.ctx.save(), this.ctx.shadowColor = d, this.ctx.shadowBlur = r.isGrabbed ? 36 : u === "legendary" ? 28 : 16, u === "legendary" || u === "epic") {
2844
+ const _ = g + 12, x = this.ctx.createRadialGradient(c, l, g, c, l, _);
2845
+ x.addColorStop(0, d), x.addColorStop(1, "rgba(0, 0, 0, 0)"), this.ctx.fillStyle = x, this.ctx.beginPath(), this.ctx.arc(c, l, _, 0, Math.PI * 2), this.ctx.fill();
2877
2846
  }
2878
- const m = this.ctx.createRadialGradient(r, c, 2, r, c, _ * 1.4);
2879
- m.addColorStop(0, l.effectHighlight), m.addColorStop(0.4, h), m.addColorStop(1, n ? "rgba(23, 50, 77, 0.24)" : "rgba(0, 0, 0, 0.5)"), this.ctx.fillStyle = m, this.ctx.beginPath(), this.ctx.arc(r, c, _, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = d, this.ctx.lineWidth = p === "legendary" ? 3.5 : 2.5, this.ctx.beginPath(), this.ctx.arc(r, c, _ + 4, e * 3, e * 3 + Math.PI * 1.2), this.ctx.stroke(), (p === "legendary" || p === "epic") && (this.ctx.strokeStyle = n ? d : l.effectHighlight, this.ctx.lineWidth = 1.5, this.ctx.beginPath(), this.ctx.arc(r, c, _ + 8, -e * 2, -e * 2 + Math.PI * 0.9), this.ctx.stroke()), this.ctx.font = "900 14px Orbitron, sans-serif", this.ctx.textAlign = "center", this.ctx.textBaseline = "middle", this.ctx.fillStyle = l.effectInk, this.ctx.shadowColor = l.effectShadow, this.ctx.shadowBlur = 6, this.ctx.fillText(o.definition.badge, r, c);
2880
- const u = i ? i.translate(o.definition.titleKey) : o.definition.badge;
2881
- this.ctx.textBaseline = "alphabetic", this.ctx.font = "900 16px Orbitron, sans-serif", this.ctx.fillStyle = l.effectInk, this.ctx.shadowColor = d, this.ctx.shadowBlur = 14, this.ctx.fillText(u, r, c - _ - 14), this.ctx.restore();
2847
+ const b = this.ctx.createRadialGradient(c, l, 2, c, l, g * 1.4);
2848
+ b.addColorStop(0, n.effectHighlight), b.addColorStop(0.4, h), b.addColorStop(1, o ? "rgba(23, 50, 77, 0.24)" : "rgba(0, 0, 0, 0.5)"), this.ctx.fillStyle = b, this.ctx.beginPath(), this.ctx.arc(c, l, g, 0, Math.PI * 2), this.ctx.fill(), this.ctx.strokeStyle = d, this.ctx.lineWidth = u === "legendary" ? 3.5 : 2.5, this.ctx.beginPath(), this.ctx.arc(c, l, g + 4, e * 3, e * 3 + Math.PI * 1.2), this.ctx.stroke(), (u === "legendary" || u === "epic") && (this.ctx.strokeStyle = o ? d : n.effectHighlight, this.ctx.lineWidth = 1.5, this.ctx.beginPath(), this.ctx.arc(c, l, g + 8, -e * 2, -e * 2 + Math.PI * 0.9), this.ctx.stroke()), this.ctx.font = "900 14px Orbitron, sans-serif", this.ctx.textAlign = "center", this.ctx.textBaseline = "middle", this.ctx.fillStyle = n.effectInk, this.ctx.shadowColor = n.effectShadow, this.ctx.shadowBlur = 6, this.ctx.fillText(r.definition.badge, c, l);
2849
+ const f = i ? i.translate(r.definition.titleKey) : r.definition.badge;
2850
+ this.ctx.textBaseline = "alphabetic", this.ctx.font = "900 16px Orbitron, sans-serif", this.ctx.fillStyle = n.effectInk, this.ctx.shadowColor = d, this.ctx.shadowBlur = 14, this.ctx.fillText(f, c, l - g - 14), this.ctx.restore();
2882
2851
  }
2883
2852
  if (i && s) {
2884
- const o = this.canvas.width / 2, r = this.canvas.height / 2 + 210, c = i.translate("rerollButton", { count: a });
2885
- this.ctx.save(), this.ctx.font = "900 14px Orbitron, sans-serif", this.ctx.textAlign = "center", this.ctx.textBaseline = "middle", this.ctx.fillStyle = l.effectPanel, this.ctx.strokeStyle = l.tendrilCyan, this.ctx.lineWidth = 2, this.ctx.shadowColor = l.tendrilCyan, this.ctx.shadowBlur = 12;
2886
- const d = this.ctx.measureText(c).width + 20 * 2, _ = 38;
2887
- this.ctx.beginPath(), this.ctx.roundRect(o - d / 2, r - _ / 2, d, _, 8), this.ctx.fill(), this.ctx.stroke(), this.ctx.fillStyle = l.effectInk, this.ctx.shadowColor = l.effectShadow, this.ctx.shadowBlur = 4, this.ctx.fillText(c, o, r), this.ctx.restore();
2853
+ const r = this.canvas.width / 2, c = this.canvas.height / 2 + 210, l = i.translate("rerollButton", { count: a });
2854
+ this.ctx.save(), this.ctx.font = "900 14px Orbitron, sans-serif", this.ctx.textAlign = "center", this.ctx.textBaseline = "middle", this.ctx.fillStyle = n.effectPanel, this.ctx.strokeStyle = n.tendrilCyan, this.ctx.lineWidth = 2, this.ctx.shadowColor = n.tendrilCyan, this.ctx.shadowBlur = 12;
2855
+ const d = this.ctx.measureText(l).width + 20 * 2, g = 38;
2856
+ this.ctx.beginPath(), this.ctx.roundRect(r - d / 2, c - g / 2, d, g, 8), this.ctx.fill(), this.ctx.stroke(), this.ctx.fillStyle = n.effectInk, this.ctx.shadowColor = n.effectShadow, this.ctx.shadowBlur = 4, this.ctx.fillText(l, r, c), this.ctx.restore();
2888
2857
  }
2889
2858
  }
2890
2859
  colorWithAlpha(t, e) {
2891
2860
  const i = t.replace("#", "");
2892
2861
  if (i.length !== 6) return t;
2893
- const s = Number.parseInt(i.slice(0, 2), 16), a = Number.parseInt(i.slice(2, 4), 16), l = Number.parseInt(i.slice(4, 6), 16);
2894
- return `rgba(${s}, ${a}, ${l}, ${e})`;
2862
+ const s = Number.parseInt(i.slice(0, 2), 16), a = Number.parseInt(i.slice(2, 4), 16), n = Number.parseInt(i.slice(4, 6), 16);
2863
+ return `rgba(${s}, ${a}, ${n}, ${e})`;
2895
2864
  }
2896
2865
  }
2897
- class mt {
2866
+ class St {
2898
2867
  container;
2899
- i18n;
2900
- waveLabelElement;
2901
- progressSlot;
2902
2868
  comboElement;
2903
2869
  settingsButton;
2904
- constructor(t, e, i) {
2905
- this.i18n = e, this.container = document.createElement("div"), this.container.className = "axon-hud", this.waveLabelElement = document.createElement("div"), this.waveLabelElement.className = "axon-hud-wave", this.progressSlot = document.createElement("div"), this.progressSlot.className = "axon-hud-level-slot", this.waveLabelElement.appendChild(this.progressSlot), this.comboElement = document.createElement("div"), this.comboElement.className = "axon-hud-combo", this.settingsButton = document.createElement("button"), this.settingsButton.className = "axon-hud-settings-btn", this.settingsButton.setAttribute("aria-label", "Settings"), this.settingsButton.innerHTML = `
2870
+ constructor(t, e) {
2871
+ this.container = document.createElement("div"), this.container.className = "axon-hud", this.comboElement = document.createElement("div"), this.comboElement.className = "axon-hud-combo", this.settingsButton = document.createElement("button"), this.settingsButton.className = "axon-hud-settings-btn", this.settingsButton.setAttribute("aria-label", "Settings"), this.settingsButton.innerHTML = `
2906
2872
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
2907
2873
  <circle cx="12" cy="12" r="3"></circle>
2908
2874
  <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
2909
2875
  </svg>
2910
- `, this.settingsButton.addEventListener("click", i), this.renderLabels(), this.container.appendChild(this.waveLabelElement), this.container.appendChild(this.comboElement), this.container.appendChild(this.settingsButton), t.appendChild(this.container);
2876
+ `, this.settingsButton.addEventListener("click", e), this.renderLabels(), this.container.appendChild(this.comboElement), this.container.appendChild(this.settingsButton), t.appendChild(this.container);
2911
2877
  }
2912
2878
  renderLabels() {
2913
- let t = this.waveLabelElement.querySelector(".axon-level-title");
2914
- t ? t.textContent = this.i18n.translate("waveLabel") : (t = document.createElement("div"), t.className = "axon-level-title", t.textContent = this.i18n.translate("waveLabel"), this.waveLabelElement.insertBefore(t, this.waveLabelElement.firstChild));
2915
- let e = this.waveLabelElement.querySelector("#axon-wave-val");
2916
- e || (e = document.createElement("span"), e.id = "axon-wave-val", e.textContent = "1", this.waveLabelElement.insertBefore(e, this.progressSlot)), this.comboElement.innerHTML = '<span id="axon-combo-val"></span>';
2879
+ this.comboElement.innerHTML = '<span id="axon-combo-val"></span>';
2917
2880
  }
2918
- update(t, e) {
2919
- const i = this.container.querySelector("#axon-wave-val"), s = this.container.querySelector("#axon-combo-val");
2920
- i && (i.textContent = t.toString()), s && (e >= 2 ? (s.textContent = `x${e}`, s.style.display = "block") : s.style.display = "none");
2881
+ update(t) {
2882
+ const e = this.container.querySelector("#axon-combo-val");
2883
+ e && (t >= 2 ? (e.textContent = `x${t}`, e.style.display = "block") : e.style.display = "none");
2921
2884
  }
2922
2885
  show() {
2923
- this.renderLabels(), this.container.style.display = "flex";
2886
+ this.container.style.display = "flex";
2924
2887
  }
2925
2888
  hide() {
2926
2889
  this.container.style.display = "none";
2927
2890
  }
2928
2891
  }
2929
- class ft {
2892
+ class It {
2930
2893
  container;
2931
2894
  i18n;
2932
2895
  onStartCallback;
@@ -2952,7 +2915,7 @@ class ft {
2952
2915
  this.container.style.display = "none";
2953
2916
  }
2954
2917
  }
2955
- class xt {
2918
+ class Tt {
2956
2919
  modal;
2957
2920
  card;
2958
2921
  i18n;
@@ -2963,12 +2926,12 @@ class xt {
2963
2926
  hapticsEnabled = !0;
2964
2927
  fpsLimit = 60;
2965
2928
  onFpsLimitChangedCallback;
2966
- constructor(t, e, i, s, a, l, n) {
2967
- this.i18n = e, this.themeManager = i, this.platform = s, this.onLanguageChangedCallback = a, this.onCloseCallback = l, this.onFpsLimitChangedCallback = n;
2968
- const o = localStorage.getItem("axon_surge_haptics");
2969
- o !== null && (this.hapticsEnabled = o === "true");
2970
- const r = Number(localStorage.getItem("axon_surge_fps_limit"));
2971
- (r === 30 || r === 60 || r === 120) && (this.fpsLimit = r), this.modal = document.createElement("dialog"), this.modal.className = "axon-settings-modal", this.card = document.createElement("div"), this.card.className = "axon-settings-card", this.modal.appendChild(this.card), t.appendChild(this.modal), this.modal.addEventListener("close", () => {
2929
+ constructor(t, e, i, s, a, n, o) {
2930
+ this.i18n = e, this.themeManager = i, this.platform = s, this.onLanguageChangedCallback = a, this.onCloseCallback = n, this.onFpsLimitChangedCallback = o;
2931
+ const r = localStorage.getItem("axon_surge_haptics");
2932
+ r !== null && (this.hapticsEnabled = r === "true");
2933
+ const c = Number(localStorage.getItem("axon_surge_fps_limit"));
2934
+ (c === 30 || c === 60 || c === 120) && (this.fpsLimit = c), this.modal = document.createElement("dialog"), this.modal.className = "axon-settings-modal", this.card = document.createElement("div"), this.card.className = "axon-settings-card", this.modal.appendChild(this.card), t.appendChild(this.modal), this.modal.addEventListener("close", () => {
2972
2935
  this.onCloseCallback && this.onCloseCallback();
2973
2936
  }), this.renderContent();
2974
2937
  }
@@ -2984,7 +2947,7 @@ class xt {
2984
2947
  s.className = "axon-select-wrapper";
2985
2948
  const a = document.createElement("select");
2986
2949
  a.className = "axon-select";
2987
- const l = {
2950
+ const n = {
2988
2951
  es: "ESPAÑOL (ES)",
2989
2952
  en: "ENGLISH (EN)",
2990
2953
  fr: "FRANÇAIS (FR)",
@@ -3001,92 +2964,92 @@ class xt {
3001
2964
  ko: "한국어 (KO)",
3002
2965
  zh: "中文 (ZH)"
3003
2966
  };
3004
- for (const I of V) {
3005
- const S = document.createElement("option");
3006
- S.value = I, S.textContent = l[I], this.i18n.getLanguage() === I && (S.selected = !0), a.appendChild(S);
2967
+ for (const I of tt) {
2968
+ const M = document.createElement("option");
2969
+ M.value = I, M.textContent = n[I], this.i18n.getLanguage() === I && (M.selected = !0), a.appendChild(M);
3007
2970
  }
3008
2971
  a.addEventListener("change", () => {
3009
2972
  const I = a.value;
3010
2973
  this.i18n.setLanguage(I), this.onLanguageChangedCallback && this.onLanguageChangedCallback(I), this.renderContent();
3011
2974
  });
3012
- const n = document.createElement("span");
3013
- n.className = "axon-select-arrow", n.textContent = "▼", s.appendChild(a), s.appendChild(n), e.appendChild(i), e.appendChild(s);
3014
- const o = document.createElement("div");
3015
- o.className = "axon-setting-row";
3016
- const r = document.createElement("span");
3017
- r.textContent = this.i18n.translate("themeLabel");
3018
- const c = document.createElement("div");
3019
- c.className = "axon-toggle-group";
2975
+ const o = document.createElement("span");
2976
+ o.className = "axon-select-arrow", o.textContent = "▼", s.appendChild(a), s.appendChild(o), e.appendChild(i), e.appendChild(s);
2977
+ const r = document.createElement("div");
2978
+ r.className = "axon-setting-row";
2979
+ const c = document.createElement("span");
2980
+ c.textContent = this.i18n.translate("themeLabel");
2981
+ const l = document.createElement("div");
2982
+ l.className = "axon-toggle-group";
3020
2983
  const h = [
3021
2984
  { id: "bioluminescent", label: "BIO" },
3022
2985
  { id: "dark", label: "DARK" },
3023
2986
  { id: "light", label: "LIGHT" }
3024
2987
  ];
3025
2988
  for (const I of h) {
3026
- const S = document.createElement("button");
3027
- S.type = "button", S.className = `axon-toggle-btn ${this.themeManager.getTheme() === I.id ? "active" : ""}`, S.textContent = I.label, S.addEventListener("click", () => {
2989
+ const M = document.createElement("button");
2990
+ M.type = "button", M.className = `axon-toggle-btn ${this.themeManager.getTheme() === I.id ? "active" : ""}`, M.textContent = I.label, M.addEventListener("click", () => {
3028
2991
  this.themeManager.setTheme(I.id), this.renderContent();
3029
- }), c.appendChild(S);
2992
+ }), l.appendChild(M);
3030
2993
  }
3031
- o.appendChild(r), o.appendChild(c);
3032
- const p = document.createElement("div");
3033
- p.className = "axon-setting-row";
2994
+ r.appendChild(c), r.appendChild(l);
2995
+ const u = document.createElement("div");
2996
+ u.className = "axon-setting-row";
3034
2997
  const d = document.createElement("span");
3035
2998
  d.textContent = this.i18n.translate("fpsLimitLabel");
3036
- const _ = document.createElement("div");
3037
- _.className = "axon-toggle-group";
3038
- const m = [
2999
+ const g = document.createElement("div");
3000
+ g.className = "axon-toggle-group";
3001
+ const b = [
3039
3002
  { limit: 30, label: "30 FPS" },
3040
3003
  { limit: 60, label: "60 FPS" },
3041
3004
  { limit: 120, label: "120 FPS" }
3042
3005
  ];
3043
- for (const I of m) {
3044
- const S = document.createElement("button");
3045
- S.type = "button", S.className = `axon-toggle-btn ${this.fpsLimit === I.limit ? "active" : ""}`, S.textContent = I.label, S.addEventListener("click", () => {
3006
+ for (const I of b) {
3007
+ const M = document.createElement("button");
3008
+ M.type = "button", M.className = `axon-toggle-btn ${this.fpsLimit === I.limit ? "active" : ""}`, M.textContent = I.label, M.addEventListener("click", () => {
3046
3009
  this.fpsLimit = I.limit, localStorage.setItem("axon_surge_fps_limit", String(I.limit)), this.onFpsLimitChangedCallback?.(I.limit), this.renderContent();
3047
- }), _.appendChild(S);
3010
+ }), g.appendChild(M);
3048
3011
  }
3049
- p.appendChild(d), p.appendChild(_);
3050
- const u = document.createElement("div");
3051
- u.className = "axon-setting-row";
3052
- const g = document.createElement("span");
3053
- g.textContent = this.i18n.translate("hapticsLabel");
3012
+ u.appendChild(d), u.appendChild(g);
3054
3013
  const f = document.createElement("div");
3055
- f.className = "axon-toggle-group";
3056
- const b = [
3014
+ f.className = "axon-setting-row";
3015
+ const _ = document.createElement("span");
3016
+ _.textContent = this.i18n.translate("hapticsLabel");
3017
+ const x = document.createElement("div");
3018
+ x.className = "axon-toggle-group";
3019
+ const y = [
3057
3020
  { enabled: !0, label: this.i18n.translate("hapticsOn") },
3058
3021
  { enabled: !1, label: this.i18n.translate("hapticsOff") }
3059
3022
  ];
3060
- for (const I of b) {
3061
- const S = document.createElement("button");
3062
- S.type = "button", S.className = `axon-toggle-btn ${this.hapticsEnabled === I.enabled ? "active" : ""}`, S.textContent = I.label, S.addEventListener("click", () => {
3023
+ for (const I of y) {
3024
+ const M = document.createElement("button");
3025
+ M.type = "button", M.className = `axon-toggle-btn ${this.hapticsEnabled === I.enabled ? "active" : ""}`, M.textContent = I.label, M.addEventListener("click", () => {
3063
3026
  this.hapticsEnabled = I.enabled, localStorage.setItem("axon_surge_haptics", String(I.enabled)), this.hapticsEnabled && this.platform.haptics.selectionChanged(), this.renderContent();
3064
- }), f.appendChild(S);
3027
+ }), x.appendChild(M);
3065
3028
  }
3066
- u.appendChild(g), u.appendChild(f);
3067
- const y = document.createElement("div");
3068
- y.className = "axon-setting-row";
3029
+ f.appendChild(_), f.appendChild(x);
3030
+ const m = document.createElement("div");
3031
+ m.className = "axon-setting-row";
3069
3032
  const v = document.createElement("span");
3070
3033
  v.textContent = this.i18n.translate("tutorialSettingLabel");
3071
- const M = document.createElement("div");
3072
- M.className = "axon-toggle-group";
3073
- const T = localStorage.getItem("axon_surge_show_tutorial") !== "false", x = [
3034
+ const A = document.createElement("div");
3035
+ A.className = "axon-toggle-group";
3036
+ const S = localStorage.getItem("axon_surge_show_tutorial") !== "false", L = [
3074
3037
  { enabled: !0, label: this.i18n.translate("hapticsOn") },
3075
3038
  { enabled: !1, label: this.i18n.translate("hapticsOff") }
3076
3039
  ];
3077
- for (const I of x) {
3078
- const S = document.createElement("button");
3079
- S.type = "button", S.className = `axon-toggle-btn ${T === I.enabled ? "active" : ""}`, S.textContent = I.label, S.addEventListener("click", () => {
3040
+ for (const I of L) {
3041
+ const M = document.createElement("button");
3042
+ M.type = "button", M.className = `axon-toggle-btn ${S === I.enabled ? "active" : ""}`, M.textContent = I.label, M.addEventListener("click", () => {
3080
3043
  localStorage.setItem("axon_surge_show_tutorial", String(I.enabled)), this.renderContent();
3081
- }), M.appendChild(S);
3044
+ }), A.appendChild(M);
3082
3045
  }
3083
- y.appendChild(v), y.appendChild(M);
3084
- const L = document.createElement("div");
3085
- L.className = "axon-settings-credits", L.textContent = this.i18n.translate("creditsJam");
3086
- const R = document.createElement("button");
3087
- R.type = "button", R.className = "axon-start-button", R.style.padding = "12px 36px", R.style.fontSize = "14px", R.textContent = this.i18n.translate("closeButton"), R.addEventListener("click", () => {
3046
+ m.appendChild(v), m.appendChild(A);
3047
+ const T = document.createElement("div");
3048
+ T.className = "axon-settings-credits", T.textContent = this.i18n.translate("creditsJam");
3049
+ const O = document.createElement("button");
3050
+ O.type = "button", O.className = "axon-start-button", O.style.padding = "12px 36px", O.style.fontSize = "14px", O.textContent = this.i18n.translate("closeButton"), O.addEventListener("click", () => {
3088
3051
  this.modal.close();
3089
- }), this.card.appendChild(t), this.card.appendChild(e), this.card.appendChild(o), this.card.appendChild(p), this.card.appendChild(u), this.card.appendChild(y), this.card.appendChild(L), this.card.appendChild(R);
3052
+ }), this.card.appendChild(t), this.card.appendChild(e), this.card.appendChild(r), this.card.appendChild(u), this.card.appendChild(f), this.card.appendChild(m), this.card.appendChild(T), this.card.appendChild(O);
3090
3053
  }
3091
3054
  isHapticsEnabled() {
3092
3055
  return this.hapticsEnabled;
@@ -3101,8 +3064,29 @@ class xt {
3101
3064
  this.modal.close();
3102
3065
  }
3103
3066
  }
3104
- const K = "axon_surge_show_tutorial";
3105
- class yt {
3067
+ class Rt {
3068
+ constructor(t) {
3069
+ this.content = t;
3070
+ }
3071
+ createEnemy(t, e, i) {
3072
+ const s = `enemy_${Date.now()}_${Math.random()}`, a = new F(s);
3073
+ return this.configureEnemy(a, t, e, i), a;
3074
+ }
3075
+ configureEnemy(t, e, i, s) {
3076
+ const a = this.content.enemies.require(e);
3077
+ t.type = e, t.position = { ...i }, t.active = !0, t.isGrabbed = !1, t.isThrown = !1, t.isGrabbable = a.canBeGrabbed;
3078
+ const n = s.x - i.x, o = s.y - i.y, r = Math.atan2(o, n);
3079
+ t.radius = a.radius, t.hitRadiusPadding = a.hitRadiusPadding ?? 0.2, t.color = a.color, t.strategy = a.impactStrategyFactory?.() ?? null, t.behavior = a.behavior, t.synergyKey = a.synergyKey ?? a.behavior.synergyKey?.(t) ?? a.id, t.maxSpeed = a.maxSpeed, t.rotation = a.initialRotation === "random" ? Math.random() * Math.PI * 2 : 0, t.velocity = {
3080
+ x: Math.cos(r) * a.speed,
3081
+ y: Math.sin(r) * a.speed
3082
+ };
3083
+ }
3084
+ }
3085
+ const Q = "axon_surge_show_tutorial";
3086
+ class Lt {
3087
+ constructor(t, e, i) {
3088
+ this.factory = t, this.launcherType = e, this.targetType = i;
3089
+ }
3106
3090
  isActive = !1;
3107
3091
  showWarning = !1;
3108
3092
  dummyLauncher = null;
@@ -3111,14 +3095,14 @@ class yt {
3111
3095
  initialTargetPos = { x: 0, y: 0 };
3112
3096
  isTutorialEnabled() {
3113
3097
  try {
3114
- return localStorage.getItem(K) !== "false";
3098
+ return localStorage.getItem(Q) !== "false";
3115
3099
  } catch {
3116
3100
  return !0;
3117
3101
  }
3118
3102
  }
3119
3103
  setTutorialEnabled(t) {
3120
3104
  try {
3121
- localStorage.setItem(K, String(t));
3105
+ localStorage.setItem(Q, String(t));
3122
3106
  } catch {
3123
3107
  }
3124
3108
  }
@@ -3136,39 +3120,39 @@ class yt {
3136
3120
  this.isActive = !1;
3137
3121
  return;
3138
3122
  }
3139
- this.isActive = !0, this.showWarning = !1, i.length = 0, this.initialLauncherPos = { x: t * 0.28, y: e * 0.28 }, this.initialTargetPos = { x: t * 0.72, y: e * 0.28 }, this.dummyLauncher = C.createEnemy(
3140
- "piercer",
3123
+ this.isActive = !0, this.showWarning = !1, i.length = 0, this.initialLauncherPos = { x: t * 0.28, y: e * 0.28 }, this.initialTargetPos = { x: t * 0.72, y: e * 0.28 }, this.dummyLauncher = this.factory.createEnemy(
3124
+ this.launcherType,
3141
3125
  this.initialLauncherPos,
3142
3126
  this.initialTargetPos
3143
- ), this.dummyLauncher.velocity = { x: 0, y: 0 }, this.dummyTarget = C.createEnemy(
3144
- "spore",
3127
+ ), this.dummyLauncher.velocity = { x: 0, y: 0 }, this.dummyTarget = this.factory.createEnemy(
3128
+ this.targetType,
3145
3129
  this.initialTargetPos,
3146
3130
  this.initialLauncherPos
3147
3131
  ), this.dummyTarget.velocity = { x: 0, y: 0 }, i.push(this.dummyLauncher), i.push(this.dummyTarget);
3148
3132
  }
3149
- update(t, e, i, s, a, l) {
3133
+ update(t, e, i, s, a, n) {
3150
3134
  if (!this.isActive || !this.dummyLauncher || !this.dummyTarget) return;
3151
- const n = this.dummyLauncher, o = this.dummyTarget;
3152
- e.includes(n) || e.push(n), e.includes(o) || e.push(o);
3153
- const r = o.position.x - n.position.x, c = o.position.y - n.position.y, h = Math.hypot(r, c);
3154
- if (n.isGrabbed) {
3155
- if (this.showWarning = !1, h <= n.radius + o.radius + 60) {
3156
- l && l(), n.isGrabbed = !1, this.showWarning = !0, this.resetDummies(n, o);
3135
+ const o = this.dummyLauncher, r = this.dummyTarget;
3136
+ e.includes(o) || e.push(o), e.includes(r) || e.push(r);
3137
+ const c = r.position.x - o.position.x, l = r.position.y - o.position.y, h = Math.hypot(c, l);
3138
+ if (o.isGrabbed) {
3139
+ if (this.showWarning = !1, h <= o.radius + r.radius + 60) {
3140
+ n && n(), o.isGrabbed = !1, this.showWarning = !0, this.resetDummies(o, r);
3157
3141
  return;
3158
3142
  }
3159
3143
  return;
3160
3144
  }
3161
- if (n.wasManipulated) {
3162
- const p = Math.hypot(n.velocity.x, n.velocity.y);
3163
- if (!n.isThrown || p < 70) {
3164
- l && l(), this.showWarning = !0, this.resetDummies(n, o);
3145
+ if (o.wasManipulated) {
3146
+ const u = Math.hypot(o.velocity.x, o.velocity.y);
3147
+ if (!o.isThrown || u < 70) {
3148
+ n && n(), this.showWarning = !0, this.resetDummies(o, r);
3165
3149
  return;
3166
3150
  }
3167
- if (h <= n.getHitRadius() + o.getHitRadius()) {
3168
- n.triggerDissolveDeath(!0), o.triggerDissolveDeath(!0), this.showWarning = !1, this.isActive = !1, a();
3151
+ if (h <= o.getHitRadius() + r.getHitRadius()) {
3152
+ o.triggerDissolveDeath(!0), r.triggerDissolveDeath(!0), this.showWarning = !1, this.isActive = !1, a();
3169
3153
  return;
3170
3154
  }
3171
- (n.position.x < -60 || n.position.x > i + 60 || n.position.y < -60 || n.position.y > s + 60 || p < 40) && (l && l(), this.showWarning = !0, this.resetDummies(n, o));
3155
+ (o.position.x < -60 || o.position.x > i + 60 || o.position.y < -60 || o.position.y > s + 60 || u < 40) && (n && n(), this.showWarning = !0, this.resetDummies(o, r));
3172
3156
  }
3173
3157
  }
3174
3158
  resetDummies(t, e) {
@@ -3178,7 +3162,7 @@ class yt {
3178
3162
  this.isActive = !1, this.showWarning = !1;
3179
3163
  }
3180
3164
  }
3181
- class Et {
3165
+ class Ct {
3182
3166
  container;
3183
3167
  i18n;
3184
3168
  tutorialManager;
@@ -3214,15 +3198,15 @@ class Et {
3214
3198
  this.container.style.display = "none";
3215
3199
  }
3216
3200
  }
3217
- class bt {
3218
- fillElement;
3201
+ class Pt {
3219
3202
  experienceCount = 0;
3220
3203
  currentLevel = 1;
3204
+ displayedProgress = 0;
3205
+ targetProgress = 0;
3206
+ levelUpPulse = 0;
3221
3207
  onLevelUpCallback;
3222
- constructor(t, e) {
3223
- this.onLevelUpCallback = e;
3224
- const i = document.createElement("div");
3225
- i.className = "axon-hud-level-track", this.fillElement = document.createElement("div"), this.fillElement.className = "axon-hud-level-fill", i.appendChild(this.fillElement), t.appendChild(i);
3208
+ constructor(t) {
3209
+ this.onLevelUpCallback = t;
3226
3210
  }
3227
3211
  getExperienceRequiredForLevel(t) {
3228
3212
  return t === 1 ? 3 : t === 2 ? 6 : t === 3 ? 10 : 10 + (t - 3) * 6;
@@ -3231,9 +3215,18 @@ class bt {
3231
3215
  this.experienceCount += Math.max(0, t);
3232
3216
  let e = this.getExperienceRequiredForLevel(this.currentLevel);
3233
3217
  for (; this.experienceCount >= e; )
3234
- this.experienceCount -= e, this.currentLevel++, this.onLevelUpCallback && this.onLevelUpCallback(this.currentLevel), e = this.getExperienceRequiredForLevel(this.currentLevel);
3235
- const i = Math.min(100, this.experienceCount / e * 100);
3236
- this.fillElement.style.width = `${i}%`;
3218
+ this.experienceCount -= e, this.currentLevel++, this.displayedProgress = 1, this.levelUpPulse = 1, this.onLevelUpCallback && this.onLevelUpCallback(this.currentLevel), e = this.getExperienceRequiredForLevel(this.currentLevel);
3219
+ this.targetProgress = this.getRawProgress();
3220
+ }
3221
+ update(t) {
3222
+ const e = 1 - Math.exp(-Math.max(0, t) * 11);
3223
+ this.displayedProgress += (this.targetProgress - this.displayedProgress) * e, this.levelUpPulse = Math.max(0, this.levelUpPulse - Math.max(0, t) * 1.8);
3224
+ }
3225
+ getProgress() {
3226
+ return this.displayedProgress;
3227
+ }
3228
+ getLevelUpPulse() {
3229
+ return this.levelUpPulse;
3237
3230
  }
3238
3231
  addKill() {
3239
3232
  this.addExperience(1);
@@ -3242,17 +3235,56 @@ class bt {
3242
3235
  return this.currentLevel;
3243
3236
  }
3244
3237
  reset() {
3245
- this.experienceCount = 0, this.currentLevel = 1, this.fillElement.style.width = "0%";
3238
+ this.experienceCount = 0, this.currentLevel = 1, this.displayedProgress = 0, this.targetProgress = 0, this.levelUpPulse = 0;
3246
3239
  }
3247
- show() {
3248
- this.fillElement && this.fillElement.parentElement && (this.fillElement.parentElement.style.display = "block");
3240
+ getRawProgress() {
3241
+ const t = this.getExperienceRequiredForLevel(this.currentLevel);
3242
+ return Math.min(1, this.experienceCount / t);
3249
3243
  }
3250
- hide() {
3251
- this.fillElement && this.fillElement.parentElement && (this.fillElement.parentElement.style.display = "none");
3244
+ }
3245
+ class P {
3246
+ constructor(t) {
3247
+ this.kind = t;
3248
+ }
3249
+ definitions = /* @__PURE__ */ new Map();
3250
+ register(t) {
3251
+ const e = t.id.trim();
3252
+ if (!e)
3253
+ throw new Error(`Cannot register ${this.kind} definition without an id`);
3254
+ if (this.definitions.has(e))
3255
+ throw new Error(`Duplicate ${this.kind} definition: ${e}`);
3256
+ this.definitions.set(e, t);
3257
+ }
3258
+ registerMany(t) {
3259
+ for (const e of t)
3260
+ this.register(e);
3261
+ }
3262
+ has(t) {
3263
+ return this.definitions.has(t);
3264
+ }
3265
+ get(t) {
3266
+ return this.definitions.get(t);
3267
+ }
3268
+ require(t) {
3269
+ const e = this.get(t);
3270
+ if (!e)
3271
+ throw new Error(`Unknown ${this.kind} definition: ${t}`);
3272
+ return e;
3273
+ }
3274
+ values() {
3275
+ return [...this.definitions.values()];
3276
+ }
3277
+ get size() {
3278
+ return this.definitions.size;
3252
3279
  }
3253
3280
  }
3254
- class F {
3255
- static powerUps = [
3281
+ class wt {
3282
+ constructor(t) {
3283
+ this.configManager = t;
3284
+ }
3285
+ registry = new P("power-up");
3286
+ defaultsRegistered = !1;
3287
+ defaultPowerUps = [
3256
3288
  {
3257
3289
  id: "spore_radius",
3258
3290
  titleKey: "pu_spore_radius_title",
@@ -3261,9 +3293,9 @@ class F {
3261
3293
  category: "radius",
3262
3294
  rarity: "common",
3263
3295
  rarityWeight: 35,
3264
- color: "#39ff14",
3296
+ color: E.spore,
3265
3297
  apply: () => {
3266
- const t = A.getInstance(), e = t.getConfig().toxicPool;
3298
+ const t = this.configManager, e = t.getConfig().toxicPool;
3267
3299
  t.updateConfig({
3268
3300
  toxicPool: {
3269
3301
  ...e,
@@ -3281,9 +3313,9 @@ class F {
3281
3313
  category: "duration",
3282
3314
  rarity: "common",
3283
3315
  rarityWeight: 35,
3284
- color: "#39ff14",
3316
+ color: E.spore,
3285
3317
  apply: () => {
3286
- const t = A.getInstance(), e = t.getConfig().toxicPool;
3318
+ const t = this.configManager, e = t.getConfig().toxicPool;
3287
3319
  t.updateConfig({
3288
3320
  toxicPool: {
3289
3321
  ...e,
@@ -3301,9 +3333,9 @@ class F {
3301
3333
  category: "radius",
3302
3334
  rarity: "common",
3303
3335
  rarityWeight: 35,
3304
- color: "#9d4edd",
3336
+ color: E.parasite,
3305
3337
  apply: () => {
3306
- const t = A.getInstance(), e = t.getConfig().gravityVortex;
3338
+ const t = this.configManager, e = t.getConfig().gravityVortex;
3307
3339
  t.updateConfig({
3308
3340
  gravityVortex: {
3309
3341
  ...e,
@@ -3321,9 +3353,9 @@ class F {
3321
3353
  category: "duration",
3322
3354
  rarity: "common",
3323
3355
  rarityWeight: 35,
3324
- color: "#9d4edd",
3356
+ color: E.parasite,
3325
3357
  apply: () => {
3326
- const t = A.getInstance(), e = t.getConfig().gravityVortex;
3358
+ const t = this.configManager, e = t.getConfig().gravityVortex;
3327
3359
  t.updateConfig({
3328
3360
  gravityVortex: {
3329
3361
  ...e,
@@ -3341,9 +3373,9 @@ class F {
3341
3373
  category: "power",
3342
3374
  rarity: "rare",
3343
3375
  rarityWeight: 25,
3344
- color: "#9d4edd",
3376
+ color: E.parasite,
3345
3377
  apply: () => {
3346
- const t = A.getInstance(), e = t.getConfig().gravityVortex;
3378
+ const t = this.configManager, e = t.getConfig().gravityVortex;
3347
3379
  t.updateConfig({
3348
3380
  gravityVortex: {
3349
3381
  ...e,
@@ -3355,15 +3387,16 @@ class F {
3355
3387
  },
3356
3388
  {
3357
3389
  id: "vortex_pocket",
3390
+ isAvailable: ({ config: t }) => !t.gravityVortex.hasImplosionLaunch,
3358
3391
  titleKey: "pu_vortex_pocket_title",
3359
3392
  descKey: "pu_vortex_pocket_desc",
3360
3393
  badge: "IMPLODE LAUNCH",
3361
3394
  category: "power",
3362
3395
  rarity: "legendary",
3363
3396
  rarityWeight: 5,
3364
- color: "#9d4edd",
3397
+ color: E.parasite,
3365
3398
  apply: () => {
3366
- const t = A.getInstance(), e = t.getConfig().gravityVortex;
3399
+ const t = this.configManager, e = t.getConfig().gravityVortex;
3367
3400
  t.updateConfig({
3368
3401
  gravityVortex: {
3369
3402
  ...e,
@@ -3374,15 +3407,16 @@ class F {
3374
3407
  },
3375
3408
  {
3376
3409
  id: "shield_fan",
3410
+ isAvailable: ({ config: t }) => !t.shield.isFanBlast,
3377
3411
  titleKey: "pu_shield_fan_title",
3378
3412
  descKey: "pu_shield_fan_desc",
3379
3413
  badge: "FAN BLAST",
3380
3414
  category: "power",
3381
3415
  rarity: "legendary",
3382
3416
  rarityWeight: 5,
3383
- color: "#00f5ff",
3417
+ color: E.heavy,
3384
3418
  apply: () => {
3385
- const t = A.getInstance(), e = t.getConfig().shield;
3419
+ const t = this.configManager, e = t.getConfig().shield;
3386
3420
  t.updateConfig({
3387
3421
  shield: {
3388
3422
  ...e,
@@ -3393,15 +3427,16 @@ class F {
3393
3427
  },
3394
3428
  {
3395
3429
  id: "shield_fan_upgrade",
3430
+ isAvailable: ({ config: t }) => t.shield.isFanBlast && !t.shield.hasFanBlastUpgrade,
3396
3431
  titleKey: "pu_shield_fan_upgrade_title",
3397
3432
  descKey: "pu_shield_fan_upgrade_desc",
3398
3433
  badge: "+0.35s / +15°",
3399
3434
  category: "power",
3400
3435
  rarity: "epic",
3401
3436
  rarityWeight: 15,
3402
- color: "#00f5ff",
3437
+ color: E.heavy,
3403
3438
  apply: () => {
3404
- const t = A.getInstance(), e = t.getConfig().shield;
3439
+ const t = this.configManager, e = t.getConfig().shield;
3405
3440
  t.updateConfig({
3406
3441
  shield: {
3407
3442
  ...e,
@@ -3420,9 +3455,9 @@ class F {
3420
3455
  category: "defense",
3421
3456
  rarity: "common",
3422
3457
  rarityWeight: 35,
3423
- color: "#00f5ff",
3458
+ color: E.heavy,
3424
3459
  apply: () => {
3425
- const t = A.getInstance(), e = t.getConfig().shield;
3460
+ const t = this.configManager, e = t.getConfig().shield;
3426
3461
  t.updateConfig({
3427
3462
  shield: {
3428
3463
  ...e,
@@ -3439,9 +3474,9 @@ class F {
3439
3474
  category: "power",
3440
3475
  rarity: "rare",
3441
3476
  rarityWeight: 25,
3442
- color: "#ffcc00",
3477
+ color: E.cluster,
3443
3478
  apply: () => {
3444
- const t = A.getInstance(), e = t.getConfig().cluster;
3479
+ const t = this.configManager, e = t.getConfig().cluster;
3445
3480
  t.updateConfig({
3446
3481
  cluster: {
3447
3482
  ...e,
@@ -3452,15 +3487,16 @@ class F {
3452
3487
  },
3453
3488
  {
3454
3489
  id: "cluster_homing",
3490
+ isAvailable: ({ config: t }) => !t.cluster.hasHoming,
3455
3491
  titleKey: "pu_cluster_homing_title",
3456
3492
  descKey: "pu_cluster_homing_desc",
3457
3493
  badge: "SEEKER FRAGS",
3458
3494
  category: "power",
3459
3495
  rarity: "legendary",
3460
3496
  rarityWeight: 5,
3461
- color: "#ffcc00",
3497
+ color: E.cluster,
3462
3498
  apply: () => {
3463
- const t = A.getInstance(), e = t.getConfig().cluster;
3499
+ const t = this.configManager, e = t.getConfig().cluster;
3464
3500
  t.updateConfig({
3465
3501
  cluster: {
3466
3502
  ...e,
@@ -3477,9 +3513,9 @@ class F {
3477
3513
  category: "power",
3478
3514
  rarity: "epic",
3479
3515
  rarityWeight: 15,
3480
- color: "#ff3366",
3516
+ color: E.piercer,
3481
3517
  apply: () => {
3482
- const t = A.getInstance(), e = t.getConfig().piercer;
3518
+ const t = this.configManager, e = t.getConfig().piercer;
3483
3519
  t.updateConfig({
3484
3520
  piercer: {
3485
3521
  ...e,
@@ -3490,15 +3526,16 @@ class F {
3490
3526
  },
3491
3527
  {
3492
3528
  id: "piercer_homing",
3529
+ isAvailable: ({ config: t }) => !t.piercer.hasHoming,
3493
3530
  titleKey: "pu_piercer_homing_title",
3494
3531
  descKey: "pu_piercer_homing_desc",
3495
3532
  badge: "SEEKER",
3496
3533
  category: "power",
3497
3534
  rarity: "legendary",
3498
3535
  rarityWeight: 5,
3499
- color: "#ff2a5f",
3536
+ color: E.piercer,
3500
3537
  apply: () => {
3501
- const t = A.getInstance(), e = t.getConfig().piercer;
3538
+ const t = this.configManager, e = t.getConfig().piercer;
3502
3539
  t.updateConfig({
3503
3540
  piercer: {
3504
3541
  ...e,
@@ -3509,15 +3546,16 @@ class F {
3509
3546
  },
3510
3547
  {
3511
3548
  id: "offscreen_radar",
3549
+ isAvailable: ({ config: t }) => !t.core.hasOffscreenIndicators,
3512
3550
  titleKey: "pu_offscreen_radar_title",
3513
3551
  descKey: "pu_offscreen_radar_desc",
3514
3552
  badge: "RADAR",
3515
3553
  category: "defense",
3516
3554
  rarity: "legendary",
3517
3555
  rarityWeight: 5,
3518
- color: "#00f0ff",
3556
+ color: E.core,
3519
3557
  apply: () => {
3520
- const t = A.getInstance(), e = t.getConfig().core;
3558
+ const t = this.configManager, e = t.getConfig().core;
3521
3559
  t.updateConfig({
3522
3560
  core: {
3523
3561
  ...e,
@@ -3528,15 +3566,16 @@ class F {
3528
3566
  },
3529
3567
  {
3530
3568
  id: "piercer_homing_target",
3569
+ isAvailable: ({ config: t }) => t.piercer.hasHoming,
3531
3570
  titleKey: "pu_piercer_homing_target_title",
3532
3571
  descKey: "pu_piercer_homing_target_desc",
3533
3572
  badge: "+1 TARGET",
3534
3573
  category: "power",
3535
3574
  rarity: "epic",
3536
3575
  rarityWeight: 15,
3537
- color: "#ff2a5f",
3576
+ color: E.piercer,
3538
3577
  apply: () => {
3539
- const t = A.getInstance(), e = t.getConfig().piercer;
3578
+ const t = this.configManager, e = t.getConfig().piercer;
3540
3579
  t.updateConfig({
3541
3580
  piercer: {
3542
3581
  ...e,
@@ -3547,15 +3586,16 @@ class F {
3547
3586
  },
3548
3587
  {
3549
3588
  id: "vampirism_unlock",
3589
+ isAvailable: ({ config: t }) => t.core.vampirismHealProgressRate <= 0,
3550
3590
  titleKey: "pu_vampirism_unlock_title",
3551
3591
  descKey: "pu_vampirism_unlock_desc",
3552
3592
  badge: "+0.5% REPAIR",
3553
3593
  category: "defense",
3554
3594
  rarity: "epic",
3555
3595
  rarityWeight: 15,
3556
- color: "#00f5ff",
3596
+ color: E.core,
3557
3597
  apply: () => {
3558
- const t = A.getInstance(), e = t.getConfig().core;
3598
+ const t = this.configManager, e = t.getConfig().core;
3559
3599
  t.updateConfig({
3560
3600
  core: {
3561
3601
  ...e,
@@ -3566,15 +3606,16 @@ class F {
3566
3606
  },
3567
3607
  {
3568
3608
  id: "vampirism_boost",
3609
+ isAvailable: ({ config: t }) => t.core.vampirismHealProgressRate > 0,
3569
3610
  titleKey: "pu_vampirism_boost_title",
3570
3611
  descKey: "pu_vampirism_boost_desc",
3571
3612
  badge: "+0.3% HEAL",
3572
3613
  category: "defense",
3573
3614
  rarity: "rare",
3574
3615
  rarityWeight: 25,
3575
- color: "#00f5ff",
3616
+ color: E.core,
3576
3617
  apply: () => {
3577
- const t = A.getInstance(), e = t.getConfig().core;
3618
+ const t = this.configManager, e = t.getConfig().core;
3578
3619
  t.updateConfig({
3579
3620
  core: {
3580
3621
  ...e,
@@ -3591,9 +3632,9 @@ class F {
3591
3632
  category: "defense",
3592
3633
  rarity: "rare",
3593
3634
  rarityWeight: 20,
3594
- color: "#00f5ff",
3635
+ color: E.core,
3595
3636
  apply: (t) => {
3596
- A.getInstance().addCoreRing(1), t && t.addMaxRing(1);
3637
+ this.configManager.addCoreRing(1), t && t.addMaxRing(1);
3597
3638
  }
3598
3639
  },
3599
3640
  {
@@ -3604,9 +3645,9 @@ class F {
3604
3645
  category: "magnet",
3605
3646
  rarity: "common",
3606
3647
  rarityWeight: 35,
3607
- color: "#00f5ff",
3648
+ color: E.core,
3608
3649
  apply: () => {
3609
- const t = A.getInstance(), e = t.getConfig().magnet;
3650
+ const t = this.configManager, e = t.getConfig().magnet;
3610
3651
  t.updateConfig({
3611
3652
  magnet: {
3612
3653
  ...e,
@@ -3623,9 +3664,9 @@ class F {
3623
3664
  category: "magnet",
3624
3665
  rarity: "rare",
3625
3666
  rarityWeight: 25,
3626
- color: "#00f5ff",
3667
+ color: E.core,
3627
3668
  apply: () => {
3628
- const t = A.getInstance(), e = t.getConfig().magnet;
3669
+ const t = this.configManager, e = t.getConfig().magnet;
3629
3670
  t.updateConfig({
3630
3671
  magnet: {
3631
3672
  ...e,
@@ -3635,26 +3676,32 @@ class F {
3635
3676
  }
3636
3677
  }
3637
3678
  ];
3638
- static getRandomOptions(t = 3) {
3639
- const e = A.getInstance().getConfig(), i = this.powerUps.filter((a) => !(a.id === "vampirism_unlock" && e.core.vampirismHealProgressRate > 0 || a.id === "vampirism_boost" && e.core.vampirismHealProgressRate <= 0 || a.id === "piercer_homing" && e.piercer.hasHoming || a.id === "piercer_homing_target" && !e.piercer.hasHoming || a.id === "vortex_pocket" && e.gravityVortex.hasImplosionLaunch || a.id === "shield_fan" && e.shield.isFanBlast || a.id === "shield_fan_upgrade" && (!e.shield.isFanBlast || e.shield.hasFanBlastUpgrade) || a.id === "cluster_homing" && e.cluster.hasHoming || a.id === "offscreen_radar" && e.core.hasOffscreenIndicators)), s = [];
3679
+ getDefinitions() {
3680
+ return this.defaultsRegistered || (this.registry.registerMany(this.defaultPowerUps), this.defaultsRegistered = !0), this.registry.values();
3681
+ }
3682
+ getAll() {
3683
+ return this.getDefinitions();
3684
+ }
3685
+ getRandomOptions(t = 3) {
3686
+ const e = this.configManager.getConfig(), i = this.getDefinitions().filter((a) => a.isAvailable?.({ config: e }) ?? !0), s = [];
3640
3687
  for (let a = 0; a < t && i.length > 0; a++) {
3641
- const l = i.reduce((o, r) => o + r.rarityWeight, 0);
3642
- let n = Math.random() * l;
3643
- for (let o = 0; o < i.length; o++) {
3644
- const r = i[o];
3645
- if (n -= r.rarityWeight, n <= 0) {
3646
- s.push(r), i.splice(o, 1);
3688
+ const n = i.reduce((r, c) => r + c.rarityWeight, 0);
3689
+ let o = Math.random() * n;
3690
+ for (let r = 0; r < i.length; r++) {
3691
+ const c = i[r];
3692
+ if (o -= c.rarityWeight, o <= 0) {
3693
+ s.push(c), i.splice(r, 1);
3647
3694
  break;
3648
3695
  }
3649
3696
  }
3650
3697
  }
3651
3698
  return s;
3652
3699
  }
3653
- static registerPowerUp(t) {
3654
- this.powerUps.push(t);
3700
+ registerPowerUp(t) {
3701
+ this.getDefinitions(), this.registry.register(t);
3655
3702
  }
3656
3703
  }
3657
- class At {
3704
+ class Ot {
3658
3705
  haptics = {
3659
3706
  impact: async (t) => {
3660
3707
  if ("vibrate" in navigator) {
@@ -3694,7 +3741,562 @@ class At {
3694
3741
  }
3695
3742
  };
3696
3743
  }
3697
- class vt {
3744
+ class Dt {
3745
+ enemies = new P("enemy");
3746
+ variants = new P("enemy variant");
3747
+ powerUps = new P("power-up");
3748
+ synergies = new P("synergy");
3749
+ areas = new P("area");
3750
+ bosses = new P("boss");
3751
+ bossRewards = new P("boss reward");
3752
+ cores = new P("core");
3753
+ spawnerPatterns = new P("spawner pattern");
3754
+ tutorial;
3755
+ constructor(t = {}) {
3756
+ this.tutorial = t.tutorial, this.enemies.registerMany(t.enemies ?? []), this.variants.registerMany(t.variants ?? []), this.powerUps.registerMany(t.powerUps ?? []), this.synergies.registerMany(t.synergies ?? []), this.areas.registerMany(t.areas ?? []), this.bosses.registerMany(t.bosses ?? []), this.bossRewards.registerMany(t.bossRewards ?? []), this.cores.registerMany(t.cores ?? []), this.spawnerPatterns.registerMany(t.spawnerPatterns ?? []);
3757
+ }
3758
+ /** Validate references once a complete catalog has been assembled. */
3759
+ validate() {
3760
+ for (const t of this.variants.values())
3761
+ this.enemies.require(t.baseEnemyId);
3762
+ for (const t of this.areas.values()) {
3763
+ for (const e of [...t.baseEnemyIds, ...t.additionalEnemyIds])
3764
+ this.enemies.require(e);
3765
+ t.bossId && this.bosses.require(t.bossId);
3766
+ }
3767
+ for (const t of this.bosses.values())
3768
+ t.areaId && this.areas.require(t.areaId), t.rewardId && this.bossRewards.require(t.rewardId);
3769
+ for (const t of this.bossRewards.values())
3770
+ for (const e of t.temporaryPowerUpIds)
3771
+ this.powerUps.require(e);
3772
+ for (const t of this.spawnerPatterns.values()) {
3773
+ if (t.enemyIds.length === 0) throw new Error(`Spawner pattern "${t.id}" has no enemy IDs`);
3774
+ for (const e of t.enemyIds) this.enemies.require(e);
3775
+ }
3776
+ this.tutorial && (this.enemies.require(this.tutorial.launcherEnemyId), this.enemies.require(this.tutorial.targetEnemyId));
3777
+ }
3778
+ }
3779
+ class Nt {
3780
+ execute(t) {
3781
+ const { thrownEnemy: e, targetEnemy: i, allEnemies: s, shockwaves: a, particles: n, floatingTexts: o } = t, r = i.position;
3782
+ e.experienceDropPending = !0, i.experienceDropPending = !0, e.active = !1, i.active = !1;
3783
+ const c = t.config, l = c.cluster.fragmentCount, h = c.cluster.hasHoming;
3784
+ a.length < c.cluster.maxExplosionShockwaves && a.push({
3785
+ id: Math.random().toString(),
3786
+ position: { ...r },
3787
+ radius: 15,
3788
+ maxRadius: 280,
3789
+ color: E.cluster,
3790
+ alpha: 1,
3791
+ lifetime: 0,
3792
+ maxLifetime: 0.6
3793
+ });
3794
+ for (let d = 0; d < l; d++) {
3795
+ const g = d * Math.PI * 2 / l, b = 800 + Math.random() * 300, f = {
3796
+ x: r.x + Math.cos(g) * 45,
3797
+ y: r.y + Math.sin(g) * 45
3798
+ };
3799
+ let _ = g;
3800
+ if (h) {
3801
+ let m = null, v = 1 / 0;
3802
+ for (const A of s) {
3803
+ if (A === e || A === i || !A.active || A.isGrabbed || A.isDying) continue;
3804
+ const S = Math.hypot(A.position.x - f.x, A.position.y - f.y);
3805
+ S < v && (v = S, m = A);
3806
+ }
3807
+ m && (_ = Math.atan2(m.position.y - f.y, m.position.x - f.x));
3808
+ }
3809
+ const x = e.behavior.spawnedEnemyIds?.[0];
3810
+ if (!x) continue;
3811
+ const y = t.factory.createEnemy(x, f, {
3812
+ x: f.x + Math.cos(_) * 500,
3813
+ y: f.y + Math.sin(_) * 500
3814
+ });
3815
+ y.isThrown = !0, y.wasManipulated = !0, y.velocity = {
3816
+ x: Math.cos(_) * b,
3817
+ y: Math.sin(_) * b
3818
+ }, s.push(y);
3819
+ }
3820
+ const u = Math.min(
3821
+ c.cluster.explosionParticleCount,
3822
+ Math.max(0, 96 - n.length)
3823
+ );
3824
+ for (let d = 0; d < u; d++) {
3825
+ const g = Math.random() * Math.PI * 2, b = 200 + Math.random() * 300;
3826
+ n.push({
3827
+ id: Math.random().toString(),
3828
+ position: { ...r },
3829
+ velocity: { x: Math.cos(g) * b, y: Math.sin(g) * b },
3830
+ color: E.cluster,
3831
+ radius: 4 + Math.random() * 4,
3832
+ alpha: 1,
3833
+ lifetime: 0,
3834
+ maxLifetime: 0.6
3835
+ });
3836
+ }
3837
+ o.length < c.cluster.maxExplosionFloatingTexts && o.push({
3838
+ id: Math.random().toString(),
3839
+ text: "CLUSTER DETONATION",
3840
+ position: { ...r },
3841
+ velocity: { x: 0, y: -60 },
3842
+ color: E.cluster,
3843
+ alpha: 1,
3844
+ lifetime: 0,
3845
+ maxLifetime: 1,
3846
+ size: 24
3847
+ });
3848
+ }
3849
+ }
3850
+ class kt {
3851
+ execute(t) {
3852
+ const { thrownEnemy: e, targetEnemy: i, vortices: s, shockwaves: a } = t, n = t.config, o = {
3853
+ x: (e.position.x + i.position.x) / 2,
3854
+ y: (e.position.y + i.position.y) / 2
3855
+ };
3856
+ s.push({
3857
+ id: Math.random().toString(),
3858
+ position: o,
3859
+ radius: Math.round(n.gravityVortex.baseRadius * 2.2),
3860
+ duration: 0,
3861
+ maxDuration: n.gravityVortex.baseDuration * 1.5,
3862
+ pullForce: n.gravityVortex.basePullForce * 1.6
3863
+ }), a.push({
3864
+ id: Math.random().toString(),
3865
+ position: o,
3866
+ radius: 20,
3867
+ maxRadius: Math.round(n.gravityVortex.baseRadius * 2.2),
3868
+ color: E.parasite,
3869
+ alpha: 1,
3870
+ lifetime: 0,
3871
+ maxLifetime: 0.65
3872
+ }), e.triggerDissolveDeath(!0), i.triggerDissolveDeath(!0);
3873
+ }
3874
+ }
3875
+ class Ut {
3876
+ execute(t) {
3877
+ const { thrownEnemy: e, targetEnemy: i, allEnemies: s, shockwaves: a, particles: n, floatingTexts: o } = t, r = i.position.x - e.position.x, c = i.position.y - e.position.y, l = Math.atan2(c, r), h = t.config.shield, u = h.blastSpeed || 1400, d = h.isFanBlast ? [0, -h.fanBlastAngle, h.fanBlastAngle] : [0];
3878
+ for (const [g, b] of d.entries()) {
3879
+ const f = l + b, _ = g === 0 ? e : t.factory.createEnemy(
3880
+ e.type,
3881
+ {
3882
+ x: e.position.x + Math.cos(f) * 30,
3883
+ y: e.position.y + Math.sin(f) * 30
3884
+ },
3885
+ { x: 0, y: 0 }
3886
+ );
3887
+ _.isStationaryShield = !1, _.isThrown = !0, _.wasManipulated = !0, _.shieldAngle = f, _.rotation = f, _.launchDelay = h.isFanBlast ? g * h.fanBlastInterval : 0, _.velocity = {
3888
+ x: Math.cos(f) * u,
3889
+ y: Math.sin(f) * u
3890
+ }, _ !== e && s.push(_);
3891
+ }
3892
+ i.active = !1, i.triggerDissolveDeath(!0), o.push({
3893
+ id: Math.random().toString(),
3894
+ text: "ARC BLAST!",
3895
+ position: { ...e.position },
3896
+ velocity: { x: 0, y: -50 },
3897
+ color: E.heavy,
3898
+ alpha: 1,
3899
+ lifetime: 0,
3900
+ maxLifetime: 0.9,
3901
+ size: 24
3902
+ });
3903
+ for (let g = 0; g < 50; g++) {
3904
+ const b = (Math.random() - 0.5) * 1.5, f = l + b, _ = 300 + Math.random() * 500;
3905
+ n.push({
3906
+ id: Math.random().toString(),
3907
+ position: { ...e.position },
3908
+ velocity: { x: Math.cos(f) * _, y: Math.sin(f) * _ },
3909
+ color: E.heavy,
3910
+ radius: 4 + Math.random() * 6,
3911
+ alpha: 1,
3912
+ lifetime: 0,
3913
+ maxLifetime: 0.7
3914
+ });
3915
+ }
3916
+ o.push({
3917
+ id: Math.random().toString(),
3918
+ text: "ARC BLAST!",
3919
+ position: { ...e.position },
3920
+ velocity: { x: 0, y: -50 },
3921
+ color: E.heavy,
3922
+ alpha: 1,
3923
+ lifetime: 0,
3924
+ maxLifetime: 0.9,
3925
+ size: 24
3926
+ });
3927
+ }
3928
+ }
3929
+ class Gt {
3930
+ execute(t) {
3931
+ const { thrownEnemy: e, targetEnemy: i, shockwaves: s, particles: a, floatingTexts: n } = t;
3932
+ i.experienceDropPending = !0, i.active = !1, s.push({
3933
+ id: Math.random().toString(),
3934
+ position: { ...i.position },
3935
+ radius: 10,
3936
+ maxRadius: 180,
3937
+ color: E.piercer,
3938
+ alpha: 1,
3939
+ lifetime: 0,
3940
+ maxLifetime: 0.5
3941
+ });
3942
+ for (let o = 0; o < 30; o++) {
3943
+ const r = Math.random() * Math.PI * 2, c = 180 + Math.random() * 320;
3944
+ a.push({
3945
+ id: Math.random().toString(),
3946
+ position: { ...i.position },
3947
+ velocity: { x: Math.cos(r) * c, y: Math.sin(r) * c },
3948
+ color: E.piercer,
3949
+ radius: 3 + Math.random() * 4,
3950
+ alpha: 1,
3951
+ lifetime: 0,
3952
+ maxLifetime: 0.5
3953
+ });
3954
+ }
3955
+ n.push({
3956
+ id: Math.random().toString(),
3957
+ text: "PIERCING SLICE",
3958
+ position: { ...i.position },
3959
+ velocity: { x: 0, y: -50 },
3960
+ color: E.piercer,
3961
+ alpha: 1,
3962
+ lifetime: 0,
3963
+ maxLifetime: 0.8,
3964
+ size: 20
3965
+ });
3966
+ }
3967
+ }
3968
+ class Bt {
3969
+ execute(t) {
3970
+ const { thrownEnemy: e, pools: i, shockwaves: s, particles: a, floatingTexts: n, eventBus: o } = t, r = e.position, c = t.config;
3971
+ i.push({
3972
+ id: Math.random().toString(),
3973
+ position: { ...r },
3974
+ radius: Math.round(c.toxicPool.baseRadius * 2),
3975
+ duration: 0,
3976
+ maxDuration: c.toxicPool.baseDuration * 1.5,
3977
+ damage: c.toxicPool.damage
3978
+ }), s.push({
3979
+ id: Math.random().toString(),
3980
+ position: { ...r },
3981
+ radius: 10,
3982
+ maxRadius: Math.round(c.toxicPool.baseRadius * 2.2),
3983
+ color: E.spore,
3984
+ alpha: 1,
3985
+ lifetime: 0,
3986
+ maxLifetime: 0.6
3987
+ });
3988
+ for (let l = 0; l < 45; l++) {
3989
+ const h = Math.random() * Math.PI * 2, u = 120 + Math.random() * 260;
3990
+ a.push({
3991
+ id: Math.random().toString(),
3992
+ position: { ...r },
3993
+ velocity: { x: Math.cos(h) * u, y: Math.sin(h) * u },
3994
+ color: E.spore,
3995
+ radius: 4 + Math.random() * 5,
3996
+ alpha: 1,
3997
+ lifetime: 0,
3998
+ maxLifetime: 0.9
3999
+ });
4000
+ }
4001
+ n.push({
4002
+ id: Math.random().toString(),
4003
+ text: "MASSIVE SPORE NEBULA",
4004
+ position: { ...r },
4005
+ velocity: { x: 0, y: -60 },
4006
+ color: E.spore,
4007
+ alpha: 1,
4008
+ lifetime: 0,
4009
+ maxLifetime: 1.2,
4010
+ size: 26
4011
+ }), e.experienceDropPending = !0, e.active = !1, o.emit("onPoolCreated", { position: r });
4012
+ }
4013
+ }
4014
+ const W = (p, t, e = []) => {
4015
+ let i = null, s = 1 / 0;
4016
+ for (const a of t) {
4017
+ if (a === p || e.includes(a) || !a.active || a.isGrabbed || a.isDying) continue;
4018
+ const n = Math.hypot(a.position.x - p.position.x, a.position.y - p.position.y);
4019
+ n < s && (s = n, i = a);
4020
+ }
4021
+ return i;
4022
+ }, Y = (p, t, e, i, s = t.color) => {
4023
+ p.shockwaves.push({
4024
+ id: Math.random().toString(),
4025
+ position: { ...t.position },
4026
+ radius: 10,
4027
+ maxRadius: e,
4028
+ color: s,
4029
+ alpha: 1,
4030
+ lifetime: 0,
4031
+ maxLifetime: i
4032
+ });
4033
+ }, et = (p, t, e, i = t.color) => {
4034
+ for (let s = 0; s < e; s++) {
4035
+ const a = Math.random() * Math.PI * 2, n = 180 + Math.random() * 320;
4036
+ p.particles.push({
4037
+ id: Math.random().toString(),
4038
+ position: { ...t.position },
4039
+ velocity: { x: Math.cos(a) * n, y: Math.sin(a) * n },
4040
+ color: i,
4041
+ radius: 3 + Math.random() * 4,
4042
+ alpha: 1,
4043
+ lifetime: 0,
4044
+ maxLifetime: 0.5
4045
+ });
4046
+ }
4047
+ }, Ht = (p, t) => {
4048
+ p.shieldAngle = p.rotation, p.velocity = { x: 0, y: 0 }, p.isStationaryShield = !0, p.isThrown = !1;
4049
+ }, Kt = (p, t) => {
4050
+ t.config.piercer.hasHoming && (p.remainingHomingKills = t.config.piercer.homingTargets);
4051
+ }, Ft = (p, t) => {
4052
+ if (!p.isThrown || !t.config.piercer.hasHoming || p.remainingHomingKills <= 0) return;
4053
+ const e = W(p, t.enemies);
4054
+ if (!e) return;
4055
+ const i = Math.atan2(e.position.y - p.position.y, e.position.x - p.position.x), s = Math.max(Math.hypot(p.velocity.x, p.velocity.y), 1e3), a = Math.atan2(p.velocity.y, p.velocity.x);
4056
+ let n = i - a;
4057
+ for (; n < -Math.PI; ) n += Math.PI * 2;
4058
+ for (; n > Math.PI; ) n -= Math.PI * 2;
4059
+ const o = a + Math.sign(n) * Math.min(Math.abs(n), 25 * t.dt);
4060
+ p.rotation = o, p.velocity = { x: Math.cos(o) * s, y: Math.sin(o) * s };
4061
+ }, Vt = (p, t) => {
4062
+ Y(t, p, 350, 0.6), et(t, p, 40);
4063
+ }, Wt = (p, t) => {
4064
+ const e = t.config.piercer.terminalAoERadius;
4065
+ if (!(e <= 0)) {
4066
+ Y(t, p, e, 0.6);
4067
+ for (const i of t.spatialHash.getNearby(p.position, e)) {
4068
+ if (!i.active || i.isGrabbed || i.isDying) continue;
4069
+ const s = i.position.x - p.position.x, a = i.position.y - p.position.y;
4070
+ s * s + a * a <= e * e && t.killEnemy(i, Math.atan2(a, s));
4071
+ }
4072
+ }
4073
+ }, Yt = (p) => {
4074
+ const { thrownEnemy: t, targetEnemy: e } = p;
4075
+ return t.strategy ? (t.strategy.execute({
4076
+ thrownEnemy: t,
4077
+ targetEnemy: e,
4078
+ allEnemies: p.enemies,
4079
+ pools: p.pools,
4080
+ vortices: p.vortices,
4081
+ shockwaves: p.shockwaves,
4082
+ particles: p.particles,
4083
+ floatingTexts: p.floatingTexts,
4084
+ eventBus: p.eventBus,
4085
+ width: p.width,
4086
+ height: p.height,
4087
+ factory: p.factory,
4088
+ config: p.config
4089
+ }), !t.behavior.continuesThroughTargets && t.active && !t.isDying && t.triggerDissolveDeath(!0), e.active && !e.isDying && e.triggerDissolveDeath(!0), p.currentThrowState.kills += t.behavior.continuesThroughTargets ? 1 : 2, p.onKill?.(), !0) : !1;
4090
+ }, $t = (p) => (p.killEnemy(p.targetEnemy, p.thrownEnemy.rotation), p.thrownEnemy.active && !p.thrownEnemy.isDying && p.thrownEnemy.triggerDissolveDeath(!0), p.currentThrowState.kills += 1, p.juice.triggerHitstop(0.04), p.juice.triggerScreenShake({
4091
+ x: p.targetEnemy.position.x - p.thrownEnemy.position.x,
4092
+ y: p.targetEnemy.position.y - p.thrownEnemy.position.y
4093
+ }, 8), p.onKill(), !0), Zt = (p) => {
4094
+ if (p.killEnemy(p.targetEnemy, p.impactAngle), !Yt(p)) return !1;
4095
+ const { thrownEnemy: t, targetEnemy: e } = p;
4096
+ if (p.juice.triggerHitstop(0.05), p.juice.triggerScreenShake({
4097
+ x: e.position.x - t.position.x,
4098
+ y: e.position.y - t.position.y
4099
+ }, 7), Y(p, e, 180, 0.45), et(p, e, 16), t.remainingHomingKills > 0) {
4100
+ t.remainingHomingKills--;
4101
+ const i = W(t, p.enemies, [e]);
4102
+ if (i) {
4103
+ const s = Math.atan2(i.position.y - t.position.y, i.position.x - t.position.x), a = Math.hypot(t.velocity.x, t.velocity.y) || 1200;
4104
+ t.rotation = s, t.velocity = { x: Math.cos(s) * a, y: Math.sin(s) * a };
4105
+ }
4106
+ }
4107
+ return !0;
4108
+ }, zt = (p) => {
4109
+ p.enemy.triggerDissolveDeath(!0);
4110
+ const t = p.config.toxicPool;
4111
+ p.pools.push({
4112
+ id: Math.random().toString(),
4113
+ position: { ...p.enemy.position },
4114
+ radius: t.baseRadius,
4115
+ duration: 0,
4116
+ maxDuration: t.baseDuration,
4117
+ damage: t.damage
4118
+ });
4119
+ }, Xt = (p) => {
4120
+ p.enemy.triggerDissolveDeath(!0);
4121
+ const t = p.config.cluster, e = p.enemy.behavior.spawnedEnemyIds?.[0];
4122
+ if (e)
4123
+ for (let i = 0; i < t.fragmentCount; i++) {
4124
+ const s = i * Math.PI * 2 / t.fragmentCount, a = {
4125
+ x: p.enemy.position.x + Math.cos(s) * 40,
4126
+ y: p.enemy.position.y + Math.sin(s) * 40
4127
+ };
4128
+ let n = s;
4129
+ if (t.hasHoming) {
4130
+ const r = W(p.enemy, p.enemies);
4131
+ r && (n = Math.atan2(r.position.y - a.y, r.position.x - a.x));
4132
+ }
4133
+ const o = p.factory.createEnemy(e, a, {
4134
+ x: a.x + Math.cos(n) * 500,
4135
+ y: a.y + Math.sin(n) * 500
4136
+ });
4137
+ o.isThrown = !0, o.wasManipulated = !0, o.velocity = { x: Math.cos(n) * (750 + Math.random() * 250), y: Math.sin(n) * (750 + Math.random() * 250) }, p.enemies.push(o);
4138
+ }
4139
+ }, Jt = (p) => {
4140
+ p.enemy.triggerDissolveDeath(!0);
4141
+ const t = p.config.gravityVortex;
4142
+ p.vortices.push({
4143
+ id: Math.random().toString(),
4144
+ position: { ...p.enemy.position },
4145
+ radius: t.baseRadius,
4146
+ duration: 0,
4147
+ maxDuration: t.baseDuration,
4148
+ pullForce: t.basePullForce
4149
+ });
4150
+ }, jt = (p) => {
4151
+ p.enemy.triggerSliceDeath(p.angle, !0);
4152
+ }, qt = (p, t) => t !== "toxic-pool" && t !== "gravity-vortex", Qt = {
4153
+ piercer: {
4154
+ rendererId: "piercer",
4155
+ rotatesWithVelocity: !0,
4156
+ canBeChainReactionTarget: !0,
4157
+ continuesThroughTargets: !0,
4158
+ onRelease: Kt,
4159
+ onThrownUpdate: Ft,
4160
+ onOutOfBounds: Wt,
4161
+ onCollision: Zt,
4162
+ onChainReaction: jt,
4163
+ canInteractWithHazard: qt,
4164
+ synergyKey: (p) => p.color
4165
+ },
4166
+ spore: {
4167
+ rendererId: "spore",
4168
+ rotatesWithVelocity: !0,
4169
+ canBeChainReactionTarget: !0,
4170
+ onChainReaction: zt,
4171
+ synergyKey: (p) => p.color
4172
+ },
4173
+ cluster: {
4174
+ rendererId: "cluster",
4175
+ rotatesWithVelocity: !0,
4176
+ canBeChainReactionTarget: !0,
4177
+ onChainReaction: Xt,
4178
+ spawnedEnemyIds: ["micro"],
4179
+ synergyKey: (p) => p.color
4180
+ },
4181
+ parasite: {
4182
+ rendererId: "parasite",
4183
+ rotatesWithVelocity: !0,
4184
+ canBeChainReactionTarget: !0,
4185
+ onChainReaction: Jt,
4186
+ synergyKey: (p) => p.color
4187
+ },
4188
+ heavy: {
4189
+ rendererId: "heavy",
4190
+ rotatesWithVelocity: !1,
4191
+ canBeChainReactionTarget: !0,
4192
+ onRelease: Ht,
4193
+ onOutOfBounds: Vt,
4194
+ onCollision: $t,
4195
+ synergyKey: (p) => p.color
4196
+ },
4197
+ micro: {
4198
+ rendererId: "circle",
4199
+ rotatesWithVelocity: !0,
4200
+ canBeChainReactionTarget: !1,
4201
+ autoDeflectFromCore: !0,
4202
+ synergyKey: (p) => p.color
4203
+ }
4204
+ }, D = (p) => Qt[p] ?? {
4205
+ rendererId: "circle",
4206
+ rotatesWithVelocity: !0,
4207
+ canBeChainReactionTarget: !0,
4208
+ synergyKey: (t) => t.color
4209
+ }, te = [
4210
+ {
4211
+ id: "piercer",
4212
+ speed: 95,
4213
+ maxSpeed: 140,
4214
+ radius: 18,
4215
+ color: E.piercer,
4216
+ canBeGrabbed: !0,
4217
+ initialRotation: "none",
4218
+ impactStrategyFactory: () => new Gt(),
4219
+ behavior: D("piercer"),
4220
+ spawnWeight: 0.2,
4221
+ ambientSpawn: !0
4222
+ },
4223
+ {
4224
+ id: "spore",
4225
+ speed: 65,
4226
+ maxSpeed: 90,
4227
+ radius: 24,
4228
+ color: E.spore,
4229
+ canBeGrabbed: !0,
4230
+ initialRotation: "none",
4231
+ impactStrategyFactory: () => new Bt(),
4232
+ behavior: D("spore"),
4233
+ spawnWeight: 0.2,
4234
+ ambientSpawn: !0
4235
+ },
4236
+ {
4237
+ id: "cluster",
4238
+ speed: 50,
4239
+ maxSpeed: 75,
4240
+ radius: 26,
4241
+ color: E.cluster,
4242
+ canBeGrabbed: !0,
4243
+ initialRotation: "none",
4244
+ impactStrategyFactory: () => new Nt(),
4245
+ behavior: D("cluster"),
4246
+ spawnWeight: 0.2,
4247
+ ambientSpawn: !0
4248
+ },
4249
+ {
4250
+ id: "parasite",
4251
+ speed: 80,
4252
+ maxSpeed: 110,
4253
+ radius: 22,
4254
+ color: E.parasite,
4255
+ canBeGrabbed: !0,
4256
+ initialRotation: "none",
4257
+ impactStrategyFactory: () => new kt(),
4258
+ behavior: D("parasite"),
4259
+ spawnWeight: 0.2,
4260
+ ambientSpawn: !0
4261
+ },
4262
+ {
4263
+ id: "heavy",
4264
+ speed: 35,
4265
+ maxSpeed: 50,
4266
+ radius: 34,
4267
+ color: E.heavy,
4268
+ canBeGrabbed: !0,
4269
+ initialRotation: "random",
4270
+ impactStrategyFactory: () => new Ut(),
4271
+ behavior: D("heavy"),
4272
+ spawnWeight: 0.2,
4273
+ ambientSpawn: !0
4274
+ },
4275
+ {
4276
+ id: "micro",
4277
+ speed: 130,
4278
+ maxSpeed: 160,
4279
+ radius: 12,
4280
+ color: E.micro,
4281
+ canBeGrabbed: !1,
4282
+ initialRotation: "none",
4283
+ behavior: D("micro"),
4284
+ spawnWeight: 0,
4285
+ ambientSpawn: !1
4286
+ }
4287
+ ], ee = new Dt({
4288
+ enemies: te,
4289
+ spawnerPatterns: [
4290
+ { id: "circle", algorithmId: "circle", enemyIds: ["piercer", "spore", "cluster", "parasite"], weight: 1 },
4291
+ { id: "dense", algorithmId: "dense", enemyIds: ["spore"], weight: 1 },
4292
+ { id: "sine", algorithmId: "sine", enemyIds: ["cluster"], weight: 1 },
4293
+ { id: "marching", algorithmId: "marching", enemyIds: ["piercer"], weight: 1 },
4294
+ { id: "spiral", algorithmId: "spiral", enemyIds: ["parasite"], weight: 0.5 },
4295
+ { id: "crossfire", algorithmId: "crossfire", enemyIds: ["spore", "cluster"], weight: 0.5 }
4296
+ ],
4297
+ tutorial: { launcherEnemyId: "piercer", targetEnemyId: "spore" }
4298
+ });
4299
+ class ie {
3698
4300
  enabled;
3699
4301
  overlay;
3700
4302
  copyButton;
@@ -3812,7 +4414,7 @@ HEAP ${(t.usedJSHeapSize / 1024 / 1024).toFixed(1)} MB` : "";
3812
4414
  `), this.overlay.textContent = this.lastReport;
3813
4415
  }
3814
4416
  }
3815
- class It {
4417
+ class se {
3816
4418
  container;
3817
4419
  canvas;
3818
4420
  i18n;
@@ -3832,6 +4434,10 @@ class It {
3832
4434
  tutorialOverlay;
3833
4435
  progressBar;
3834
4436
  performanceAudit;
4437
+ content = ee;
4438
+ entityFactory;
4439
+ configManager = new N();
4440
+ powerUpRegistry = new wt(this.configManager);
3835
4441
  core;
3836
4442
  enemies = [];
3837
4443
  ambientEnemies = [];
@@ -3852,24 +4458,27 @@ class It {
3852
4458
  isRunning = !1;
3853
4459
  isPaused = !1;
3854
4460
  constructor(t) {
3855
- this.container = t.root, this.container.classList.add("axon-game-root"), this.i18n = new X(t.language), this.themeManager = new pt(t.theme, this.container), this.platform = t.platform || new At(), this.canvas = document.createElement("canvas"), this.canvas.className = "axon-canvas", this.container.appendChild(this.canvas), this.resizeCanvas(), window.addEventListener("resize", () => this.resizeCanvas()), this.eventBus = new W(), this.core = new Z(this.canvas.width / 2, this.canvas.height / 2), this.inputManager = new $(this.canvas), this.physicsEngine = new rt(this.eventBus, this.i18n), this.waveDirector = new ct(), this.renderer = new _t(this.canvas, this.themeManager), this.juice = new J(), this.settingsModal = new xt(
4461
+ this.container = t.root, this.container.classList.add("axon-game-root"), this.i18n = new ct(t.language), this.themeManager = new yt(t.theme, this.container), this.platform = t.platform || new Ot(), this.canvas = document.createElement("canvas"), this.canvas.className = "axon-canvas", this.container.appendChild(this.canvas), this.resizeCanvas(), window.addEventListener("resize", () => this.resizeCanvas()), this.eventBus = new it(), this.entityFactory = new Rt(this.content), this.core = new at(this.canvas.width / 2, this.canvas.height / 2, this.configManager), this.inputManager = new ot(this.canvas, this.configManager), this.physicsEngine = new gt(this.eventBus, this.i18n, this.entityFactory, this.configManager), this.waveDirector = new _t(this.entityFactory, this.content), this.renderer = new Mt(this.canvas, this.themeManager, this.configManager), this.juice = new rt(), this.settingsModal = new Tt(
3856
4462
  this.container,
3857
4463
  this.i18n,
3858
4464
  this.themeManager,
3859
4465
  this.platform,
3860
- (e) => this.onLanguageChanged(e),
4466
+ (i) => this.onLanguageChanged(i),
3861
4467
  () => {
3862
4468
  this.isPaused = !1;
3863
4469
  },
3864
- (e) => {
3865
- this.gameLoop.setFpsLimit(e), this.performanceAudit.setFpsLimit(e);
4470
+ (i) => {
4471
+ this.gameLoop.setFpsLimit(i), this.performanceAudit.setFpsLimit(i);
3866
4472
  }
3867
- ), this.interactiveTutorial = new yt(), this.tutorialOverlay = new Et(this.container, this.i18n, this.interactiveTutorial), this.hud = new mt(this.container, this.i18n, () => {
4473
+ );
4474
+ const e = this.content.tutorial;
4475
+ if (!e) throw new Error("Content catalog must define tutorial enemy IDs");
4476
+ this.interactiveTutorial = new Lt(this.entityFactory, e.launcherEnemyId, e.targetEnemyId), this.tutorialOverlay = new Ct(this.container, this.i18n, this.interactiveTutorial), this.hud = new St(this.container, () => {
3868
4477
  this.isPaused = !0, this.settingsModal.show();
3869
- }), this.hud.show(), this.progressBar = new bt(this.hud.progressSlot, (e) => {
3870
- this.waveDirector.wave = e, this.triggerLevelUp(e);
3871
- }), this.performanceAudit = new vt(this.container, this.debugPerformanceEnabled), this.startMenu = new ft(this.container, this.i18n, () => this.startGame()), this.initAmbientArena(), this.gameLoop = new Y(
3872
- (e) => this.update(e),
4478
+ }), this.hud.show(), this.progressBar = new Pt((i) => {
4479
+ this.waveDirector.wave = i, this.triggerLevelUp(i);
4480
+ }), this.performanceAudit = new ie(this.container, this.debugPerformanceEnabled), this.startMenu = new It(this.container, this.i18n, () => this.startGame()), this.initAmbientArena(), this.gameLoop = new st(
4481
+ (i) => this.update(i),
3873
4482
  () => this.render()
3874
4483
  ), this.gameLoop.setFpsLimit(this.settingsModal.getFpsLimit()), this.performanceAudit.setFpsLimit(this.settingsModal.getFpsLimit()), this.setupEvents(), this.gameLoop.start();
3875
4484
  }
@@ -3886,20 +4495,19 @@ class It {
3886
4495
  return this.themeManager.getTheme();
3887
4496
  }
3888
4497
  onLanguageChanged(t) {
3889
- this.startMenu.renderContent(), this.hud.renderLabels(), this.tutorialOverlay.updateLanguage();
4498
+ this.startMenu.renderContent(), this.tutorialOverlay.updateLanguage();
3890
4499
  }
3891
4500
  initAmbientArena() {
3892
4501
  this.ambientEnemies = [];
3893
- const t = ["piercer", "spore", "cluster", "parasite"];
3894
- for (let e = 0; e < 16; e++) {
3895
- const i = t[e % t.length], s = e * Math.PI * 2 / 16, a = 180 + Math.random() * 220, l = {
4502
+ for (let t = 0; t < 16; t++) {
4503
+ const e = this.content.enemies.values().filter((r) => r.ambientSpawn).map((r) => r.id), i = e[t % e.length], s = t * Math.PI * 2 / 16, a = 180 + Math.random() * 220, n = {
3896
4504
  x: this.canvas.width / 2 + Math.cos(s) * a,
3897
4505
  y: this.canvas.height / 2 + Math.sin(s) * a
3898
- }, n = C.createEnemy(i, l, {
3899
- x: l.x + Math.sin(s) * 100,
3900
- y: l.y - Math.cos(s) * 100
4506
+ }, o = this.entityFactory.createEnemy(i, n, {
4507
+ x: n.x + Math.sin(s) * 100,
4508
+ y: n.y - Math.cos(s) * 100
3901
4509
  });
3902
- this.ambientEnemies.push(n);
4510
+ this.ambientEnemies.push(o);
3903
4511
  }
3904
4512
  }
3905
4513
  resizeCanvas() {
@@ -3910,7 +4518,7 @@ class It {
3910
4518
  this.eventBus.on("onCoreDamaged", () => {
3911
4519
  this.settingsModal.isHapticsEnabled() && this.platform.haptics.notification("error");
3912
4520
  }), this.eventBus.on("onGameOver", () => {
3913
- this.isRunning = !1, this.experienceOrbs = [], this.pendingLevelUps = [], this.tutorialOverlay.hide(), this.progressBar.hide(), this.initAmbientArena(), this.startMenu.show();
4521
+ this.isRunning = !1, this.clearTransientRunState(), this.experienceOrbs = [], this.pendingLevelUps = [], this.tutorialOverlay.hide(), this.initAmbientArena(), this.startMenu.show();
3914
4522
  });
3915
4523
  const t = (i = !1) => {
3916
4524
  if (!this.isRunning || this.isPaused || this.isLevelUpActive) return;
@@ -3926,7 +4534,10 @@ class It {
3926
4534
  }), this.canvas.addEventListener("touchstart", () => t(!0), { passive: !0 }), this.canvas.addEventListener("touchend", e, { passive: !0 }), this.canvas.addEventListener("touchcancel", e, { passive: !0 });
3927
4535
  }
3928
4536
  startGame() {
3929
- A.getInstance().resetToDefault(), this.score.value = 0, this.combo.value = 0, this.enemies = [], this.ambientEnemies = [], this.pools = [], this.vortices = [], this.shockwaves = [], this.particles = [], this.floatingTexts = [], this.experienceOrbs = [], this.orbitingOrbs = [], this.isLevelUpActive = !1, this.pendingLevelUps = [], this.rerollsRemaining = this.getRerollLimit(), this.core.reset(this.canvas.width / 2, this.canvas.height / 2), this.waveDirector.reset(), this.inputManager.reset(), this.hud.show(), this.hud.update(1, 0), this.progressBar.reset(), this.progressBar.show(), this.isPaused = !1, this.isRunning = !0, this.interactiveTutorial.isTutorialEnabled() ? (this.interactiveTutorial.start(this.canvas.width, this.canvas.height, this.enemies), this.tutorialOverlay.show()) : (this.interactiveTutorial.isActive = !1, this.tutorialOverlay.hide());
4537
+ this.configManager.resetToDefault(), this.score.value = 0, this.combo.value = 0, this.clearTransientRunState(), this.enemies = [], this.ambientEnemies = [], this.experienceOrbs = [], this.orbitingOrbs = [], this.isLevelUpActive = !1, this.pendingLevelUps = [], this.rerollsRemaining = this.getRerollLimit(), this.core.reset(this.canvas.width / 2, this.canvas.height / 2), this.waveDirector.reset(), this.inputManager.reset(), this.hud.show(), this.hud.update(0), this.progressBar.reset(), this.isPaused = !1, this.isRunning = !0, this.interactiveTutorial.isTutorialEnabled() ? (this.interactiveTutorial.start(this.canvas.width, this.canvas.height, this.enemies), this.tutorialOverlay.show()) : (this.interactiveTutorial.isActive = !1, this.tutorialOverlay.hide());
4538
+ }
4539
+ clearTransientRunState() {
4540
+ this.enemies = [], this.pools = [], this.vortices = [], this.shockwaves = [], this.particles = [], this.floatingTexts = [], this.experienceOrbs = [], this.orbitingOrbs = [], this.pendingLevelUps = [], this.inputManager?.forceRelease();
3930
4541
  }
3931
4542
  update(t) {
3932
4543
  if (this.performanceAudit.beginFrame(), this.performanceAudit.setState(this.isRunning ? this.isPaused ? "PAUSED" : "GAME" : "MENU"), this.core.update(t), !this.isRunning || this.isPaused) {
@@ -3969,16 +4580,16 @@ class It {
3969
4580
  !this.inputManager.isPointerDown && i && (i.isGrabbed = !1, this.inputManager.grabbedOrb = null);
3970
4581
  const s = this.inputManager.pointerPosition;
3971
4582
  if (this.inputManager.isPointerDown && !this.inputManager.grabbedOrb && this.rerollsRemaining > 0) {
3972
- const a = this.canvas.width / 2, l = this.canvas.height / 2 + 210;
3973
- Math.hypot(s.x - a, s.y - l) <= 80 && (this.triggerReroll(), this.inputManager.isPointerDown = !1);
4583
+ const a = this.canvas.width / 2, n = this.canvas.height / 2 + 210;
4584
+ Math.hypot(s.x - a, s.y - n) <= 80 && (this.triggerReroll(), this.inputManager.isPointerDown = !1);
3974
4585
  }
3975
4586
  for (const a of this.orbitingOrbs) {
3976
- const l = s.x - a.position.x, n = s.y - a.position.y;
3977
- if (a.isHovered = Math.hypot(l, n) <= a.radius * 1.5, a === i && this.inputManager.isPointerDown) {
4587
+ const n = s.x - a.position.x, o = s.y - a.position.y;
4588
+ if (a.isHovered = Math.hypot(n, o) <= a.radius * 1.5, a === i && this.inputManager.isPointerDown) {
3978
4589
  a.position = { ...this.inputManager.pointerPosition };
3979
- const o = this.core.position.x - a.position.x, r = this.core.position.y - a.position.y;
3980
- if (Math.hypot(o, r) <= this.core.radius + a.radius) {
3981
- a.definition.apply(this.core), this.core.absorbPowerUpColor(a.definition.color, 0.4), this.juice.triggerFlash(0.8), this.juice.triggerScreenShake({ x: o, y: r }, 24);
4590
+ const r = this.core.position.x - a.position.x, c = this.core.position.y - a.position.y;
4591
+ if (Math.hypot(r, c) <= this.core.radius + a.radius) {
4592
+ a.definition.apply(this.core), this.core.absorbPowerUpColor(a.definition.color, 0.4), this.juice.triggerFlash(0.8), this.juice.triggerScreenShake({ x: r, y: c }, 24);
3982
4593
  const h = this.i18n.translate(a.definition.titleKey);
3983
4594
  this.floatingTexts.push({
3984
4595
  id: Math.random().toString(),
@@ -3991,12 +4602,12 @@ class It {
3991
4602
  maxLifetime: 1.6,
3992
4603
  size: 26
3993
4604
  });
3994
- for (let p = 0; p < 45; p++) {
3995
- const d = Math.random() * Math.PI * 2, _ = 200 + Math.random() * 400;
4605
+ for (let u = 0; u < 45; u++) {
4606
+ const d = Math.random() * Math.PI * 2, g = 200 + Math.random() * 400;
3996
4607
  this.particles.push({
3997
4608
  id: Math.random().toString(),
3998
4609
  position: { ...this.core.position },
3999
- velocity: { x: Math.cos(d) * _, y: Math.sin(d) * _ },
4610
+ velocity: { x: Math.cos(d) * g, y: Math.sin(d) * g },
4000
4611
  color: a.definition.color,
4001
4612
  radius: 4 + Math.random() * 5,
4002
4613
  alpha: 1,
@@ -4033,7 +4644,7 @@ class It {
4033
4644
  text: "RING RESTORED!",
4034
4645
  position: { ...this.core.position },
4035
4646
  velocity: { x: 0, y: -60 },
4036
- color: "#00f5ff",
4647
+ color: E.core,
4037
4648
  alpha: 1,
4038
4649
  lifetime: 0,
4039
4650
  maxLifetime: 1.2,
@@ -4042,7 +4653,7 @@ class It {
4042
4653
  }
4043
4654
  ), e = this.debugPerformanceEnabled ? performance.now() - i : 0;
4044
4655
  }
4045
- this.spawnExperienceDrops(), this.isLevelUpActive || this.updateExperienceOrbs(t), this.hud.update(this.progressBar.getLevel(), this.combo.value), this.juice.update(t, this.floatingTexts, this.particles), this.enemies = this.enemies.filter((i) => i.active), this.performanceAudit.finishUpdate(e, {
4656
+ this.spawnExperienceDrops(), this.isLevelUpActive || this.updateExperienceOrbs(t), this.progressBar.update(t), this.hud.update(this.combo.value), this.juice.update(t, this.floatingTexts, this.particles), this.enemies = this.enemies.filter((i) => i.active), this.performanceAudit.finishUpdate(e, {
4046
4657
  enemies: this.enemies.length,
4047
4658
  particles: this.particles.length,
4048
4659
  shockwaves: this.shockwaves.length,
@@ -4065,7 +4676,7 @@ class It {
4065
4676
  text: this.i18n.translate("levelUpChoose", { level: t }),
4066
4677
  position: { ...this.core.position },
4067
4678
  velocity: { x: 0, y: -100 },
4068
- color: "#00f0ff",
4679
+ color: E.core,
4069
4680
  alpha: 1,
4070
4681
  lifetime: 0,
4071
4682
  maxLifetime: 2,
@@ -4077,55 +4688,60 @@ class It {
4077
4688
  id: Math.random().toString(),
4078
4689
  position: { ...this.core.position },
4079
4690
  velocity: { x: Math.cos(s) * a, y: Math.sin(s) * a },
4080
- color: "#00f0ff",
4691
+ color: E.core,
4081
4692
  radius: 2 + Math.random() * 3,
4082
4693
  alpha: 1,
4083
4694
  lifetime: 0,
4084
4695
  maxLifetime: 0.7
4085
4696
  });
4086
4697
  }
4087
- const e = F.getRandomOptions(3);
4698
+ const e = this.powerUpRegistry.getRandomOptions(3);
4088
4699
  this.orbitingOrbs = e.map((i, s) => {
4089
4700
  const a = s * Math.PI * 2 / e.length;
4090
- return new H(`orb_${Date.now()}_${s}`, i, a);
4701
+ return new j(`orb_${Date.now()}_${s}`, i, a);
4091
4702
  }), this.rerollsRemaining = this.getRerollLimit();
4092
4703
  }
4093
4704
  spawnExperienceDrops() {
4094
4705
  for (const t of this.enemies)
4095
- t.experienceDropPending && (t.experienceDropPending = !1, this.experienceOrbs.push(new ht(t.position)));
4706
+ t.experienceDropPending && (t.experienceDropPending = !1, this.experienceOrbs.push(new k(t.position, 1, this.configManager.getConfig().experience)));
4096
4707
  }
4097
4708
  updateExperienceOrbs(t) {
4098
- const e = A.getInstance().getConfig().magnet;
4099
- for (const i of this.experienceOrbs)
4100
- if (i.active) {
4101
- if (i.isAbsorbing || Math.hypot(
4102
- this.core.position.x - i.position.x,
4103
- this.core.position.y - i.position.y
4104
- ) <= this.core.getHitboxRadius() + e.corePickupRadius && i.beginAbsorption(), !i.isAbsorbing) {
4105
- const s = this.inputManager.pointerPosition.x - i.position.x, a = this.inputManager.pointerPosition.y - i.position.y, l = e.psionicRadius + e.pickupRadius + i.radius;
4106
- Math.hypot(s, a) <= l && i.beginAbsorption();
4709
+ const e = this.configManager.getConfig(), i = e.magnet;
4710
+ k.attractNearby(this.experienceOrbs, e.experience, t), k.mergeNearby(this.experienceOrbs, e.experience.mergeDistance);
4711
+ for (const s of this.experienceOrbs)
4712
+ if (s.active) {
4713
+ if (s.isMerging) {
4714
+ s.update(t, this.core.position, this.core.getHitboxRadius(), i.absorptionSpeed);
4715
+ continue;
4716
+ }
4717
+ if (s.isAbsorbing || Math.hypot(
4718
+ this.core.position.x - s.position.x,
4719
+ this.core.position.y - s.position.y
4720
+ ) <= this.core.getHitboxRadius() + i.corePickupRadius && s.beginAbsorption(), !s.isAbsorbing) {
4721
+ const a = this.inputManager.pointerPosition.x - s.position.x, n = this.inputManager.pointerPosition.y - s.position.y, o = i.psionicRadius + i.pickupRadius + s.radius;
4722
+ Math.hypot(a, n) <= o && s.beginAbsorption();
4107
4723
  }
4108
- i.update(t, this.core.position, this.core.getHitboxRadius(), e.absorptionSpeed), i.active || (this.progressBar.addExperience(i.value), this.juice.triggerFlash(0.12), this.floatingTexts.push({
4724
+ s.update(t, this.core.position, this.core.getHitboxRadius(), i.absorptionSpeed), !s.active && s.absorbedByCore && (this.progressBar.addExperience(s.value), this.juice.triggerFlash(0.12), this.floatingTexts.push({
4109
4725
  id: Math.random().toString(),
4110
- text: `+${i.value} XP`,
4726
+ text: `+${s.value} XP`,
4111
4727
  position: { ...this.core.position },
4112
4728
  velocity: { x: 0, y: -55 },
4113
- color: "#fbbf24",
4729
+ color: s.color,
4114
4730
  alpha: 1,
4115
4731
  lifetime: 0,
4116
4732
  maxLifetime: 0.8,
4117
4733
  size: 18
4118
4734
  }));
4119
4735
  }
4120
- this.experienceOrbs = this.experienceOrbs.filter((i) => i.active);
4736
+ this.experienceOrbs = this.experienceOrbs.filter((s) => s.active);
4121
4737
  }
4122
4738
  triggerReroll() {
4123
4739
  if (this.rerollsRemaining <= 0) return;
4124
4740
  this.rerollsRemaining -= 1, this.juice.triggerFlash(0.4);
4125
- const t = F.getRandomOptions(3);
4741
+ const t = this.powerUpRegistry.getRandomOptions(3);
4126
4742
  this.orbitingOrbs = t.map((e, i) => {
4127
4743
  const s = i * Math.PI * 2 / t.length;
4128
- return new H(`orb_${Date.now()}_${i}`, e, s);
4744
+ return new j(`orb_${Date.now()}_${i}`, e, s);
4129
4745
  });
4130
4746
  }
4131
4747
  render() {
@@ -4143,6 +4759,8 @@ class It {
4143
4759
  this.inputManager.grabbedEnemy,
4144
4760
  this.inputManager.pointerPosition,
4145
4761
  this.progressBar.getLevel(),
4762
+ this.progressBar.getProgress(),
4763
+ this.progressBar.getLevelUpPulse(),
4146
4764
  this.interactiveTutorial.isActive ? this.interactiveTutorial : void 0
4147
4765
  ), this.isLevelUpActive && this.orbitingOrbs.length > 0 && this.renderer.drawOrbitingModifierOrbs(
4148
4766
  this.orbitingOrbs,
@@ -4159,14 +4777,14 @@ class It {
4159
4777
  this.gameLoop.stop(), this.performanceAudit.destroy(), this.canvas.remove();
4160
4778
  }
4161
4779
  }
4162
- function St(E) {
4163
- return new It(E);
4780
+ function ae(p) {
4781
+ return new se(p);
4164
4782
  }
4165
4783
  export {
4166
- At as BrowserPlatform,
4167
- It as GameEngine,
4168
- V as LANGUAGES,
4169
- pt as ThemeManager,
4170
- St as createAxonSurge,
4171
- w as isLanguage
4784
+ Ot as BrowserPlatform,
4785
+ se as GameEngine,
4786
+ tt as LANGUAGES,
4787
+ yt as ThemeManager,
4788
+ ae as createAxonSurge,
4789
+ B as isLanguage
4172
4790
  };