polats Claude Opus 4.8 (1M context) commited on
Commit
2cea337
·
1 Parent(s): f9e5860

Rebuild comboBattler: procedural skill VFX

Browse files

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. web/comboBattler.js +143 -0
web/comboBattler.js CHANGED
@@ -6036,6 +6036,106 @@ async function createEmoteLayer(pixi, parent, { url, unit, sizeTiles = 1.3, head
6036
  return { show, update, clear, destroy, keys: Object.keys(CELL) };
6037
  }
6038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6039
  // ../auto-battler/src/render/comboBattler.js
6040
  var TILE8 = 8;
6041
  var SKILL_YELLOW = 16769357;
@@ -6092,6 +6192,7 @@ function mountComboBattler(pixi, host, opts = {}) {
6092
  })();
6093
  let battle = null, R = null, combatRoot = null, rings = null, markers = null, navDbg = null, spawnFn = null, dead = false;
6094
  let emoteLayer = null;
 
6095
  const emoPrev = /* @__PURE__ */ new Map();
6096
  let idleEmoAcc = 0;
6097
  let runState = { time: 0, kills: 0, diff: 1 };
@@ -6417,6 +6518,18 @@ function mountComboBattler(pixi, host, opts = {}) {
6417
  for (const e of ea()) if (e.alive) m = Math.min(m, dist3(p, e));
6418
  return m;
6419
  };
 
 
 
 
 
 
 
 
 
 
 
 
6420
  function tick(ticker) {
6421
  if (!battle || !R) return;
6422
  if (paused) return;
@@ -6504,6 +6617,23 @@ function mountComboBattler(pixi, host, opts = {}) {
6504
  R.updateFloats(dtMS);
6505
  R.drawProjectiles(battle);
6506
  R.processLog(battle, cursor, {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6507
  onHit: (e) => {
6508
  const p2 = pa();
6509
  if (!p2) return;
@@ -6539,6 +6669,7 @@ function mountComboBattler(pixi, host, opts = {}) {
6539
  }
6540
  }
6541
  updateEmotes(dt);
 
6542
  updateHud();
6543
  drawRings();
6544
  drawMarkers();
@@ -6693,6 +6824,11 @@ function mountComboBattler(pixi, host, opts = {}) {
6693
  }
6694
  emoteLayer = null;
6695
  emoPrev.clear();
 
 
 
 
 
6696
  hitstopT = 0;
6697
  shake.t = 0;
6698
  shake.mag = 0;
@@ -6782,9 +6918,16 @@ function mountComboBattler(pixi, host, opts = {}) {
6782
  emoteLayer = null;
6783
  }
6784
  }
 
 
 
 
 
6785
  if (!alive) {
6786
  emoteLayer?.destroy();
6787
  emoteLayer = null;
 
 
6788
  return;
6789
  }
6790
  let foeN = 0;
 
6036
  return { show, update, clear, destroy, keys: Object.keys(CELL) };
6037
  }
6038
 
6039
+ // ../auto-battler/src/render/skillVfx.js
6040
+ function createSkillVfx(pixi, parent, { gScale = 1 } = {}) {
6041
+ const { Container, Graphics } = pixi;
6042
+ const layer = new Container();
6043
+ layer.zIndex = 6e8;
6044
+ parent.addChild(layer);
6045
+ const active = [];
6046
+ const hex = (s) => {
6047
+ const n = parseInt(String(s || "").replace("#", ""), 16);
6048
+ return Number.isFinite(n) ? n : 16777215;
6049
+ };
6050
+ const lwOf = (base) => base / gScale;
6051
+ function play(visual, x, y, radius, now, { aim } = {}) {
6052
+ const g = new Graphics();
6053
+ layer.addChild(g);
6054
+ active.push({ g, born: now, dur: 0.45 + (visual?.intensity || 2) * 0.13, shape: visual?.shape || "nova", color: hex(visual?.color), intensity: visual?.intensity || 2, x, y, radius: radius || 120, aim });
6055
+ }
6056
+ function update(now) {
6057
+ for (let i = active.length - 1; i >= 0; i--) {
6058
+ const e = active[i];
6059
+ const t = (now - e.born) / e.dur;
6060
+ if (t >= 1 || t < 0) {
6061
+ try {
6062
+ e.g.destroy();
6063
+ } catch {
6064
+ }
6065
+ active.splice(i, 1);
6066
+ continue;
6067
+ }
6068
+ draw(e, t);
6069
+ }
6070
+ }
6071
+ function draw(e, t) {
6072
+ const g = e.g;
6073
+ g.clear();
6074
+ const ease = 1 - Math.pow(1 - t, 2);
6075
+ const a = 1 - t;
6076
+ const lw = lwOf(2 + e.intensity);
6077
+ const c = e.color, R = e.radius;
6078
+ switch (e.shape) {
6079
+ case "ring":
6080
+ case "shockwave": {
6081
+ const r = R * ease;
6082
+ g.circle(e.x, e.y, r).stroke({ width: lw * 1.6, color: c, alpha: a * 0.95 });
6083
+ if (e.shape === "shockwave") g.circle(e.x, e.y, r * 0.66).stroke({ width: lw, color: c, alpha: a * 0.5 });
6084
+ break;
6085
+ }
6086
+ case "beam": {
6087
+ const aim = e.aim || { x: e.x + R, y: e.y };
6088
+ g.moveTo(e.x, e.y).lineTo(aim.x, aim.y).stroke({ width: lw * 2.4 * (1 - t * 0.5), color: c, alpha: a });
6089
+ g.circle(aim.x, aim.y, R * 0.18 * ease).fill({ color: c, alpha: a * 0.7 });
6090
+ break;
6091
+ }
6092
+ case "pillar": {
6093
+ const w = R * 0.5 * (1 - t * 0.3), h = R * 1.5;
6094
+ g.rect(e.x - w / 2, e.y - h, w, h).fill({ color: c, alpha: a * 0.32 });
6095
+ g.rect(e.x - w / 2, e.y - h, w, h).stroke({ width: lw, color: c, alpha: a * 0.85 });
6096
+ break;
6097
+ }
6098
+ case "projectile": {
6099
+ const r = R * ease;
6100
+ g.circle(e.x, e.y, Math.max(lwOf(4), R * 0.16)).fill({ color: c, alpha: a });
6101
+ g.circle(e.x, e.y, r).stroke({ width: lw, color: c, alpha: a * 0.4 });
6102
+ break;
6103
+ }
6104
+ case "slash": {
6105
+ const r = R * (0.6 + ease * 0.5);
6106
+ g.arc(e.x, e.y, r, -0.8 + t * 1.2, 0.8 + t * 1.2).stroke({ width: lw * 2, color: c, alpha: a });
6107
+ break;
6108
+ }
6109
+ case "nova":
6110
+ default: {
6111
+ const r = R * ease;
6112
+ g.circle(e.x, e.y, r).fill({ color: c, alpha: a * 0.22 });
6113
+ g.circle(e.x, e.y, r).stroke({ width: lw * 1.5, color: c, alpha: a * 0.9 });
6114
+ const spokes = e.intensity * 4;
6115
+ for (let k = 0; k < spokes; k++) {
6116
+ const ang = k / spokes * Math.PI * 2;
6117
+ g.moveTo(e.x, e.y).lineTo(e.x + Math.cos(ang) * r, e.y + Math.sin(ang) * r).stroke({ width: lw * 0.6, color: c, alpha: a * 0.5 });
6118
+ }
6119
+ break;
6120
+ }
6121
+ }
6122
+ }
6123
+ function destroy() {
6124
+ for (const e of active) {
6125
+ try {
6126
+ e.g.destroy();
6127
+ } catch {
6128
+ }
6129
+ }
6130
+ active.length = 0;
6131
+ try {
6132
+ layer.destroy({ children: true });
6133
+ } catch {
6134
+ }
6135
+ }
6136
+ return { play, update, destroy };
6137
+ }
6138
+
6139
  // ../auto-battler/src/render/comboBattler.js
6140
  var TILE8 = 8;
6141
  var SKILL_YELLOW = 16769357;
 
6192
  })();
