From 6b819e00b0a7679c0cbbbf86e10fe26ebd42d2f6 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Tue, 19 May 2026 20:17:23 -0600 Subject: [PATCH] add visual skip overlay for skipped opponents - Create a red "X" overlay that animates onto the portrait of a skipped opponent - Implement `showSkipOverlay` to render the effect using Phaser DOM objects - Trigger the overlay when a player is skipped to provide clearer visual feedback --- public/src/games/uno/UnoGame.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/public/src/games/uno/UnoGame.js b/public/src/games/uno/UnoGame.js index 2ada24b..3fb34ef 100644 --- a/public/src/games/uno/UnoGame.js +++ b/public/src/games/uno/UnoGame.js @@ -1088,6 +1088,7 @@ export default class UnoGame extends Phaser.Scene { const e = after.log[i]; if (e.kind === 'skip') { this.opponentPortraits[e.seat]?.playEmotion('upset'); + this.showSkipOverlay(e.seat); this.showBanner(`${this.opponentName(e.seat)} is skipped!`); this.time.delayedCall(900, () => this.hideBanner()); } else if (e.kind === 'reverse') { @@ -1271,6 +1272,36 @@ export default class UnoGame extends Phaser.Scene { }); } + showSkipOverlay(skippedSeat) { + const slot = this.slotForSeat[skippedSeat]; + const layout = slotLayout(slot, this.gs.players.length); + const { x: px, y: py, r: pr } = layout.portrait; + const r = pr + 8; + const size = r * 2; + const strokeW = Math.round(r * 0.09); + const slashW = Math.round(r * 0.13); + + // Build as a DOM element so it renders above the portrait video layer. + const el = document.createElement('div'); + el.style.cssText = `width:${size}px;height:${size}px;border-radius:50%;background:transparent;border:${strokeW}px solid rgba(204,0,0,0.95);box-sizing:border-box;position:relative;overflow:hidden;`; + const slash = document.createElement('div'); + slash.style.cssText = `position:absolute;width:${slashW}px;height:${Math.round(size * 1.1)}px;background:rgba(204,0,0,0.95);border-radius:${Math.round(slashW / 2)}px;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-45deg);`; + el.appendChild(slash); + + const domObj = this.add.dom(DISCARD_POS.x, DISCARD_POS.y, el); + domObj.setDepth(D.banner); + domObj.setScale(0.2); + this.transientObjs.push(domObj); + + this.tweens.add({ + targets: domObj, + x: px, y: py, + scaleX: 1, scaleY: 1, + duration: 1500, + ease: 'Back.easeOut', + }); + } + // ── Color picker modal ──────────────────────────────────────────────────── openColorPicker(onPick) {