diff --git a/public/game.html b/public/game.html index 16f5662..d0e991a 100644 --- a/public/game.html +++ b/public/game.html @@ -371,6 +371,29 @@ logs... { + try { + if (eff && eff.type === 'toucher') { + let targetSquare = eff.pieceSquare || null; + if (!targetSquare && eff.pieceId && data.boardState && Array.isArray(data.boardState.pieces)) { + const p = data.boardState.pieces.find((x) => x.id === eff.pieceId); + if (p && p.square) targetSquare = p.square; + } + if (targetSquare && boardEl) { + const el = boardEl.querySelector(`.square[data-square="${targetSquare}"]`); + if (el) { + el.classList.add('targeted'); + try { el.dataset.toucherEffectId = eff.id || ''; } catch(_) {} + } + } + } + } catch(_) {} + }); + } + } catch(_) {} // restore last-move highlight from localStorage (persist across refresh) try { if (roomId) { @@ -554,10 +577,62 @@ logs... x.id === effect.pieceId); + if (p && p.square) targetSquare = p.square; + } + if (targetSquare && boardEl) { + const el = boardEl.querySelector(`.square[data-square="${targetSquare}"]`); + if (el) { + el.classList.add("targeted"); + try { el.dataset.toucherEffectId = effect.id || ""; } catch(_) {} + } + } + } + } catch (e) {} } catch (e) { console.warn("card:effect:applied handler error", e); } }); + + // toucher + socket.on("card:effect:removed", (data) => { + try { + const effId = data && (data.effectId || data.id); + const type = data && data.type; + if (!effId) return; + if (type === "toucher") { + // find any square with matching dataset.toucherEffectId and remove the visual + try { + const sq = boardEl && boardEl.querySelector && boardEl.querySelector(`.square[data-toucher-effect-id][data-toucher-effect-id="${effId}"]`); + if (sq) { + sq.classList.remove("last-move-source"); + sq.classList.remove("targeted"); + try { delete sq.dataset.toucherEffectId; } catch(_) {} + } else { + // fallback: remove any .last-move-source or .targeted if its dataset matches + const all = boardEl.querySelectorAll('.square.last-move-source, .square.targeted'); + all.forEach((s) => { + try { + if (s.dataset && s.dataset.toucherEffectId === String(effId)) { + s.classList.remove('last-move-source'); + s.classList.remove('targeted'); + delete s.dataset.toucherEffectId; + } + } catch(_) {} + }); + } + } catch (_) {} + } + } catch (e) { + console.warn('card:effect:removed handler error', e); + } + }); // when the server sends the stolen card to the stealer (private) socket.on("card:stolen", (data) => { try { diff --git a/public/styles/style.css b/public/styles/style.css index 8e18fc2..093996b 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -245,23 +245,29 @@ button:hover{transform:translateY(-3px);transition:all 180ms ease} } #board.shuffle-play .square img{ transition: transform 700ms cubic-bezier(.2,.9,.2,1); transform: rotate(2deg) scale(0.995); filter: drop-shadow(0 6px 18px rgba(0,0,0,0.06)); } -/* overlay that fills the whole square (subtle green) so the entire cell is highlighted */ +.square.targeted::after{ + content: ''; + position: absolute; + left: 0; top: 0; right: 0; bottom: 0; + background: rgb(255, 201, 201); + pointer-events: none; + z-index: 2; +} + .square.last-move-dest::after{ content: ''; position: absolute; left: 0; top: 0; right: 0; bottom: 0; - background: rgb(164, 229, 208); + background: rgb(212, 255, 241); pointer-events: none; z-index: 2; } -/* subtle highlight for the source square of the last move (less prominent) */ .square.last-move-source::after{ content: ''; position: absolute; left: 0; top: 0; right: 0; bottom: 0; - background: rgb(164, 229, 208); + background: rgb(212, 255, 241); pointer-events: none; z-index: 2; -} - +} \ No newline at end of file diff --git a/server.js b/server.js index c0c914d..09ffd8a 100644 --- a/server.js +++ b/server.js @@ -2538,6 +2538,27 @@ io.on("connection", (socket) => { const removed = hand.splice(idx, 1)[0]; room.hands[senderId] = hand; room.discard = room.discard || []; + // touhcer + try { + room.activeCardEffects = room.activeCardEffects || []; + for (let ei = room.activeCardEffects.length - 1; ei >= 0; ei--) { + const ev = room.activeCardEffects[ei]; + if (!ev) continue; + if (ev.type === "toucher" && ev.playerId === senderId) { + try { + room.activeCardEffects.splice(ei, 1); + } catch (_) {} + try { + io.to(room.id).emit("card:effect:removed", { + roomId: room.id, + effectId: ev.id, + type: ev.type, + playerId: ev.playerId, + }); + } catch (_) {} + } + } + } catch (_) {} if (!room.noRemise) { room.discard.push(removed); played.card = removed;