feat(dominion): add animated comet trails to playable hand cards
- Implement a visual effect using Phaser Graphics to draw animated "comet" trails along the perimeter of playable action and treasure cards. - Add `perimPoint` helper to calculate coordinates along a rectangle's perimeter. - Manage particle graphics and tweens via new `_handFxGraphics` and `_handFxTweens` arrays, ensuring proper cleanup during render cycles. - Replace simple face highlighting with the new `_buildHandFxItem` method for enhanced visual feedback.
This commit is contained in:
parent
285d992915
commit
41466e14f3
|
|
@ -18,6 +18,20 @@ const CX = GAME_WIDTH / 2;
|
|||
|
||||
const D = { bg: -1, supply: 5, inplay: 6, hand: 10, hud: 15, portrait: 20, prompt: 40, modal: 80, popup: 95 };
|
||||
|
||||
// Returns the screen (x, y) at distance d along the clockwise perimeter of a
|
||||
// rectangle with top-left corner (left, top) and dimensions W×H.
|
||||
function perimPoint(d, W, H, left, top) {
|
||||
const perim = 2 * (W + H);
|
||||
d = ((d % perim) + perim) % perim;
|
||||
if (d < W) return [left + d, top ];
|
||||
d -= W;
|
||||
if (d < H) return [left + W, top + d];
|
||||
d -= H;
|
||||
if (d < W) return [left + W - d, top + H];
|
||||
d -= W;
|
||||
return [left, top + H - d];
|
||||
}
|
||||
|
||||
const SUPPLY_W = 100, SUPPLY_H = 144;
|
||||
const HAND_W = 132, HAND_H = 190;
|
||||
const PLAY_W = 78, PLAY_H = 112;
|
||||
|
|
@ -84,6 +98,8 @@ export default class DominionGame extends Phaser.Scene {
|
|||
this._arrowSeat = null;
|
||||
this._boughtThisTurn = false;
|
||||
this._suppressTurnUi = false;
|
||||
this._handFxGraphics = [];
|
||||
this._handFxTweens = [];
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
@ -404,6 +420,7 @@ export default class DominionGame extends Phaser.Scene {
|
|||
// ── Render (dynamic layer) ───────────────────────────────────────────────────
|
||||
|
||||
render() {
|
||||
this._clearHandFx();
|
||||
if (this.hoverTimer) { this.hoverTimer.remove(); this.hoverTimer = null; }
|
||||
this.hideHover();
|
||||
this.dynamicLayer?.destroy(true);
|
||||
|
|
@ -554,8 +571,8 @@ export default class DominionGame extends Phaser.Scene {
|
|||
const hs = { iid: c.iid, id: c.id, def, x, baseY, face, hit, isPlayableAction, isPlayableTreasure };
|
||||
this.handSprites.push(hs);
|
||||
|
||||
if (isPlayableAction) this.highlightFace(face, COLORS.accent);
|
||||
else if (isPlayableTreasure) this.highlightFace(face, COLORS.gold);
|
||||
if (isPlayableAction) this._buildHandFxItem(hs, COLORS.accent);
|
||||
else if (isPlayableTreasure) this._buildHandFxItem(hs, COLORS.gold);
|
||||
|
||||
hit.on('pointerdown', (ptr) => {
|
||||
if (this._animating) return;
|
||||
|
|
@ -577,6 +594,62 @@ export default class DominionGame extends Phaser.Scene {
|
|||
face.addAt(glow, 0);
|
||||
}
|
||||
|
||||
_clearHandFx() {
|
||||
this._handFxTweens.forEach(t => t.destroy());
|
||||
this._handFxGraphics.forEach(g => g.destroy());
|
||||
this._handFxTweens = [];
|
||||
this._handFxGraphics = [];
|
||||
}
|
||||
|
||||
_buildHandFxItem(hs, color) {
|
||||
const gfx = this.add.graphics();
|
||||
gfx.setDepth(D.hand + 25);
|
||||
this._handFxGraphics.push(gfx);
|
||||
|
||||
const W = HAND_W, H = HAND_H;
|
||||
const perim = 2 * (W + H);
|
||||
const TAIL_PX = 90;
|
||||
const SEGMENTS = 20;
|
||||
const COMETS = 2;
|
||||
|
||||
const tracker = { t: 0 };
|
||||
const tween = this.tweens.add({
|
||||
targets: tracker,
|
||||
t: { from: 0, to: 1 },
|
||||
duration: 1200,
|
||||
repeat: -1,
|
||||
ease: 'Linear',
|
||||
onUpdate: () => {
|
||||
const left = hs.x - W / 2;
|
||||
const top = hs.face.y - H / 2;
|
||||
gfx.clear();
|
||||
|
||||
gfx.lineStyle(2, color, 0.2);
|
||||
gfx.strokeRect(left, top, W, H);
|
||||
|
||||
for (let c = 0; c < COMETS; c++) {
|
||||
const headDist = ((tracker.t + c / COMETS) % 1) * perim;
|
||||
for (let seg = 0; seg < SEGMENTS; seg++) {
|
||||
const frac = seg / SEGMENTS;
|
||||
const d1 = ((headDist - (1 - frac) * TAIL_PX + perim * 100) % perim);
|
||||
const d2 = ((headDist - (1 - (seg + 1) / SEGMENTS) * TAIL_PX + perim * 100) % perim);
|
||||
const [x1, y1] = perimPoint(d1, W, H, left, top);
|
||||
const [x2, y2] = perimPoint(d2, W, H, left, top);
|
||||
const alpha = 0.1 + frac * 0.85;
|
||||
const lineColor = frac > 0.85 ? 0xffffff : color;
|
||||
const lineWidth = frac > 0.85 ? 4 : 2;
|
||||
gfx.lineStyle(lineWidth, lineColor, alpha);
|
||||
gfx.beginPath();
|
||||
gfx.moveTo(x1, y1);
|
||||
gfx.lineTo(x2, y2);
|
||||
gfx.strokePath();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
this._handFxTweens.push(tween);
|
||||
}
|
||||
|
||||
renderCounts() {
|
||||
const gs = this.gs;
|
||||
// Human deck — face-down card pile with count badge
|
||||
|
|
|
|||
Loading…
Reference in New Issue