@almadar/ui 5.85.0 → 5.87.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.
@@ -1270,12 +1270,12 @@ function kebabToPascal(name) {
1270
1270
  }).join("");
1271
1271
  }
1272
1272
  function loadLib(key, importer) {
1273
- let p2 = libPromises.get(key);
1274
- if (!p2) {
1275
- p2 = importer();
1276
- libPromises.set(key, p2);
1273
+ let p = libPromises.get(key);
1274
+ if (!p) {
1275
+ p = importer();
1276
+ libPromises.set(key, p);
1277
1277
  }
1278
- return p2;
1278
+ return p;
1279
1279
  }
1280
1280
  function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
1281
1281
  const Lazy = React74__namespace.default.lazy(async () => {
@@ -5373,28 +5373,28 @@ var init_ConfettiEffect = __esm({
5373
5373
  ),
5374
5374
  "aria-hidden": "true",
5375
5375
  children: [
5376
- particles.map((p2) => {
5377
- const rad = p2.angle * Math.PI / 180;
5378
- const tx = Math.cos(rad) * p2.distance;
5379
- const ty = Math.sin(rad) * p2.distance - 20;
5376
+ particles.map((p) => {
5377
+ const rad = p.angle * Math.PI / 180;
5378
+ const tx = Math.cos(rad) * p.distance;
5379
+ const ty = Math.sin(rad) * p.distance - 20;
5380
5380
  return /* @__PURE__ */ jsxRuntime.jsx(
5381
5381
  exports.Box,
5382
5382
  {
5383
5383
  className: "absolute rounded-sm",
5384
5384
  style: {
5385
- left: `${p2.left}%`,
5385
+ left: `${p.left}%`,
5386
5386
  top: "50%",
5387
- width: p2.size,
5388
- height: p2.size,
5389
- backgroundColor: p2.color,
5390
- animation: `confetti-burst ${duration - p2.delay}ms ease-out ${p2.delay}ms forwards`,
5387
+ width: p.size,
5388
+ height: p.size,
5389
+ backgroundColor: p.color,
5390
+ animation: `confetti-burst ${duration - p.delay}ms ease-out ${p.delay}ms forwards`,
5391
5391
  opacity: 0,
5392
5392
  "--confetti-tx": `${tx}px`,
5393
5393
  "--confetti-ty": `${ty}px`,
5394
- "--confetti-rotate": `${p2.rotation}deg`
5394
+ "--confetti-rotate": `${p.rotation}deg`
5395
5395
  }
5396
5396
  },
5397
- p2.id
5397
+ p.id
5398
5398
  );
5399
5399
  }),
5400
5400
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
@@ -6305,8 +6305,8 @@ var init_AnimatedGraphic = __esm({
6305
6305
  const el = containerRef.current;
6306
6306
  if (!el || !strokeColor) return;
6307
6307
  const paths = el.querySelectorAll("path, line, polyline, polygon, circle, ellipse, rect");
6308
- paths.forEach((p2) => {
6309
- p2.style.stroke = strokeColor;
6308
+ paths.forEach((p) => {
6309
+ p.style.stroke = strokeColor;
6310
6310
  });
6311
6311
  }, [resolvedSvg, strokeColor]);
6312
6312
  React74.useEffect(() => {
@@ -6314,23 +6314,23 @@ var init_AnimatedGraphic = __esm({
6314
6314
  if (!el || !resolvedSvg) return;
6315
6315
  if (animation === "draw" || animation === "fill") {
6316
6316
  const paths = el.querySelectorAll("path, line, polyline, polygon, circle, ellipse, rect");
6317
- paths.forEach((p2) => {
6318
- if ("getTotalLength" in p2 && typeof p2.getTotalLength === "function") {
6319
- const len = p2.getTotalLength();
6320
- p2.style.strokeDasharray = `${len}`;
6321
- p2.style.strokeDashoffset = `${len}`;
6317
+ paths.forEach((p) => {
6318
+ if ("getTotalLength" in p && typeof p.getTotalLength === "function") {
6319
+ const len = p.getTotalLength();
6320
+ p.style.strokeDasharray = `${len}`;
6321
+ p.style.strokeDashoffset = `${len}`;
6322
6322
  }
6323
6323
  if (animation === "fill") {
6324
- p2.style.fillOpacity = "0";
6324
+ p.style.fillOpacity = "0";
6325
6325
  }
6326
6326
  });
6327
6327
  }
6328
6328
  if (animation === "morph") {
6329
6329
  const paths = el.querySelectorAll("path, circle, ellipse, rect, polygon");
6330
- paths.forEach((p2) => {
6331
- p2.style.transform = "scale(0)";
6332
- p2.style.transformOrigin = "center";
6333
- p2.style.opacity = "0";
6330
+ paths.forEach((p) => {
6331
+ p.style.transform = "scale(0)";
6332
+ p.style.transformOrigin = "center";
6333
+ p.style.opacity = "0";
6334
6334
  });
6335
6335
  }
6336
6336
  }, [resolvedSvg, animation]);
@@ -9600,8 +9600,8 @@ function shapeBounds(shape) {
9600
9600
  case "polygon":
9601
9601
  if (!shape.points || shape.points.length === 0) return null;
9602
9602
  {
9603
- const xs = shape.points.map((p2) => p2.x);
9604
- const ys = shape.points.map((p2) => p2.y);
9603
+ const xs = shape.points.map((p) => p.x);
9604
+ const ys = shape.points.map((p) => p.y);
9605
9605
  const minX = Math.min(...xs);
9606
9606
  const minY = Math.min(...ys);
9607
9607
  return {
@@ -9727,13 +9727,13 @@ function drawShape(ctx, shape, width, height) {
9727
9727
  }
9728
9728
  case "path": {
9729
9729
  if (!shape.path) break;
9730
- const p2 = new Path2D(shape.path);
9730
+ const p = new Path2D(shape.path);
9731
9731
  if (fill) {
9732
9732
  ctx.fillStyle = fill;
9733
- ctx.fill(p2);
9733
+ ctx.fill(p);
9734
9734
  }
9735
9735
  ctx.strokeStyle = stroke;
9736
- ctx.stroke(p2);
9736
+ ctx.stroke(p);
9737
9737
  break;
9738
9738
  }
9739
9739
  case "text": {
@@ -12790,7 +12790,7 @@ function renderStateMachineToDomData(stateMachine, options = {}, config = DEFAUL
12790
12790
  }
12791
12791
  let domOutputs;
12792
12792
  if (outputs.length > 0) {
12793
- const maxX = Math.max(...Object.values(positions).map((p2) => p2.x));
12793
+ const maxX = Math.max(...Object.values(positions).map((p) => p.x));
12794
12794
  const maxOutputLength = Math.max(...outputs.map((o) => o.length));
12795
12795
  const boxWidth = Math.max(200, maxOutputLength * 7 + 30);
12796
12796
  const lineHeight = 22;
@@ -13772,7 +13772,7 @@ var init_BookViewer = __esm({
13772
13772
  }
13773
13773
  });
13774
13774
  function BoxPattern({
13775
- p: p2,
13775
+ p,
13776
13776
  m,
13777
13777
  bg = "transparent",
13778
13778
  border = false,
@@ -13785,7 +13785,7 @@ function BoxPattern({
13785
13785
  return /* @__PURE__ */ jsxRuntime.jsx(
13786
13786
  exports.Box,
13787
13787
  {
13788
- padding: p2,
13788
+ padding: p,
13789
13789
  margin: m,
13790
13790
  bg,
13791
13791
  border,
@@ -15700,13 +15700,13 @@ function useRenderInterpolation(options = {}) {
15700
15700
  const prevMap = /* @__PURE__ */ new Map();
15701
15701
  for (const e of prev.entities) prevMap.set(e.id, e);
15702
15702
  for (const c of curr.entities) {
15703
- const p2 = prevMap.get(c.id);
15704
- if (!p2) {
15703
+ const p = prevMap.get(c.id);
15704
+ if (!p) {
15705
15705
  out.set(c.id, { x: c.x, y: c.y });
15706
15706
  } else {
15707
15707
  out.set(c.id, {
15708
- x: p2.x + (c.x - p2.x) * alpha,
15709
- y: p2.y + (c.y - p2.y) * alpha
15708
+ x: p.x + (c.x - p.x) * alpha,
15709
+ y: p.y + (c.y - p.y) * alpha
15710
15710
  });
15711
15711
  }
15712
15712
  }
@@ -16455,8 +16455,8 @@ function Canvas2D({
16455
16455
  if (isFree) return 0;
16456
16456
  return (gridHeight - 1) * (scaledTileWidth / 2);
16457
16457
  }, [isFree, gridHeight, scaledTileWidth]);
16458
- const validMoveSet = React74.useMemo(() => new Set(validMoves.map((p2) => `${p2.x},${p2.y}`)), [validMoves]);
16459
- const attackTargetSet = React74.useMemo(() => new Set(attackTargets.map((p2) => `${p2.x},${p2.y}`)), [attackTargets]);
16458
+ const validMoveSet = React74.useMemo(() => new Set(validMoves.map((p) => `${p.x},${p.y}`)), [validMoves]);
16459
+ const attackTargetSet = React74.useMemo(() => new Set(attackTargets.map((p) => `${p.x},${p.y}`)), [attackTargets]);
16460
16460
  const spriteUrls = React74.useMemo(() => {
16461
16461
  const urls = [];
16462
16462
  for (const tile of sortedTiles) {
@@ -17174,1278 +17174,6 @@ var init_Canvas2D = __esm({
17174
17174
  }
17175
17175
  });
17176
17176
 
17177
- // components/game/shared/effects.ts
17178
- exports.EMPTY_EFFECT_STATE = void 0;
17179
- var init_effects = __esm({
17180
- "components/game/shared/effects.ts"() {
17181
- exports.EMPTY_EFFECT_STATE = {
17182
- particles: [],
17183
- sequences: [],
17184
- overlays: []
17185
- };
17186
- }
17187
- });
17188
-
17189
- // components/game/shared/canvasEffects.ts
17190
- function getOffscreenCtx(w, h) {
17191
- if (!_offscreen) {
17192
- if (typeof OffscreenCanvas !== "undefined") {
17193
- _offscreen = new OffscreenCanvas(w, h);
17194
- } else {
17195
- _offscreen = document.createElement("canvas");
17196
- }
17197
- }
17198
- if (_offscreen.width < w) _offscreen.width = w;
17199
- if (_offscreen.height < h) _offscreen.height = h;
17200
- if (!_offCtx) {
17201
- _offCtx = _offscreen.getContext("2d");
17202
- }
17203
- return _offCtx;
17204
- }
17205
- function drawTintedImage(ctx, img, x, y, w, h, tint, alpha, blendMode = "source-over") {
17206
- if (w <= 0 || h <= 0) return;
17207
- const oc = getOffscreenCtx(w, h);
17208
- oc.clearRect(0, 0, w, h);
17209
- oc.globalCompositeOperation = "source-over";
17210
- oc.drawImage(img, 0, 0, w, h);
17211
- oc.globalCompositeOperation = "source-atop";
17212
- oc.fillStyle = `rgb(${tint.r}, ${tint.g}, ${tint.b})`;
17213
- oc.fillRect(0, 0, w, h);
17214
- const prevAlpha = ctx.globalAlpha;
17215
- const prevBlend = ctx.globalCompositeOperation;
17216
- ctx.globalAlpha = alpha;
17217
- ctx.globalCompositeOperation = blendMode;
17218
- ctx.drawImage(_offscreen, 0, 0, w, h, x, y, w, h);
17219
- ctx.globalAlpha = prevAlpha;
17220
- ctx.globalCompositeOperation = prevBlend;
17221
- }
17222
- function randRange(min, max) {
17223
- return min + Math.random() * (max - min);
17224
- }
17225
- function spawnParticles(config, animTime) {
17226
- const particles = [];
17227
- for (let i = 0; i < config.count; i++) {
17228
- const angle = randRange(config.angleMin, config.angleMax);
17229
- const speed = randRange(config.velocityMin, config.velocityMax);
17230
- const spriteUrl = config.spriteUrls[Math.floor(Math.random() * config.spriteUrls.length)];
17231
- particles.push({
17232
- spriteUrl,
17233
- x: config.originX + randRange(-config.spread, config.spread),
17234
- y: config.originY + randRange(-config.spread, config.spread),
17235
- vx: Math.cos(angle) * speed,
17236
- vy: Math.sin(angle) * speed,
17237
- gravity: config.gravity,
17238
- rotation: Math.random() * Math.PI * 2,
17239
- rotationSpeed: randRange(config.rotationSpeedMin ?? -2, config.rotationSpeedMax ?? 2),
17240
- scale: randRange(config.scaleMin, config.scaleMax),
17241
- scaleSpeed: config.scaleSpeed ?? 0,
17242
- alpha: config.alpha ?? 1,
17243
- fadeRate: config.fadeRate ?? -1.5,
17244
- tint: { ...config.tint },
17245
- blendMode: config.blendMode ?? "source-over",
17246
- spawnTime: animTime,
17247
- lifetime: randRange(config.lifetimeMin, config.lifetimeMax)
17248
- });
17249
- }
17250
- return particles;
17251
- }
17252
- function spawnSequence(config, animTime) {
17253
- return {
17254
- frameUrls: config.frameUrls,
17255
- x: config.originX,
17256
- y: config.originY,
17257
- frameDuration: config.frameDuration,
17258
- startTime: animTime,
17259
- loop: config.loop ?? false,
17260
- scale: config.scale ?? 1,
17261
- tint: config.tint ?? null,
17262
- alpha: config.alpha ?? 1,
17263
- blendMode: config.blendMode ?? "source-over"
17264
- };
17265
- }
17266
- function spawnOverlay(config, animTime) {
17267
- return {
17268
- spriteUrl: config.spriteUrl,
17269
- x: config.originX,
17270
- y: config.originY,
17271
- alpha: config.alpha ?? 0.8,
17272
- fadeRate: config.fadeRate ?? -0.5,
17273
- pulseAmplitude: config.pulseAmplitude ?? 0,
17274
- pulseFrequency: config.pulseFrequency ?? 2,
17275
- scale: config.scale ?? 1,
17276
- blendMode: config.blendMode ?? "source-over",
17277
- spawnTime: animTime,
17278
- lifetime: config.lifetime ?? 2e3
17279
- };
17280
- }
17281
- function updateEffectState(state, animTime, deltaMs) {
17282
- const dt = deltaMs / 1e3;
17283
- const particles = state.particles.map((p2) => ({
17284
- ...p2,
17285
- x: p2.x + p2.vx * dt,
17286
- y: p2.y + p2.vy * dt,
17287
- vy: p2.vy + p2.gravity * dt,
17288
- rotation: p2.rotation + p2.rotationSpeed * dt,
17289
- scale: Math.max(0, p2.scale + p2.scaleSpeed * dt),
17290
- alpha: Math.max(0, p2.alpha + p2.fadeRate * dt)
17291
- })).filter((p2) => p2.alpha > 0.01 && animTime - p2.spawnTime < p2.lifetime);
17292
- const sequences = state.sequences.filter((s) => {
17293
- const elapsed = animTime - s.startTime;
17294
- const totalDuration = s.frameUrls.length * s.frameDuration;
17295
- return s.loop || elapsed < totalDuration;
17296
- });
17297
- const overlays = state.overlays.map((o) => ({
17298
- ...o,
17299
- alpha: Math.max(0, o.alpha + o.fadeRate * dt)
17300
- })).filter((o) => o.alpha > 0.01 && animTime - o.spawnTime < o.lifetime);
17301
- return { particles, sequences, overlays };
17302
- }
17303
- function drawEffectState(ctx, state, animTime, getImage) {
17304
- for (const o of state.overlays) {
17305
- const img = getImage(o.spriteUrl);
17306
- if (!img) continue;
17307
- let alpha = o.alpha;
17308
- if (o.pulseAmplitude > 0) {
17309
- const elapsed = (animTime - o.spawnTime) / 1e3;
17310
- alpha += Math.sin(elapsed * o.pulseFrequency * Math.PI * 2) * o.pulseAmplitude;
17311
- alpha = Math.max(0, Math.min(1, alpha));
17312
- }
17313
- const w = img.naturalWidth * o.scale;
17314
- const h = img.naturalHeight * o.scale;
17315
- const prevAlpha = ctx.globalAlpha;
17316
- const prevBlend = ctx.globalCompositeOperation;
17317
- ctx.globalAlpha = alpha;
17318
- ctx.globalCompositeOperation = o.blendMode;
17319
- ctx.drawImage(img, o.x - w / 2, o.y - h / 2, w, h);
17320
- ctx.globalAlpha = prevAlpha;
17321
- ctx.globalCompositeOperation = prevBlend;
17322
- }
17323
- for (const s of state.sequences) {
17324
- const elapsed = animTime - s.startTime;
17325
- let frameIndex = Math.floor(elapsed / s.frameDuration);
17326
- if (s.loop) {
17327
- frameIndex = frameIndex % s.frameUrls.length;
17328
- } else if (frameIndex >= s.frameUrls.length) {
17329
- continue;
17330
- }
17331
- const img = getImage(s.frameUrls[frameIndex]);
17332
- if (!img) continue;
17333
- const w = img.naturalWidth * s.scale;
17334
- const h = img.naturalHeight * s.scale;
17335
- if (s.tint) {
17336
- drawTintedImage(ctx, img, s.x - w / 2, s.y - h / 2, w, h, s.tint, s.alpha, s.blendMode);
17337
- } else {
17338
- const prevAlpha = ctx.globalAlpha;
17339
- const prevBlend = ctx.globalCompositeOperation;
17340
- ctx.globalAlpha = s.alpha;
17341
- ctx.globalCompositeOperation = s.blendMode;
17342
- ctx.drawImage(img, s.x - w / 2, s.y - h / 2, w, h);
17343
- ctx.globalAlpha = prevAlpha;
17344
- ctx.globalCompositeOperation = prevBlend;
17345
- }
17346
- }
17347
- for (const p2 of state.particles) {
17348
- const img = getImage(p2.spriteUrl);
17349
- if (!img) continue;
17350
- const w = img.naturalWidth * p2.scale;
17351
- const h = img.naturalHeight * p2.scale;
17352
- ctx.save();
17353
- ctx.translate(p2.x, p2.y);
17354
- ctx.rotate(p2.rotation);
17355
- drawTintedImage(ctx, img, -w / 2, -h / 2, w, h, p2.tint, p2.alpha, p2.blendMode);
17356
- ctx.restore();
17357
- }
17358
- }
17359
- function hasActiveEffects(state) {
17360
- return state.particles.length > 0 || state.sequences.length > 0 || state.overlays.length > 0;
17361
- }
17362
- function getAllEffectSpriteUrls(manifest) {
17363
- const urls = [];
17364
- const base = manifest.baseUrl;
17365
- if (manifest.particles) {
17366
- for (const value of Object.values(manifest.particles)) {
17367
- if (Array.isArray(value)) {
17368
- value.forEach((v) => urls.push(`${base}/${v}`));
17369
- } else if (typeof value === "string") {
17370
- urls.push(`${base}/${value}`);
17371
- }
17372
- }
17373
- }
17374
- if (manifest.animations) {
17375
- for (const frames of Object.values(manifest.animations)) {
17376
- if (Array.isArray(frames)) {
17377
- frames.forEach((f3) => urls.push(`${base}/${f3}`));
17378
- }
17379
- }
17380
- }
17381
- return urls;
17382
- }
17383
- var _offscreen, _offCtx;
17384
- var init_canvasEffects = __esm({
17385
- "components/game/shared/canvasEffects.ts"() {
17386
- _offscreen = null;
17387
- _offCtx = null;
17388
- }
17389
- });
17390
-
17391
- // components/game/shared/combatPresets.ts
17392
- function p(manifest, key) {
17393
- const particles = manifest.particles;
17394
- if (!particles) return [];
17395
- const val = particles[key];
17396
- if (Array.isArray(val)) return val.map((v) => `${manifest.baseUrl}/${v}`);
17397
- if (typeof val === "string") return [`${manifest.baseUrl}/${val}`];
17398
- return [];
17399
- }
17400
- function anim(manifest, key) {
17401
- const animations = manifest.animations;
17402
- if (!animations) return [];
17403
- const val = animations[key];
17404
- if (Array.isArray(val)) return val.map((v) => `${manifest.baseUrl}/${v}`);
17405
- return [];
17406
- }
17407
- function createCombatPresets(manifest) {
17408
- return {
17409
- // =====================================================================
17410
- // MELEE — slash (red) + dirt + scratch + flash sequence
17411
- // =====================================================================
17412
- melee: (originX, originY) => {
17413
- const particles = [
17414
- {
17415
- spriteUrls: p(manifest, "slash"),
17416
- count: 6,
17417
- originX,
17418
- originY,
17419
- spread: 8,
17420
- velocityMin: 40,
17421
- velocityMax: 120,
17422
- angleMin: -PI2 * 0.8,
17423
- angleMax: -PI2 * 0.2,
17424
- gravity: 0,
17425
- tint: { r: 255, g: 60, b: 40 },
17426
- scaleMin: 0.3,
17427
- scaleMax: 0.6,
17428
- lifetimeMin: 300,
17429
- lifetimeMax: 500,
17430
- fadeRate: -2.5
17431
- },
17432
- {
17433
- spriteUrls: p(manifest, "dirt"),
17434
- count: 4,
17435
- originX,
17436
- originY: originY + 10,
17437
- spread: 12,
17438
- velocityMin: 20,
17439
- velocityMax: 60,
17440
- angleMin: -PI2 * 0.9,
17441
- angleMax: -PI2 * 0.1,
17442
- gravity: 120,
17443
- tint: { r: 180, g: 140, b: 90 },
17444
- scaleMin: 0.15,
17445
- scaleMax: 0.3,
17446
- lifetimeMin: 400,
17447
- lifetimeMax: 700,
17448
- fadeRate: -1.8
17449
- },
17450
- {
17451
- spriteUrls: p(manifest, "scratch"),
17452
- count: 2,
17453
- originX,
17454
- originY,
17455
- spread: 5,
17456
- velocityMin: 10,
17457
- velocityMax: 30,
17458
- angleMin: -PI2 * 0.7,
17459
- angleMax: -PI2 * 0.3,
17460
- gravity: 0,
17461
- tint: { r: 255, g: 200, b: 150 },
17462
- scaleMin: 0.25,
17463
- scaleMax: 0.4,
17464
- lifetimeMin: 200,
17465
- lifetimeMax: 400,
17466
- fadeRate: -3
17467
- }
17468
- ];
17469
- const sequences = [];
17470
- const flashFrames = anim(manifest, "flash");
17471
- if (flashFrames.length > 0) {
17472
- sequences.push({
17473
- frameUrls: flashFrames,
17474
- originX,
17475
- originY,
17476
- frameDuration: 35,
17477
- scale: 0.4
17478
- });
17479
- }
17480
- return {
17481
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17482
- sequences,
17483
- overlays: [],
17484
- screenShake: 4,
17485
- screenFlash: null
17486
- };
17487
- },
17488
- // =====================================================================
17489
- // RANGED — muzzle + trace + smoke + explosion sequence
17490
- // =====================================================================
17491
- ranged: (originX, originY) => {
17492
- const particles = [
17493
- {
17494
- spriteUrls: p(manifest, "muzzle"),
17495
- count: 3,
17496
- originX,
17497
- originY,
17498
- spread: 4,
17499
- velocityMin: 60,
17500
- velocityMax: 150,
17501
- angleMin: -PI2 * 0.6,
17502
- angleMax: -PI2 * 0.4,
17503
- gravity: 0,
17504
- tint: { r: 255, g: 220, b: 100 },
17505
- scaleMin: 0.2,
17506
- scaleMax: 0.4,
17507
- lifetimeMin: 200,
17508
- lifetimeMax: 400,
17509
- fadeRate: -3
17510
- },
17511
- {
17512
- spriteUrls: p(manifest, "trace"),
17513
- count: 5,
17514
- originX,
17515
- originY,
17516
- spread: 3,
17517
- velocityMin: 100,
17518
- velocityMax: 200,
17519
- angleMin: -PI2 * 0.55,
17520
- angleMax: -PI2 * 0.45,
17521
- gravity: 0,
17522
- tint: { r: 255, g: 200, b: 80 },
17523
- scaleMin: 0.15,
17524
- scaleMax: 0.3,
17525
- lifetimeMin: 150,
17526
- lifetimeMax: 300,
17527
- fadeRate: -4
17528
- },
17529
- {
17530
- spriteUrls: p(manifest, "smoke").slice(0, 3),
17531
- count: 3,
17532
- originX,
17533
- originY: originY + 5,
17534
- spread: 6,
17535
- velocityMin: 10,
17536
- velocityMax: 30,
17537
- angleMin: -PI2 * 0.8,
17538
- angleMax: -PI2 * 0.2,
17539
- gravity: -20,
17540
- tint: { r: 200, g: 200, b: 200 },
17541
- scaleMin: 0.2,
17542
- scaleMax: 0.35,
17543
- lifetimeMin: 500,
17544
- lifetimeMax: 800,
17545
- fadeRate: -1.5
17546
- }
17547
- ];
17548
- const sequences = [];
17549
- const explosionFrames = anim(manifest, "smokeExplosion");
17550
- if (explosionFrames.length > 0) {
17551
- sequences.push({
17552
- frameUrls: explosionFrames,
17553
- originX,
17554
- originY,
17555
- frameDuration: 50,
17556
- scale: 0.35
17557
- });
17558
- }
17559
- return {
17560
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17561
- sequences,
17562
- overlays: [],
17563
- screenShake: 2,
17564
- screenFlash: null
17565
- };
17566
- },
17567
- // =====================================================================
17568
- // MAGIC — twirl (purple) + spark (purple) + star
17569
- // =====================================================================
17570
- magic: (originX, originY) => {
17571
- const particles = [
17572
- {
17573
- spriteUrls: p(manifest, "twirl"),
17574
- count: 5,
17575
- originX,
17576
- originY,
17577
- spread: 15,
17578
- velocityMin: 20,
17579
- velocityMax: 80,
17580
- angleMin: 0,
17581
- angleMax: PI2 * 2,
17582
- gravity: -30,
17583
- tint: { r: 180, g: 80, b: 255 },
17584
- scaleMin: 0.2,
17585
- scaleMax: 0.5,
17586
- lifetimeMin: 500,
17587
- lifetimeMax: 900,
17588
- fadeRate: -1.2,
17589
- blendMode: "lighter",
17590
- rotationSpeedMin: -4,
17591
- rotationSpeedMax: 4
17592
- },
17593
- {
17594
- spriteUrls: p(manifest, "spark"),
17595
- count: 8,
17596
- originX,
17597
- originY,
17598
- spread: 20,
17599
- velocityMin: 30,
17600
- velocityMax: 100,
17601
- angleMin: 0,
17602
- angleMax: PI2 * 2,
17603
- gravity: -15,
17604
- tint: { r: 200, g: 120, b: 255 },
17605
- scaleMin: 0.1,
17606
- scaleMax: 0.25,
17607
- lifetimeMin: 300,
17608
- lifetimeMax: 600,
17609
- fadeRate: -2,
17610
- blendMode: "lighter"
17611
- },
17612
- {
17613
- spriteUrls: p(manifest, "star"),
17614
- count: 4,
17615
- originX,
17616
- originY,
17617
- spread: 10,
17618
- velocityMin: 15,
17619
- velocityMax: 50,
17620
- angleMin: -PI2,
17621
- angleMax: 0,
17622
- gravity: -40,
17623
- tint: { r: 220, g: 180, b: 255 },
17624
- scaleMin: 0.15,
17625
- scaleMax: 0.3,
17626
- lifetimeMin: 600,
17627
- lifetimeMax: 1e3,
17628
- fadeRate: -1,
17629
- blendMode: "lighter"
17630
- }
17631
- ];
17632
- const overlays = [];
17633
- const circleUrls = p(manifest, "circle");
17634
- if (circleUrls.length > 0) {
17635
- overlays.push({
17636
- spriteUrl: circleUrls[0],
17637
- originX,
17638
- originY,
17639
- alpha: 0.5,
17640
- fadeRate: -0.6,
17641
- pulseAmplitude: 0.2,
17642
- pulseFrequency: 3,
17643
- scale: 0.5,
17644
- blendMode: "lighter",
17645
- lifetime: 1200
17646
- });
17647
- }
17648
- return {
17649
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17650
- sequences: [],
17651
- overlays,
17652
- screenShake: 0,
17653
- screenFlash: null
17654
- };
17655
- },
17656
- // =====================================================================
17657
- // HEAL — circle (green) + star (green) + light (green, pulse)
17658
- // =====================================================================
17659
- heal: (originX, originY) => {
17660
- const particles = [
17661
- {
17662
- spriteUrls: p(manifest, "circle"),
17663
- count: 6,
17664
- originX,
17665
- originY,
17666
- spread: 15,
17667
- velocityMin: 10,
17668
- velocityMax: 40,
17669
- angleMin: -PI2,
17670
- angleMax: -PI2 * 0.3,
17671
- gravity: -50,
17672
- tint: { r: 80, g: 255, b: 120 },
17673
- scaleMin: 0.15,
17674
- scaleMax: 0.35,
17675
- lifetimeMin: 600,
17676
- lifetimeMax: 1e3,
17677
- fadeRate: -0.8,
17678
- blendMode: "lighter"
17679
- },
17680
- {
17681
- spriteUrls: p(manifest, "star"),
17682
- count: 5,
17683
- originX,
17684
- originY,
17685
- spread: 12,
17686
- velocityMin: 15,
17687
- velocityMax: 50,
17688
- angleMin: -PI2 * 0.9,
17689
- angleMax: -PI2 * 0.1,
17690
- gravity: -60,
17691
- tint: { r: 100, g: 255, b: 140 },
17692
- scaleMin: 0.1,
17693
- scaleMax: 0.2,
17694
- lifetimeMin: 500,
17695
- lifetimeMax: 800,
17696
- fadeRate: -1.2,
17697
- blendMode: "lighter"
17698
- }
17699
- ];
17700
- const overlays = [];
17701
- const lightUrls = p(manifest, "light");
17702
- if (lightUrls.length > 0) {
17703
- overlays.push({
17704
- spriteUrl: lightUrls[0],
17705
- originX,
17706
- originY,
17707
- alpha: 0.6,
17708
- fadeRate: -0.4,
17709
- pulseAmplitude: 0.25,
17710
- pulseFrequency: 2.5,
17711
- scale: 0.6,
17712
- blendMode: "lighter",
17713
- lifetime: 1500
17714
- });
17715
- }
17716
- return {
17717
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17718
- sequences: [],
17719
- overlays,
17720
- screenShake: 0,
17721
- screenFlash: null
17722
- };
17723
- },
17724
- // =====================================================================
17725
- // DEFEND / SHIELD — star (blue) + circle (blue, pulse)
17726
- // =====================================================================
17727
- defend: (originX, originY) => {
17728
- const particles = [
17729
- {
17730
- spriteUrls: p(manifest, "star"),
17731
- count: 8,
17732
- originX,
17733
- originY,
17734
- spread: 18,
17735
- velocityMin: 10,
17736
- velocityMax: 35,
17737
- angleMin: 0,
17738
- angleMax: PI2 * 2,
17739
- gravity: 0,
17740
- tint: { r: 80, g: 160, b: 255 },
17741
- scaleMin: 0.12,
17742
- scaleMax: 0.25,
17743
- lifetimeMin: 600,
17744
- lifetimeMax: 1e3,
17745
- fadeRate: -0.8,
17746
- blendMode: "lighter",
17747
- rotationSpeedMin: -1,
17748
- rotationSpeedMax: 1
17749
- }
17750
- ];
17751
- const overlays = [];
17752
- const circleUrls = p(manifest, "circle");
17753
- if (circleUrls.length > 0) {
17754
- overlays.push({
17755
- spriteUrl: circleUrls[0],
17756
- originX,
17757
- originY,
17758
- alpha: 0.6,
17759
- fadeRate: -0.3,
17760
- pulseAmplitude: 0.2,
17761
- pulseFrequency: 2,
17762
- scale: 0.6,
17763
- blendMode: "lighter",
17764
- lifetime: 1500
17765
- });
17766
- }
17767
- return {
17768
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17769
- sequences: [],
17770
- overlays,
17771
- screenShake: 0,
17772
- screenFlash: null
17773
- };
17774
- },
17775
- // shield aliases to defend
17776
- shield: (originX, originY) => {
17777
- const particles = [
17778
- {
17779
- spriteUrls: p(manifest, "star"),
17780
- count: 10,
17781
- originX,
17782
- originY,
17783
- spread: 20,
17784
- velocityMin: 8,
17785
- velocityMax: 30,
17786
- angleMin: 0,
17787
- angleMax: PI2 * 2,
17788
- gravity: 0,
17789
- tint: { r: 60, g: 180, b: 255 },
17790
- scaleMin: 0.1,
17791
- scaleMax: 0.22,
17792
- lifetimeMin: 700,
17793
- lifetimeMax: 1200,
17794
- fadeRate: -0.7,
17795
- blendMode: "lighter",
17796
- rotationSpeedMin: -0.8,
17797
- rotationSpeedMax: 0.8
17798
- }
17799
- ];
17800
- const overlays = [];
17801
- const circleUrls = p(manifest, "circle");
17802
- if (circleUrls.length > 0) {
17803
- overlays.push({
17804
- spriteUrl: circleUrls[0],
17805
- originX,
17806
- originY,
17807
- alpha: 0.7,
17808
- fadeRate: -0.25,
17809
- pulseAmplitude: 0.25,
17810
- pulseFrequency: 1.8,
17811
- scale: 0.7,
17812
- blendMode: "lighter",
17813
- lifetime: 1800
17814
- });
17815
- }
17816
- return {
17817
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17818
- sequences: [],
17819
- overlays,
17820
- screenShake: 0,
17821
- screenFlash: null
17822
- };
17823
- },
17824
- // =====================================================================
17825
- // HIT — spark (orange) + flash (5 frames) + screen shake/flash
17826
- // =====================================================================
17827
- hit: (originX, originY) => {
17828
- const particles = [
17829
- {
17830
- spriteUrls: p(manifest, "spark"),
17831
- count: 10,
17832
- originX,
17833
- originY,
17834
- spread: 8,
17835
- velocityMin: 50,
17836
- velocityMax: 150,
17837
- angleMin: 0,
17838
- angleMax: PI2 * 2,
17839
- gravity: 80,
17840
- tint: { r: 255, g: 180, b: 50 },
17841
- scaleMin: 0.08,
17842
- scaleMax: 0.2,
17843
- lifetimeMin: 200,
17844
- lifetimeMax: 500,
17845
- fadeRate: -2.5
17846
- }
17847
- ];
17848
- const sequences = [];
17849
- const flashFrames = anim(manifest, "flash");
17850
- if (flashFrames.length > 0) {
17851
- sequences.push({
17852
- frameUrls: flashFrames.slice(0, 5),
17853
- originX,
17854
- originY,
17855
- frameDuration: 40,
17856
- scale: 0.3
17857
- });
17858
- }
17859
- return {
17860
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17861
- sequences,
17862
- overlays: [],
17863
- screenShake: 3,
17864
- screenFlash: { r: 255, g: 50, b: 50, duration: 150 }
17865
- };
17866
- },
17867
- // critical aliases to hit with bigger shake
17868
- critical: (originX, originY) => {
17869
- const particles = [
17870
- {
17871
- spriteUrls: p(manifest, "flame"),
17872
- count: 8,
17873
- originX,
17874
- originY,
17875
- spread: 12,
17876
- velocityMin: 60,
17877
- velocityMax: 180,
17878
- angleMin: 0,
17879
- angleMax: PI2 * 2,
17880
- gravity: 60,
17881
- tint: { r: 255, g: 120, b: 30 },
17882
- scaleMin: 0.15,
17883
- scaleMax: 0.4,
17884
- lifetimeMin: 300,
17885
- lifetimeMax: 600,
17886
- fadeRate: -2
17887
- },
17888
- {
17889
- spriteUrls: p(manifest, "spark"),
17890
- count: 12,
17891
- originX,
17892
- originY,
17893
- spread: 10,
17894
- velocityMin: 80,
17895
- velocityMax: 200,
17896
- angleMin: 0,
17897
- angleMax: PI2 * 2,
17898
- gravity: 100,
17899
- tint: { r: 255, g: 200, b: 60 },
17900
- scaleMin: 0.06,
17901
- scaleMax: 0.18,
17902
- lifetimeMin: 200,
17903
- lifetimeMax: 400,
17904
- fadeRate: -3
17905
- }
17906
- ];
17907
- const sequences = [];
17908
- const flashFrames = anim(manifest, "flash");
17909
- if (flashFrames.length > 0) {
17910
- sequences.push({
17911
- frameUrls: flashFrames,
17912
- originX,
17913
- originY,
17914
- frameDuration: 30,
17915
- scale: 0.5
17916
- });
17917
- }
17918
- return {
17919
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17920
- sequences,
17921
- overlays: [],
17922
- screenShake: 6,
17923
- screenFlash: { r: 255, g: 80, b: 0, duration: 200 }
17924
- };
17925
- },
17926
- // =====================================================================
17927
- // DEATH — dirt (gray) + explosion + black smoke + scorch (ground)
17928
- // =====================================================================
17929
- death: (originX, originY) => {
17930
- const particles = [
17931
- {
17932
- spriteUrls: p(manifest, "dirt"),
17933
- count: 8,
17934
- originX,
17935
- originY,
17936
- spread: 10,
17937
- velocityMin: 30,
17938
- velocityMax: 100,
17939
- angleMin: 0,
17940
- angleMax: PI2 * 2,
17941
- gravity: 100,
17942
- tint: { r: 140, g: 140, b: 140 },
17943
- scaleMin: 0.15,
17944
- scaleMax: 0.35,
17945
- lifetimeMin: 500,
17946
- lifetimeMax: 900,
17947
- fadeRate: -1.2
17948
- }
17949
- ];
17950
- const sequences = [];
17951
- const explosionFrames = anim(manifest, "explosion");
17952
- if (explosionFrames.length > 0) {
17953
- sequences.push({
17954
- frameUrls: explosionFrames,
17955
- originX,
17956
- originY,
17957
- frameDuration: 60,
17958
- scale: 0.5
17959
- });
17960
- }
17961
- const blackSmokeFrames = anim(manifest, "blackSmoke");
17962
- if (blackSmokeFrames.length > 0) {
17963
- sequences.push({
17964
- frameUrls: blackSmokeFrames,
17965
- originX,
17966
- originY: originY - 10,
17967
- frameDuration: 50,
17968
- scale: 0.4,
17969
- alpha: 0.7
17970
- });
17971
- }
17972
- const overlays = [];
17973
- const scorchUrls = p(manifest, "scorch");
17974
- if (scorchUrls.length > 0) {
17975
- overlays.push({
17976
- spriteUrl: scorchUrls[0],
17977
- originX,
17978
- originY: originY + 10,
17979
- alpha: 0.6,
17980
- fadeRate: -0.15,
17981
- scale: 0.4,
17982
- lifetime: 4e3
17983
- });
17984
- }
17985
- return {
17986
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
17987
- sequences,
17988
- overlays,
17989
- screenShake: 0,
17990
- screenFlash: null
17991
- };
17992
- },
17993
- // =====================================================================
17994
- // BUFF — star (gold) + symbol + flare (gold, pulse)
17995
- // =====================================================================
17996
- buff: (originX, originY) => {
17997
- const particles = [
17998
- {
17999
- spriteUrls: p(manifest, "star"),
18000
- count: 6,
18001
- originX,
18002
- originY,
18003
- spread: 15,
18004
- velocityMin: 15,
18005
- velocityMax: 50,
18006
- angleMin: -PI2,
18007
- angleMax: 0,
18008
- gravity: -30,
18009
- tint: { r: 255, g: 215, b: 50 },
18010
- scaleMin: 0.12,
18011
- scaleMax: 0.25,
18012
- lifetimeMin: 600,
18013
- lifetimeMax: 1e3,
18014
- fadeRate: -0.8,
18015
- blendMode: "lighter"
18016
- },
18017
- {
18018
- spriteUrls: p(manifest, "symbol"),
18019
- count: 2,
18020
- originX,
18021
- originY: originY - 10,
18022
- spread: 8,
18023
- velocityMin: 5,
18024
- velocityMax: 20,
18025
- angleMin: -PI2 * 0.7,
18026
- angleMax: -PI2 * 0.3,
18027
- gravity: -20,
18028
- tint: { r: 255, g: 230, b: 100 },
18029
- scaleMin: 0.2,
18030
- scaleMax: 0.35,
18031
- lifetimeMin: 800,
18032
- lifetimeMax: 1200,
18033
- fadeRate: -0.6,
18034
- blendMode: "lighter"
18035
- }
18036
- ];
18037
- const overlays = [];
18038
- const flareUrls = p(manifest, "flare");
18039
- if (flareUrls.length > 0) {
18040
- overlays.push({
18041
- spriteUrl: flareUrls[0],
18042
- originX,
18043
- originY,
18044
- alpha: 0.5,
18045
- fadeRate: -0.3,
18046
- pulseAmplitude: 0.3,
18047
- pulseFrequency: 2,
18048
- scale: 0.5,
18049
- blendMode: "lighter",
18050
- lifetime: 1500
18051
- });
18052
- }
18053
- return {
18054
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
18055
- sequences: [],
18056
- overlays,
18057
- screenShake: 0,
18058
- screenFlash: null
18059
- };
18060
- },
18061
- // =====================================================================
18062
- // DEBUFF — scorch (dark) + smoke (purple tint)
18063
- // =====================================================================
18064
- debuff: (originX, originY) => {
18065
- const particles = [
18066
- {
18067
- spriteUrls: p(manifest, "scorch"),
18068
- count: 4,
18069
- originX,
18070
- originY,
18071
- spread: 12,
18072
- velocityMin: 15,
18073
- velocityMax: 40,
18074
- angleMin: -PI2,
18075
- angleMax: 0,
18076
- gravity: -20,
18077
- tint: { r: 120, g: 40, b: 160 },
18078
- scaleMin: 0.15,
18079
- scaleMax: 0.3,
18080
- lifetimeMin: 500,
18081
- lifetimeMax: 800,
18082
- fadeRate: -1
18083
- },
18084
- {
18085
- spriteUrls: p(manifest, "smoke").slice(0, 3),
18086
- count: 3,
18087
- originX,
18088
- originY,
18089
- spread: 10,
18090
- velocityMin: 8,
18091
- velocityMax: 25,
18092
- angleMin: -PI2 * 0.8,
18093
- angleMax: -PI2 * 0.2,
18094
- gravity: -15,
18095
- tint: { r: 100, g: 50, b: 140 },
18096
- scaleMin: 0.2,
18097
- scaleMax: 0.35,
18098
- lifetimeMin: 600,
18099
- lifetimeMax: 1e3,
18100
- fadeRate: -0.8
18101
- }
18102
- ];
18103
- const overlays = [];
18104
- const circleUrls = p(manifest, "circle");
18105
- if (circleUrls.length > 0) {
18106
- overlays.push({
18107
- spriteUrl: circleUrls[0],
18108
- originX,
18109
- originY,
18110
- alpha: 0.4,
18111
- fadeRate: -0.4,
18112
- pulseAmplitude: 0.15,
18113
- pulseFrequency: 2,
18114
- scale: 0.45,
18115
- lifetime: 1200
18116
- });
18117
- }
18118
- return {
18119
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
18120
- sequences: [],
18121
- overlays,
18122
- screenShake: 0,
18123
- screenFlash: null
18124
- };
18125
- },
18126
- // =====================================================================
18127
- // AOE — explosion (large) + flame + spark (radial) + screen shake
18128
- // =====================================================================
18129
- aoe: (originX, originY) => {
18130
- const particles = [
18131
- {
18132
- spriteUrls: p(manifest, "flame"),
18133
- count: 10,
18134
- originX,
18135
- originY,
18136
- spread: 20,
18137
- velocityMin: 40,
18138
- velocityMax: 140,
18139
- angleMin: 0,
18140
- angleMax: PI2 * 2,
18141
- gravity: 40,
18142
- tint: { r: 255, g: 140, b: 30 },
18143
- scaleMin: 0.2,
18144
- scaleMax: 0.5,
18145
- lifetimeMin: 400,
18146
- lifetimeMax: 800,
18147
- fadeRate: -1.5
18148
- },
18149
- {
18150
- spriteUrls: p(manifest, "spark"),
18151
- count: 15,
18152
- originX,
18153
- originY,
18154
- spread: 15,
18155
- velocityMin: 60,
18156
- velocityMax: 200,
18157
- angleMin: 0,
18158
- angleMax: PI2 * 2,
18159
- gravity: 60,
18160
- tint: { r: 255, g: 180, b: 60 },
18161
- scaleMin: 0.06,
18162
- scaleMax: 0.15,
18163
- lifetimeMin: 200,
18164
- lifetimeMax: 500,
18165
- fadeRate: -2.5
18166
- }
18167
- ];
18168
- const sequences = [];
18169
- const explosionFrames = anim(manifest, "explosion");
18170
- if (explosionFrames.length > 0) {
18171
- sequences.push({
18172
- frameUrls: explosionFrames,
18173
- originX,
18174
- originY,
18175
- frameDuration: 50,
18176
- scale: 0.6
18177
- });
18178
- }
18179
- return {
18180
- particles: particles.filter((pc) => pc.spriteUrls.length > 0),
18181
- sequences,
18182
- overlays: [],
18183
- screenShake: 5,
18184
- screenFlash: { r: 255, g: 160, b: 0, duration: 180 }
18185
- };
18186
- }
18187
- };
18188
- }
18189
- var PI2;
18190
- var init_combatPresets = __esm({
18191
- "components/game/shared/combatPresets.ts"() {
18192
- PI2 = Math.PI;
18193
- }
18194
- });
18195
- function CanvasEffectEngine({
18196
- actionType,
18197
- x,
18198
- y,
18199
- duration = 2e3,
18200
- intensity = 1,
18201
- onComplete,
18202
- className,
18203
- assetManifest,
18204
- width = 400,
18205
- height = 300
18206
- }) {
18207
- const canvasRef = React74.useRef(null);
18208
- const stateRef = React74.useRef({ ...exports.EMPTY_EFFECT_STATE });
18209
- const lastTimeRef = React74.useRef(0);
18210
- const rafRef = React74.useRef(0);
18211
- const imageCacheRef = React74.useRef(/* @__PURE__ */ new Map());
18212
- const [shakeOffset, setShakeOffset] = React74.useState({ x: 0, y: 0 });
18213
- const [flash, setFlash] = React74.useState(null);
18214
- const shakeRef = React74.useRef({ x: 0, y: 0, intensity: 0 });
18215
- const presets = React74.useMemo(() => createCombatPresets(assetManifest), [assetManifest]);
18216
- const spriteUrls = React74.useMemo(() => getAllEffectSpriteUrls(assetManifest), [assetManifest]);
18217
- React74.useEffect(() => {
18218
- const cache = imageCacheRef.current;
18219
- for (const url of spriteUrls) {
18220
- if (!cache.has(url)) {
18221
- const img = new Image();
18222
- img.crossOrigin = "anonymous";
18223
- img.src = url;
18224
- cache.set(url, img);
18225
- }
18226
- }
18227
- }, [spriteUrls]);
18228
- const getImage = React74.useCallback((url) => {
18229
- const img = imageCacheRef.current.get(url);
18230
- return img?.complete ? img : void 0;
18231
- }, []);
18232
- React74.useEffect(() => {
18233
- const now = performance.now();
18234
- const effectX = x || width / 2;
18235
- const effectY = y || height / 2;
18236
- const preset = presets[actionType](effectX, effectY);
18237
- const state = stateRef.current;
18238
- for (const emitter of preset.particles) {
18239
- const scaledEmitter = { ...emitter, count: Math.round(emitter.count * intensity) };
18240
- state.particles.push(...spawnParticles(scaledEmitter, now));
18241
- }
18242
- for (const seqConfig of preset.sequences) {
18243
- state.sequences.push(spawnSequence(seqConfig, now));
18244
- }
18245
- for (const ovConfig of preset.overlays) {
18246
- state.overlays.push(spawnOverlay(ovConfig, now));
18247
- }
18248
- if (preset.screenShake > 0) {
18249
- shakeRef.current.intensity = preset.screenShake * intensity;
18250
- }
18251
- if (preset.screenFlash) {
18252
- const { r, g, b, duration: flashDur } = preset.screenFlash;
18253
- setFlash({ color: `rgb(${r}, ${g}, ${b})`, alpha: 0.3 });
18254
- setTimeout(() => setFlash(null), flashDur);
18255
- }
18256
- const timer = setTimeout(() => {
18257
- onComplete?.();
18258
- }, duration);
18259
- return () => clearTimeout(timer);
18260
- }, []);
18261
- React74.useEffect(() => {
18262
- const canvas = canvasRef.current;
18263
- if (!canvas) return;
18264
- const ctx = canvas.getContext("2d");
18265
- if (!ctx) return;
18266
- function loop(animTime) {
18267
- const delta = lastTimeRef.current > 0 ? animTime - lastTimeRef.current : 16;
18268
- lastTimeRef.current = animTime;
18269
- stateRef.current = updateEffectState(stateRef.current, animTime, delta);
18270
- if (shakeRef.current.intensity > 0.2) {
18271
- const i = shakeRef.current.intensity;
18272
- shakeRef.current.x = (Math.random() - 0.5) * i * 2;
18273
- shakeRef.current.y = (Math.random() - 0.5) * i * 2;
18274
- shakeRef.current.intensity *= 0.85;
18275
- setShakeOffset({ x: shakeRef.current.x, y: shakeRef.current.y });
18276
- } else if (shakeRef.current.intensity > 0) {
18277
- shakeRef.current = { x: 0, y: 0, intensity: 0 };
18278
- setShakeOffset({ x: 0, y: 0 });
18279
- }
18280
- ctx.clearRect(0, 0, width, height);
18281
- drawEffectState(ctx, stateRef.current, animTime, getImage);
18282
- if (hasActiveEffects(stateRef.current)) {
18283
- rafRef.current = requestAnimationFrame(loop);
18284
- }
18285
- }
18286
- rafRef.current = requestAnimationFrame(loop);
18287
- return () => cancelAnimationFrame(rafRef.current);
18288
- }, [width, height, getImage]);
18289
- const shakeStyle = shakeOffset.x !== 0 || shakeOffset.y !== 0 ? { transform: `translate(${shakeOffset.x}px, ${shakeOffset.y}px)` } : {};
18290
- return /* @__PURE__ */ jsxRuntime.jsxs(
18291
- exports.Box,
18292
- {
18293
- className: cn("absolute inset-0 pointer-events-none z-10", className),
18294
- style: shakeStyle,
18295
- children: [
18296
- flash && /* @__PURE__ */ jsxRuntime.jsx(
18297
- exports.Box,
18298
- {
18299
- className: "absolute inset-0 z-20 pointer-events-none rounded-container",
18300
- style: { backgroundColor: flash.color, opacity: flash.alpha }
18301
- }
18302
- ),
18303
- /* @__PURE__ */ jsxRuntime.jsx(
18304
- "canvas",
18305
- {
18306
- ref: canvasRef,
18307
- width,
18308
- height,
18309
- className: "absolute inset-0 w-full h-full",
18310
- style: { imageRendering: "pixelated" }
18311
- }
18312
- )
18313
- ]
18314
- }
18315
- );
18316
- }
18317
- function EmojiEffect({
18318
- actionType,
18319
- x,
18320
- y,
18321
- duration = 800,
18322
- intensity = 1,
18323
- onComplete,
18324
- className,
18325
- effectSpriteUrl,
18326
- assetBaseUrl
18327
- }) {
18328
- const [visible, setVisible] = React74.useState(true);
18329
- const [phase, setPhase] = React74.useState("enter");
18330
- React74.useEffect(() => {
18331
- const enterTimer = setTimeout(() => setPhase("active"), 100);
18332
- const exitTimer = setTimeout(() => setPhase("exit"), duration * 0.7);
18333
- const doneTimer = setTimeout(() => {
18334
- setVisible(false);
18335
- onComplete?.();
18336
- }, duration);
18337
- return () => {
18338
- clearTimeout(enterTimer);
18339
- clearTimeout(exitTimer);
18340
- clearTimeout(doneTimer);
18341
- };
18342
- }, [duration, onComplete]);
18343
- if (!visible) return null;
18344
- const config = ACTION_EMOJI[actionType] ?? ACTION_EMOJI.melee;
18345
- const scaleVal = phase === "enter" ? 0.3 : phase === "active" ? intensity : 0.5;
18346
- const opacity = phase === "exit" ? 0 : 1;
18347
- const resolvedSpriteUrl = effectSpriteUrl ? effectSpriteUrl.startsWith("http") || effectSpriteUrl.startsWith("/") ? effectSpriteUrl : assetBaseUrl ? `${assetBaseUrl.replace(/\/$/, "")}/${effectSpriteUrl}` : effectSpriteUrl : void 0;
18348
- return /* @__PURE__ */ jsxRuntime.jsxs(
18349
- exports.Box,
18350
- {
18351
- className: cn(
18352
- "fixed pointer-events-none z-50 flex items-center justify-center",
18353
- "transition-all ease-out",
18354
- className
18355
- ),
18356
- style: {
18357
- left: x,
18358
- top: y,
18359
- transform: `translate(-50%, -50%) scale(${scaleVal})`,
18360
- opacity,
18361
- transitionDuration: phase === "enter" ? "100ms" : "300ms"
18362
- },
18363
- children: [
18364
- /* @__PURE__ */ jsxRuntime.jsx(
18365
- exports.Box,
18366
- {
18367
- className: "absolute rounded-pill animate-ping",
18368
- style: {
18369
- width: 48 * intensity,
18370
- height: 48 * intensity,
18371
- backgroundColor: config.color,
18372
- opacity: 0.25
18373
- }
18374
- }
18375
- ),
18376
- resolvedSpriteUrl ? /* @__PURE__ */ jsxRuntime.jsx(
18377
- "img",
18378
- {
18379
- src: resolvedSpriteUrl,
18380
- alt: config.label,
18381
- className: "relative drop-shadow-lg",
18382
- style: {
18383
- width: `${3 * intensity}rem`,
18384
- height: `${3 * intensity}rem`,
18385
- objectFit: "contain",
18386
- imageRendering: "pixelated"
18387
- }
18388
- }
18389
- ) : /* @__PURE__ */ jsxRuntime.jsx(
18390
- "span",
18391
- {
18392
- className: "relative text-3xl drop-shadow-lg",
18393
- style: { fontSize: `${2 * intensity}rem` },
18394
- role: "img",
18395
- "aria-label": config.label,
18396
- children: config.emoji
18397
- }
18398
- )
18399
- ]
18400
- }
18401
- );
18402
- }
18403
- function CanvasEffect({
18404
- effectSpriteUrl,
18405
- assetBaseUrl,
18406
- ...props
18407
- }) {
18408
- const eventBus = useEventBus();
18409
- const mergedProps = { effectSpriteUrl, assetBaseUrl, ...props };
18410
- const { completeEvent, onComplete, ...rest } = mergedProps;
18411
- const handleComplete = React74.useCallback(() => {
18412
- if (completeEvent) eventBus.emit(`UI:${completeEvent}`, {});
18413
- onComplete?.();
18414
- }, [completeEvent, eventBus, onComplete]);
18415
- const enhancedProps = { ...rest, onComplete: handleComplete };
18416
- if (rest.assetManifest) {
18417
- return /* @__PURE__ */ jsxRuntime.jsx(CanvasEffectEngine, { ...enhancedProps, assetManifest: rest.assetManifest });
18418
- }
18419
- return /* @__PURE__ */ jsxRuntime.jsx(EmojiEffect, { ...enhancedProps });
18420
- }
18421
- var ACTION_EMOJI;
18422
- var init_CanvasEffect = __esm({
18423
- "components/game/2d/organisms/CanvasEffect.tsx"() {
18424
- "use client";
18425
- init_cn();
18426
- init_useEventBus();
18427
- init_Box();
18428
- init_effects();
18429
- init_canvasEffects();
18430
- init_combatPresets();
18431
- ACTION_EMOJI = {
18432
- melee: { emoji: "\u2694\uFE0F", color: "var(--color-error)", label: "Slash" },
18433
- ranged: { emoji: "\u{1F3F9}", color: "var(--color-warning)", label: "Arrow" },
18434
- magic: { emoji: "\u2728", color: "var(--color-primary)", label: "Spell" },
18435
- heal: { emoji: "\u{1F49A}", color: "var(--color-success)", label: "Heal" },
18436
- buff: { emoji: "\u2B06\uFE0F", color: "var(--color-info)", label: "Buff" },
18437
- debuff: { emoji: "\u2B07\uFE0F", color: "var(--color-warning)", label: "Debuff" },
18438
- shield: { emoji: "\u{1F6E1}\uFE0F", color: "var(--color-info)", label: "Shield" },
18439
- aoe: { emoji: "\u{1F4A5}", color: "var(--color-error)", label: "Explosion" },
18440
- critical: { emoji: "\u{1F525}", color: "var(--color-error)", label: "Critical" },
18441
- defend: { emoji: "\u{1F6E1}\uFE0F", color: "var(--color-info)", label: "Defend" },
18442
- hit: { emoji: "\u{1F4A5}", color: "var(--color-error)", label: "Hit" },
18443
- death: { emoji: "\u{1F480}", color: "var(--color-error)", label: "Death" }
18444
- };
18445
- CanvasEffect.displayName = "CanvasEffect";
18446
- }
18447
- });
18448
-
18449
17177
  // lib/getNestedValue.ts
18450
17178
  function getNestedValue(obj, path) {
18451
17179
  if (obj === null || obj === void 0 || !path) {
@@ -19232,10 +17960,10 @@ var init_Chart = __esm({
19232
17960
  const set = [];
19233
17961
  const seen = /* @__PURE__ */ new Set();
19234
17962
  for (const s of series) {
19235
- for (const p2 of s.data) {
19236
- if (!seen.has(p2.label)) {
19237
- seen.add(p2.label);
19238
- set.push(p2.label);
17963
+ for (const p of s.data) {
17964
+ if (!seen.has(p.label)) {
17965
+ seen.add(p.label);
17966
+ set.push(p.label);
19239
17967
  }
19240
17968
  }
19241
17969
  }
@@ -19243,8 +17971,8 @@ var init_Chart = __esm({
19243
17971
  }, [series]);
19244
17972
  const valueAt = React74.useCallback(
19245
17973
  (s, label) => {
19246
- const p2 = s.data.find((d) => d.label === label);
19247
- return p2 ? p2.value : 0;
17974
+ const p = s.data.find((d) => d.label === label);
17975
+ return p ? p.value : 0;
19248
17976
  },
19249
17977
  []
19250
17978
  );
@@ -19261,7 +17989,7 @@ var init_Chart = __esm({
19261
17989
  }
19262
17990
  let m = 1;
19263
17991
  for (const s of series) {
19264
- for (const p2 of s.data) if (p2.value > m) m = p2.value;
17992
+ for (const p of s.data) if (p.value > m) m = p.value;
19265
17993
  }
19266
17994
  return m;
19267
17995
  }, [series, stack, columnTotals]);
@@ -19521,10 +18249,10 @@ var init_Chart = __esm({
19521
18249
  const seen = /* @__PURE__ */ new Set();
19522
18250
  const out = [];
19523
18251
  for (const s of series) {
19524
- for (const p2 of s.data) {
19525
- if (!seen.has(p2.label)) {
19526
- seen.add(p2.label);
19527
- out.push(p2.label);
18252
+ for (const p of s.data) {
18253
+ if (!seen.has(p.label)) {
18254
+ seen.add(p.label);
18255
+ out.push(p.label);
19528
18256
  }
19529
18257
  }
19530
18258
  }
@@ -19533,7 +18261,7 @@ var init_Chart = __esm({
19533
18261
  const maxValue = React74.useMemo(() => {
19534
18262
  let m = 1;
19535
18263
  for (const s of series) {
19536
- for (const p2 of s.data) if (p2.value > m) m = p2.value;
18264
+ for (const p of s.data) if (p.value > m) m = p.value;
19537
18265
  }
19538
18266
  return m;
19539
18267
  }, [series]);
@@ -19580,7 +18308,7 @@ var init_Chart = __esm({
19580
18308
  label
19581
18309
  };
19582
18310
  });
19583
- const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
18311
+ const linePath = points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
19584
18312
  const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
19585
18313
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
19586
18314
  fill && /* @__PURE__ */ jsxRuntime.jsx(
@@ -19603,19 +18331,19 @@ var init_Chart = __esm({
19603
18331
  strokeDasharray: s.dashed ? "6 4" : void 0
19604
18332
  }
19605
18333
  ),
19606
- points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18334
+ points.map((p, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
19607
18335
  /* @__PURE__ */ jsxRuntime.jsx(
19608
18336
  "circle",
19609
18337
  {
19610
- cx: p2.x,
19611
- cy: p2.y,
18338
+ cx: p.x,
18339
+ cy: p.y,
19612
18340
  r: "4",
19613
18341
  fill: "var(--color-card)",
19614
18342
  stroke: color,
19615
18343
  strokeWidth: "2",
19616
18344
  className: "cursor-pointer",
19617
18345
  onClick: () => onPointClick?.(
19618
- { label: p2.label, value: p2.value, color },
18346
+ { label: p.label, value: p.value, color },
19619
18347
  s.name
19620
18348
  )
19621
18349
  }
@@ -19623,13 +18351,13 @@ var init_Chart = __esm({
19623
18351
  showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(
19624
18352
  "text",
19625
18353
  {
19626
- x: p2.x,
19627
- y: p2.y - 10,
18354
+ x: p.x,
18355
+ y: p.y - 10,
19628
18356
  textAnchor: "middle",
19629
18357
  fill: "var(--color-foreground)",
19630
18358
  fontSize: "10",
19631
18359
  fontWeight: "500",
19632
- children: p2.value
18360
+ children: p.value
19633
18361
  }
19634
18362
  )
19635
18363
  ] }, idx))
@@ -19664,11 +18392,11 @@ var init_Chart = __esm({
19664
18392
  let mxX = data[0].x;
19665
18393
  let mnY = data[0].y;
19666
18394
  let mxY = data[0].y;
19667
- for (const p2 of data) {
19668
- if (p2.x < mnX) mnX = p2.x;
19669
- if (p2.x > mxX) mxX = p2.x;
19670
- if (p2.y < mnY) mnY = p2.y;
19671
- if (p2.y > mxY) mxY = p2.y;
18395
+ for (const p of data) {
18396
+ if (p.x < mnX) mnX = p.x;
18397
+ if (p.x > mxX) mxX = p.x;
18398
+ if (p.y < mnY) mnY = p.y;
18399
+ if (p.y > mxY) mxY = p.y;
19672
18400
  }
19673
18401
  return { minX: mnX, maxX: mxX, minY: mnY, maxY: mxY };
19674
18402
  }, [data]);
@@ -19698,11 +18426,11 @@ var init_Chart = __esm({
19698
18426
  frac
19699
18427
  );
19700
18428
  }),
19701
- data.map((p2, idx) => {
19702
- const cx = padding.left + (p2.x - minX) / rangeX * chartWidth;
19703
- const cy = padding.top + chartHeight - (p2.y - minY) / rangeY * chartHeight;
19704
- const r = p2.size ?? 5;
19705
- const color = p2.color ?? CHART_COLORS[idx % CHART_COLORS.length];
18429
+ data.map((p, idx) => {
18430
+ const cx = padding.left + (p.x - minX) / rangeX * chartWidth;
18431
+ const cy = padding.top + chartHeight - (p.y - minY) / rangeY * chartHeight;
18432
+ const r = p.size ?? 5;
18433
+ const color = p.color ?? CHART_COLORS[idx % CHART_COLORS.length];
19706
18434
  return /* @__PURE__ */ jsxRuntime.jsx(
19707
18435
  "circle",
19708
18436
  {
@@ -19714,13 +18442,13 @@ var init_Chart = __esm({
19714
18442
  className: "cursor-pointer hover:opacity-100",
19715
18443
  onClick: () => onPointClick?.(
19716
18444
  {
19717
- label: p2.label ?? `(${p2.x}, ${p2.y})`,
19718
- value: p2.y,
18445
+ label: p.label ?? `(${p.x}, ${p.y})`,
18446
+ value: p.y,
19719
18447
  color
19720
18448
  },
19721
18449
  "default"
19722
18450
  ),
19723
- children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: p2.label ?? `(${p2.x}, ${p2.y})` })
18451
+ children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: p.label ?? `(${p.x}, ${p.y})` })
19724
18452
  },
19725
18453
  idx
19726
18454
  );
@@ -24637,7 +23365,7 @@ var init_NodeSlotEditor = __esm({
24637
23365
  }
24638
23366
  }, []);
24639
23367
  const options = React74__namespace.default.useMemo(
24640
- () => [{ value: "", label: "\u2014 none \u2014" }, ...patterns.map((p2) => ({ value: p2, label: p2 }))],
23368
+ () => [{ value: "", label: "\u2014 none \u2014" }, ...patterns.map((p) => ({ value: p, label: p }))],
24641
23369
  [patterns]
24642
23370
  );
24643
23371
  const emit = (nextType, nextProps) => {
@@ -26121,11 +24849,11 @@ var init_OnboardingSpotlight = __esm({
26121
24849
  if (!step || typeof document === "undefined") return null;
26122
24850
  const isLast = stepIndex >= steps.length - 1;
26123
24851
  const backdrop = rect ? (() => {
26124
- const p2 = cutoutPadding;
26125
- const top = Math.max(0, rect.top - p2);
26126
- const left = Math.max(0, rect.left - p2);
26127
- const right = rect.right + p2;
26128
- const bottom = rect.bottom + p2;
24852
+ const p = cutoutPadding;
24853
+ const top = Math.max(0, rect.top - p);
24854
+ const left = Math.max(0, rect.left - p);
24855
+ const right = rect.right + p;
24856
+ const bottom = rect.bottom + p;
26129
24857
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
26130
24858
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: DIM, style: { top: 0, left: 0, right: 0, height: top } }),
26131
24859
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: DIM, style: { top: bottom, left: 0, right: 0, bottom: 0 } }),
@@ -27641,7 +26369,7 @@ var init_LineChart = __esm({
27641
26369
  }, [sortedData, width, height]);
27642
26370
  const linePath = React74.useMemo(() => {
27643
26371
  if (points.length === 0) return "";
27644
- return points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
26372
+ return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
27645
26373
  }, [points]);
27646
26374
  const areaPath = React74.useMemo(() => {
27647
26375
  if (points.length === 0 || !showArea) return "";
@@ -27847,6 +26575,18 @@ var init_game = __esm({
27847
26575
  }
27848
26576
  });
27849
26577
 
26578
+ // components/game/shared/effects.ts
26579
+ exports.EMPTY_EFFECT_STATE = void 0;
26580
+ var init_effects = __esm({
26581
+ "components/game/shared/effects.ts"() {
26582
+ exports.EMPTY_EFFECT_STATE = {
26583
+ particles: [],
26584
+ sequences: [],
26585
+ overlays: []
26586
+ };
26587
+ }
26588
+ });
26589
+
27850
26590
  // components/game/shared/makeAsset.ts
27851
26591
  function makeAsset(url, role, overrides = {}) {
27852
26592
  const defaults = ROLE_DEFAULTS[role] ?? { dimension: "2d", animations: ["static"], aspect: "1:1" };
@@ -27894,8 +26634,6 @@ var init_shared = __esm({
27894
26634
  init_isometric();
27895
26635
  init_spriteAnimation();
27896
26636
  init_spriteSheetConstants();
27897
- init_combatPresets();
27898
- init_canvasEffects();
27899
26637
  }
27900
26638
  });
27901
26639
  function GameCard({
@@ -29100,83 +27838,6 @@ var init_GameMenu = __esm({
29100
27838
  GameMenu.displayName = "GameMenu";
29101
27839
  }
29102
27840
  });
29103
- function useCanvasEffects({
29104
- manifest
29105
- }) {
29106
- const stateRef = React74.useRef({ ...exports.EMPTY_EFFECT_STATE });
29107
- const lastTimeRef = React74.useRef(0);
29108
- const shakeRef = React74.useRef({ x: 0, y: 0, intensity: 0 });
29109
- const [active, setActive] = React74.useState(false);
29110
- const [screenShake, setScreenShake] = React74.useState({ x: 0, y: 0 });
29111
- const [screenFlash, setScreenFlash] = React74.useState(null);
29112
- const effectSpriteUrls = React74.useMemo(
29113
- () => getAllEffectSpriteUrls(manifest),
29114
- [manifest]
29115
- );
29116
- const presets = React74.useMemo(
29117
- () => createCombatPresets(manifest),
29118
- [manifest]
29119
- );
29120
- const spawnEffect = React74.useCallback((type, screenX, screenY) => {
29121
- const now = performance.now();
29122
- const preset = presets[type](screenX, screenY);
29123
- const state = stateRef.current;
29124
- for (const emitter of preset.particles) {
29125
- state.particles.push(...spawnParticles(emitter, now));
29126
- }
29127
- for (const seqConfig of preset.sequences) {
29128
- state.sequences.push(spawnSequence(seqConfig, now));
29129
- }
29130
- for (const ovConfig of preset.overlays) {
29131
- state.overlays.push(spawnOverlay(ovConfig, now));
29132
- }
29133
- if (preset.screenShake > 0) {
29134
- shakeRef.current.intensity = preset.screenShake;
29135
- }
29136
- if (preset.screenFlash) {
29137
- const { r, g, b, duration } = preset.screenFlash;
29138
- setScreenFlash({ color: `rgb(${r}, ${g}, ${b})`, alpha: 0.3 });
29139
- setTimeout(() => setScreenFlash(null), duration);
29140
- }
29141
- setActive(true);
29142
- }, [presets]);
29143
- const drawEffects = React74.useCallback((ctx, animTime, getImage) => {
29144
- const delta = lastTimeRef.current > 0 ? animTime - lastTimeRef.current : 16;
29145
- lastTimeRef.current = animTime;
29146
- stateRef.current = updateEffectState(stateRef.current, animTime, delta);
29147
- if (shakeRef.current.intensity > 0.2) {
29148
- const i = shakeRef.current.intensity;
29149
- shakeRef.current.x = (Math.random() - 0.5) * i * 2;
29150
- shakeRef.current.y = (Math.random() - 0.5) * i * 2;
29151
- shakeRef.current.intensity *= 0.85;
29152
- setScreenShake({ x: shakeRef.current.x, y: shakeRef.current.y });
29153
- } else if (shakeRef.current.intensity > 0) {
29154
- shakeRef.current = { x: 0, y: 0, intensity: 0 };
29155
- setScreenShake({ x: 0, y: 0 });
29156
- }
29157
- drawEffectState(ctx, stateRef.current, animTime, getImage);
29158
- const isActive = hasActiveEffects(stateRef.current);
29159
- if (!isActive && active) {
29160
- setActive(false);
29161
- }
29162
- }, [active]);
29163
- return {
29164
- effectSpriteUrls,
29165
- spawnEffect,
29166
- drawEffects,
29167
- hasActiveEffects: active,
29168
- screenShake,
29169
- screenFlash
29170
- };
29171
- }
29172
- var init_useCanvasEffects = __esm({
29173
- "components/game/shared/hooks/useCanvasEffects.ts"() {
29174
- "use client";
29175
- init_effects();
29176
- init_canvasEffects();
29177
- init_combatPresets();
29178
- }
29179
- });
29180
27841
  function pickPath(entry) {
29181
27842
  if (Array.isArray(entry.path)) {
29182
27843
  return entry.path[Math.floor(Math.random() * entry.path.length)];
@@ -31863,7 +30524,7 @@ function NegotiatorBoard({
31863
30524
  React74.useEffect(() => {
31864
30525
  if (currentRound > prevRoundRef.current && lastPlayerAction) {
31865
30526
  const opponentPayoffEntry = payoffMatrix.find(
31866
- (p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
30527
+ (p) => p.playerAction === lastPlayerAction && p.opponentAction === lastOpponentAction
31867
30528
  );
31868
30529
  setHistory((prev) => [
31869
30530
  ...prev,
@@ -32156,14 +30817,14 @@ function SimulationCanvas({
32156
30817
  const ctx = canvas.getContext("2d");
32157
30818
  if (!ctx) return;
32158
30819
  const bodies = bodiesRef.current;
32159
- const p2 = presetRef.current;
30820
+ const p = presetRef.current;
32160
30821
  const w = widthRef.current;
32161
30822
  const h = heightRef.current;
32162
30823
  ctx.clearRect(0, 0, w, h);
32163
- ctx.fillStyle = p2.backgroundColor ?? "#1a1a2e";
30824
+ ctx.fillStyle = p.backgroundColor ?? "#1a1a2e";
32164
30825
  ctx.fillRect(0, 0, w, h);
32165
- if (p2.constraints) {
32166
- for (const c of p2.constraints) {
30826
+ if (p.constraints) {
30827
+ for (const c of p.constraints) {
32167
30828
  const a = bodies[c.bodyA];
32168
30829
  const b = bodies[c.bodyB];
32169
30830
  if (a && b) {
@@ -32186,7 +30847,7 @@ function SimulationCanvas({
32186
30847
  ctx.arc(pos.x, pos.y, body.radius, 0, Math.PI * 2);
32187
30848
  ctx.fillStyle = body.color ?? "#e94560";
32188
30849
  ctx.fill();
32189
- if (p2.showVelocity) {
30850
+ if (p.showVelocity) {
32190
30851
  ctx.beginPath();
32191
30852
  ctx.moveTo(pos.x, pos.y);
32192
30853
  ctx.lineTo(pos.x + body.vx * 0.1, pos.y + body.vy * 0.1);
@@ -32646,8 +31307,6 @@ var init_molecules = __esm({
32646
31307
  init_GameMenu();
32647
31308
  init_Canvas2D();
32648
31309
  init_useUnitSpriteAtlas();
32649
- init_CanvasEffect();
32650
- init_useCanvasEffects();
32651
31310
  init_GameAudioProvider();
32652
31311
  init_GameAudioToggle();
32653
31312
  init_useGameAudio();
@@ -32751,18 +31410,18 @@ var init_MathCanvas = __esm({
32751
31410
  });
32752
31411
  }
32753
31412
  }
32754
- for (const p2 of points) {
32755
- if (p2.x < xMin || p2.x > xMax || p2.y < yMin || p2.y > yMax) continue;
31413
+ for (const p of points) {
31414
+ if (p.x < xMin || p.x > xMax || p.y < yMin || p.y > yMax) continue;
32756
31415
  out.push({
32757
31416
  type: "circle",
32758
- x: mapX(p2.x),
32759
- y: mapY(p2.y),
32760
- radius: p2.radius ?? 4,
32761
- color: p2.color ?? "#dc2626",
32762
- fill: p2.color ?? "#dc2626"
31417
+ x: mapX(p.x),
31418
+ y: mapY(p.y),
31419
+ radius: p.radius ?? 4,
31420
+ color: p.color ?? "#dc2626",
31421
+ fill: p.color ?? "#dc2626"
32763
31422
  });
32764
- if (p2.label) {
32765
- out.push({ type: "text", x: mapX(p2.x) + 8, y: mapY(p2.y) - 8, text: p2.label, color: "#111827", fontSize: 12 });
31423
+ if (p.label) {
31424
+ out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
32766
31425
  }
32767
31426
  }
32768
31427
  for (const v of vectors) {
@@ -33281,13 +31940,13 @@ var init_MapView = __esm({
33281
31940
  shadowSize: [41, 41]
33282
31941
  });
33283
31942
  L.Marker.prototype.options.icon = defaultIcon;
33284
- const { useEffect: useEffect72, useRef: useRef68, useCallback: useCallback116, useState: useState110 } = React74__namespace.default;
31943
+ const { useEffect: useEffect71, useRef: useRef66, useCallback: useCallback114, useState: useState108 } = React74__namespace.default;
33285
31944
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33286
31945
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33287
31946
  function MapUpdater({ centerLat, centerLng, zoom }) {
33288
31947
  const map = useMap();
33289
- const prevRef = useRef68({ centerLat, centerLng, zoom });
33290
- useEffect72(() => {
31948
+ const prevRef = useRef66({ centerLat, centerLng, zoom });
31949
+ useEffect71(() => {
33291
31950
  const prev = prevRef.current;
33292
31951
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33293
31952
  map.setView([centerLat, centerLng], zoom);
@@ -33298,7 +31957,7 @@ var init_MapView = __esm({
33298
31957
  }
33299
31958
  function MapClickHandler({ onMapClick }) {
33300
31959
  const map = useMap();
33301
- useEffect72(() => {
31960
+ useEffect71(() => {
33302
31961
  if (!onMapClick) return;
33303
31962
  const handler = (e) => {
33304
31963
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -33326,8 +31985,8 @@ var init_MapView = __esm({
33326
31985
  showAttribution = true
33327
31986
  }) {
33328
31987
  const eventBus = useEventBus2();
33329
- const [clickedPosition, setClickedPosition] = useState110(null);
33330
- const handleMapClick = useCallback116((lat, lng) => {
31988
+ const [clickedPosition, setClickedPosition] = useState108(null);
31989
+ const handleMapClick = useCallback114((lat, lng) => {
33331
31990
  if (showClickedPin) {
33332
31991
  setClickedPosition({ lat, lng });
33333
31992
  }
@@ -33336,7 +31995,7 @@ var init_MapView = __esm({
33336
31995
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
33337
31996
  }
33338
31997
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
33339
- const handleMarkerClick = useCallback116((marker) => {
31998
+ const handleMarkerClick = useCallback114((marker) => {
33340
31999
  onMarkerClick?.(marker);
33341
32000
  if (markerClickEvent) {
33342
32001
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -34409,7 +33068,7 @@ function TableView({
34409
33068
  children: [
34410
33069
  header,
34411
33070
  dnd.wrapContainer(body),
34412
- hasMore && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p2) => p2 + (pageSize || 5)), children: [
33071
+ hasMore && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p) => p + (pageSize || 5)), children: [
34413
33072
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
34414
33073
  t("common.showMore"),
34415
33074
  " (",
@@ -37131,7 +35790,7 @@ var init_QrScanner = __esm({
37131
35790
  setCurrentFacing((f3) => f3 === "environment" ? "user" : "environment");
37132
35791
  }, []);
37133
35792
  const togglePause = React74.useCallback(() => {
37134
- setIsPaused((p2) => !p2);
35793
+ setIsPaused((p) => !p);
37135
35794
  }, []);
37136
35795
  const handleMockScan = React74.useCallback(() => {
37137
35796
  const result = {
@@ -40053,10 +38712,10 @@ var init_ModuleCard = __esm({
40053
38712
  ] })
40054
38713
  ] }, trait.name);
40055
38714
  }),
40056
- pages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 flex items-center gap-2 flex-wrap", children: pages.map((p2) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
38715
+ pages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 flex items-center gap-2 flex-wrap", children: pages.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
40057
38716
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 10, height: 10, viewBox: "0 0 12 12", children: /* @__PURE__ */ jsxRuntime.jsx(AvlPage, { x: 6, y: 6, size: 5 }) }),
40058
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-[var(--color-muted-foreground)]", children: p2.route })
40059
- ] }, p2.name)) })
38717
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-[var(--color-muted-foreground)]", children: p.route })
38718
+ ] }, p.name)) })
40060
38719
  ]
40061
38720
  }
40062
38721
  );
@@ -41592,12 +40251,12 @@ var init_DocumentViewer = __esm({
41592
40251
  const handleZoomIn = React74.useCallback(() => setZoom((z) => Math.min(z + 25, 200)), []);
41593
40252
  const handleZoomOut = React74.useCallback(() => setZoom((z) => Math.max(z - 25, 50)), []);
41594
40253
  const handlePagePrev = React74.useCallback(() => {
41595
- setCurrentPage((p2) => Math.max(p2 - 1, 1));
40254
+ setCurrentPage((p) => Math.max(p - 1, 1));
41596
40255
  eventBus.emit("UI:DOCUMENT_PAGE_CHANGE", { page: currentPage - 1 });
41597
40256
  }, [eventBus, currentPage]);
41598
40257
  const handlePageNext = React74.useCallback(() => {
41599
40258
  if (totalPages) {
41600
- setCurrentPage((p2) => Math.min(p2 + 1, totalPages));
40259
+ setCurrentPage((p) => Math.min(p + 1, totalPages));
41601
40260
  eventBus.emit("UI:DOCUMENT_PAGE_CHANGE", { page: currentPage + 1 });
41602
40261
  }
41603
40262
  }, [totalPages, eventBus, currentPage]);
@@ -45896,7 +44555,7 @@ function WalkMinimap() {
45896
44555
  const graphH = 120;
45897
44556
  const nodeR = 10;
45898
44557
  const positions = activeTrait ? layoutGraph(activeTrait.states, activeTrait.transitions, activeTrait.initialState, graphW, graphH) : [];
45899
- const posMap = new Map(positions.map((p2) => [p2.state, p2]));
44558
+ const posMap = new Map(positions.map((p) => [p.state, p]));
45900
44559
  const visitedStates = new Set(
45901
44560
  coveredEdges.filter((e) => e.trait === walkStep.traitName).flatMap((e) => [e.from, e.to])
45902
44561
  );
@@ -49166,7 +47825,6 @@ var init_component_registry_generated = __esm({
49166
47825
  init_CTABanner();
49167
47826
  init_CalendarGrid();
49168
47827
  init_Canvas2D();
49169
- init_CanvasEffect();
49170
47828
  init_Card();
49171
47829
  init_CardGrid();
49172
47830
  init_Carousel();
@@ -49465,7 +48123,6 @@ var init_component_registry_generated = __esm({
49465
48123
  "CTABanner": exports.CTABanner,
49466
48124
  "CalendarGrid": CalendarGrid,
49467
48125
  "Canvas2D": Canvas2D,
49468
- "CanvasEffect": CanvasEffect,
49469
48126
  "Card": exports.Card,
49470
48127
  "CardGrid": exports.CardGrid,
49471
48128
  "Carousel": exports.Carousel,
@@ -52960,7 +51617,6 @@ exports.ActionTile = ActionTile;
52960
51617
  exports.BuilderBoard = BuilderBoard;
52961
51618
  exports.CalendarGrid = CalendarGrid;
52962
51619
  exports.Canvas2D = Canvas2D;
52963
- exports.CanvasEffect = CanvasEffect;
52964
51620
  exports.ChatBar = ChatBar;
52965
51621
  exports.ChoiceButton = ChoiceButton;
52966
51622
  exports.ClassifierBoard = ClassifierBoard;
@@ -53042,17 +51698,12 @@ exports.bool = bool;
53042
51698
  exports.calculateAttackTargets = calculateAttackTargets;
53043
51699
  exports.calculateValidMoves = calculateValidMoves;
53044
51700
  exports.cn = cn;
53045
- exports.createCombatPresets = createCombatPresets;
53046
51701
  exports.createInitialGameState = createInitialGameState;
53047
51702
  exports.createTranslate = createTranslate;
53048
51703
  exports.createUnitAnimationState = createUnitAnimationState;
53049
- exports.drawEffectState = drawEffectState;
53050
51704
  exports.drawSprite = drawSprite;
53051
- exports.drawTintedImage = drawTintedImage;
53052
- exports.getAllEffectSpriteUrls = getAllEffectSpriteUrls;
53053
51705
  exports.getCurrentFrame = getCurrentFrame;
53054
51706
  exports.getTileDimensions = getTileDimensions;
53055
- exports.hasActiveEffects = hasActiveEffects;
53056
51707
  exports.inferDirection = inferDirection;
53057
51708
  exports.isoToScreen = isoToScreen;
53058
51709
  exports.makeAsset = makeAsset;
@@ -53077,9 +51728,6 @@ exports.resolveFrame = resolveFrame;
53077
51728
  exports.resolveSheetDirection = resolveSheetDirection;
53078
51729
  exports.rows = rows;
53079
51730
  exports.screenToIso = screenToIso;
53080
- exports.spawnOverlay = spawnOverlay;
53081
- exports.spawnParticles = spawnParticles;
53082
- exports.spawnSequence = spawnSequence;
53083
51731
  exports.str = str;
53084
51732
  exports.tickAnimationState = tickAnimationState;
53085
51733
  exports.toCodeLanguage = toCodeLanguage;
@@ -53087,12 +51735,10 @@ exports.transitionAnimation = transitionAnimation;
53087
51735
  exports.unitHealth = unitHealth;
53088
51736
  exports.unitPosition = unitPosition;
53089
51737
  exports.unitTeam = unitTeam;
53090
- exports.updateEffectState = updateEffectState;
53091
51738
  exports.useAgentChat = useAgentChat;
53092
51739
  exports.useAnchorRect = useAnchorRect;
53093
51740
  exports.useAuthContext = useAuthContext;
53094
51741
  exports.useCamera = useCamera;
53095
- exports.useCanvasEffects = useCanvasEffects;
53096
51742
  exports.useCanvasGestures = useCanvasGestures;
53097
51743
  exports.useCompile = useCompile;
53098
51744
  exports.useConnectGitHub = useConnectGitHub;