6193
  let battle = null, R = null, combatRoot = null, rings = null, markers = null, navDbg = null, spawnFn = null, dead = false;
6194
  let emoteLayer = null;
6195
+ let skillVfxLayer = null;
6196
  const emoPrev = /* @__PURE__ */ new Map();
6197
  let idleEmoAcc = 0;
6198
  let runState = { time: 0, kills: 0, diff: 1 };
 
6518
  for (const e of ea()) if (e.alive) m = Math.min(m, dist3(p, e));
6519
  return m;
6520
  };
6521
+ const nearestFoeTo = (a) => {
6522
+ if (!a) return null;
6523
+ let m = Infinity, best = null;
6524
+ for (const e of ea()) if (e.alive) {
6525
+ const d = dist3(a, e);
6526
+ if (d < m) {
6527
+ m = d;
6528
+ best = e;
6529
+ }
6530
+ }
6531
+ return best;
6532
+ };
6533
  function tick(ticker) {
6534
  if (!battle || !R) return;
6535
  if (paused) return;
 
6617
  R.updateFloats(dtMS);
6618
  R.drawProjectiles(battle);
6619
  R.processLog(battle, cursor, {
6620
+ onCast: (e) => {
6621
+ const caster = battle.actors.find((x) => x.id === e.who);
6622
+ const skill = caster?.bar?.find((s) => s && s.id === e.skillId);
6623
+ if (skill?.visual && skillVfxLayer) {
6624
+ const aoe = (skill.effects || []).find((x) => x.scope === "area_around_caster");
6625
+ const radius = aoe?.range || 110;
6626
+ let aim = null;
6627
+ if (skill.visual.shape === "beam" || skill.visual.shape === "projectile") {
6628
+ const f = nearestFoeTo(caster);
6629
+ if (f) aim = { x: f.x, y: f.y };
6630
+ }
6631
+ skillVfxLayer.play(skill.visual, caster.x, caster.y, radius, battle.t, { aim });
6632
+ const inten = skill.visual.intensity || 2;
6633
+ addShake(0.1 + inten * 0.05, 2 + inten * 1.5);
6634
+ }
6635
+ return {};
6636
+ },
6637
  onHit: (e) => {
6638
  const p2 = pa();
6639
  if (!p2) return;
 
6669
  }
6670
  }
6671
  updateEmotes(dt);
6672
+ skillVfxLayer?.update(battle.t);
6673
  updateHud();
6674
  drawRings();
6675
  drawMarkers();
 
6824
  }
6825
  emoteLayer = null;
6826
  emoPrev.clear();
6827
+ try {
6828
+ skillVfxLayer?.destroy();
6829
+ } catch {
6830
+ }
6831
+ skillVfxLayer = null;
6832
  hitstopT = 0;
6833
  shake.t = 0;
6834
  shake.mag = 0;
 
6918
  emoteLayer = null;
6919
  }
6920
  }
6921
+ try {
6922
+ skillVfxLayer = createSkillVfx(pixi, combatRoot, { gScale: G });
6923
+ } catch {
6924
+ skillVfxLayer = null;
6925
+ }
6926
  if (!alive) {
6927
  emoteLayer?.destroy();
6928
  emoteLayer = null;
6929
+ skillVfxLayer?.destroy();
6930
+ skillVfxLayer = null;
6931
  return;
6932
  }
6933
  let foeN = 0;