diff --git a/public/src/games/uno/UnoGame.js b/public/src/games/uno/UnoGame.js index 9e1cb1a..e9e17dc 100644 --- a/public/src/games/uno/UnoGame.js +++ b/public/src/games/uno/UnoGame.js @@ -699,7 +699,17 @@ export default class UnoGame extends Phaser.Scene { slotIndicator.setDepth(D.card - 1); this.transientObjs.push(slotIndicator); - this.dragState = { cardIdx: handIdx, offsetX, offsetY, prevX: card.x, shadow, slotIndicator, insertIdx, spread, handStartX, handCenterY }; + const discardHighlight = this.add.graphics(); + discardHighlight.lineStyle(3, COLORS.accent, 0.9); + discardHighlight.strokeRoundedRect(-CARD_W / 2 - 6, -CARD_H / 2 - 6, CARD_W + 12, CARD_H + 12, 12); + discardHighlight.fillStyle(COLORS.accent, 0.18); + discardHighlight.fillRoundedRect(-CARD_W / 2 - 6, -CARD_H / 2 - 6, CARD_W + 12, CARD_H + 12, 12); + discardHighlight.setPosition(DISCARD_POS.x, DISCARD_POS.y); + discardHighlight.setDepth(D.card + 1); + discardHighlight.setAlpha(0); + this.transientObjs.push(discardHighlight); + + this.dragState = { cardIdx: handIdx, offsetX, offsetY, prevX: card.x, shadow, slotIndicator, discardHighlight, insertIdx, spread, handStartX, handCenterY, overDiscard: false }; card.setDepth(D.card + 10); this.tweens.add({ targets: card, scaleX: 1.08, scaleY: 1.08, duration: 120, ease: 'Cubic.easeOut' }); @@ -718,12 +728,50 @@ export default class UnoGame extends Phaser.Scene { card.setRotation(tiltRad); ds.prevX = pointer.x; - const newInsertIdx = Phaser.Math.Clamp(Math.round((card.x - ds.handStartX) / ds.spread), 0, Math.max(0, n - 1)); - if (newInsertIdx !== ds.insertIdx) { - ds.insertIdx = newInsertIdx; - this.tweens.killTweensOf(ds.slotIndicator); - this.tweens.add({ targets: ds.slotIndicator, x: ds.handStartX + newInsertIdx * ds.spread, duration: 100, ease: 'Cubic.easeOut' }); - this._updateNonDraggedCards(); + const overDiscard = this._isDiscardDropTarget(card.x, card.y); + if (overDiscard !== ds.overDiscard) { + ds.overDiscard = overDiscard; + if (overDiscard) { + this.tweens.killTweensOf(ds.slotIndicator); + ds.slotIndicator.setAlpha(0); + this._settleNonDraggedCards(); + this.tweens.add({ targets: ds.discardHighlight, alpha: 1, duration: 150 }); + } else { + ds.slotIndicator.setAlpha(1); + this._updateNonDraggedCards(); + this.tweens.add({ targets: ds.discardHighlight, alpha: 0, duration: 150 }); + } + } + + if (!overDiscard) { + const newInsertIdx = Phaser.Math.Clamp(Math.round((card.x - ds.handStartX) / ds.spread), 0, Math.max(0, n - 1)); + if (newInsertIdx !== ds.insertIdx) { + ds.insertIdx = newInsertIdx; + this.tweens.killTweensOf(ds.slotIndicator); + this.tweens.add({ targets: ds.slotIndicator, x: ds.handStartX + newInsertIdx * ds.spread, duration: 100, ease: 'Cubic.easeOut' }); + this._updateNonDraggedCards(); + } + } + } + + _isDiscardDropTarget(cardX, cardY) { + if (!this.isLocalTurn() || this.gs.phase !== 'play') return false; + const me = this.gs.players[0]; + const handCard = me.hand[this.dragState.cardIdx]; + if (!handCard || !isLegalPlay(this.gs, handCard, me.hand)) return false; + return Math.abs(cardX - DISCARD_POS.x) < CARD_W * 1.2 && Math.abs(cardY - DISCARD_POS.y) < CARD_H * 1.2; + } + + _settleNonDraggedCards() { + const ds = this.dragState; + const n = this.gs.players[0].hand.length; + let k = 0; + for (let j = 0; j < n; j++) { + if (j === ds.cardIdx) continue; + const cardObj = this.localHandCards[j]; + this.tweens.killTweensOf(cardObj); + this.tweens.add({ targets: cardObj, x: ds.handStartX + k * ds.spread, rotation: 0, scaleX: 1, scaleY: 1, duration: 100, ease: 'Cubic.easeOut' }); + k++; } } @@ -752,6 +800,32 @@ export default class UnoGame extends Phaser.Scene { const card = this.localHandCards[ds.cardIdx]; const hand = this.gs.players[0].hand; const n = hand.length; + + this.tweens.killTweensOf(ds.discardHighlight); + ds.discardHighlight.setAlpha(0); + + // ── Play path: dropped on discard pile ─────────────────────────────────── + if (ds.overDiscard) { + const handCard = hand[ds.cardIdx]; + this.tweens.killTweensOf(card); + card.setScale(1); + card.setRotation(0); + ds.shadow.setAlpha(0); + this._settleNonDraggedCards(); + this.dragState = null; + if (handCard.isWild) { + this.pendingWildPlayCardId = handCard.id; + this.openColorPicker((color) => { + this.pendingWildPlayCardId = null; + this.executeLocalPlay(handCard.id, color); + }); + } else { + this.executeLocalPlay(handCard.id); + } + return; + } + + // ── Reorder path: dropped back in hand ─────────────────────────────────── const finalIdx = Phaser.Math.Clamp(Math.round((card.x - ds.handStartX) / ds.spread), 0, n - 1); this.tweens.killTweensOf(card);