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;