bouton de pioche
This commit is contained in:
parent
a73e07f510
commit
18403495eb
3 changed files with 147 additions and 10 deletions
|
|
@ -20,9 +20,13 @@
|
|||
<div style="margin-top:8px">
|
||||
<!-- movement controls removed as requested -->
|
||||
</div>
|
||||
<pre id="log" style="margin-top:8px;height:160px;overflow:auto">logs...</pre>
|
||||
<div style="margin-top:8px;display:flex;gap:8px;align-items:center">
|
||||
<pre id="log" style="margin-top:0px;height:140px;overflow:auto;flex:1">logs...</pre>
|
||||
</div>
|
||||
</section>
|
||||
<div id="hands" style="margin-top:8px;display:flex;flex-direction:column;gap:8px"></div>
|
||||
<div style="margin-top:10px;display:flex;align-items:center;gap:12px">
|
||||
<div id="hands" style="display:flex;flex-direction:column;gap:8px;flex:1;margin-left:6px"></div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
|
|
@ -154,6 +158,23 @@
|
|||
} else if(data.handsOwn || data.handCounts){
|
||||
renderHands(data.handsOwn || [], data.handCounts || {});
|
||||
}
|
||||
// show/hide draw button: only when autoDraw is OFF and it's this player's turn
|
||||
try{
|
||||
const drawBtn = document.getElementById('drawBtn');
|
||||
if(drawBtn){
|
||||
const auto = !!data.autoDraw;
|
||||
const board = data.boardState || null;
|
||||
const myShort = myColor && myColor[0];
|
||||
if(!auto && board && myShort && board.turn === myShort){
|
||||
// enable draw if hand not full
|
||||
const myHandLen = (data.handsOwn && data.handsOwn.length) || 0;
|
||||
drawBtn.style.display = 'inline-block';
|
||||
drawBtn.disabled = myHandLen >= 5;
|
||||
} else {
|
||||
drawBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}catch(_){ }
|
||||
});
|
||||
socket.on('game:over', (data)=>{ log('game:over', data); if(data.boardState){ renderBoardFromState(data.boardState); updateTurnBanner(data.boardState); } });
|
||||
socket.on('game:started', (data)=>{ log('game:started', data); if(data.boardState){ renderBoardFromState(data.boardState); updateTurnBanner(data.boardState); } });
|
||||
|
|
@ -165,6 +186,8 @@
|
|||
showCardDrawnNotification(data);
|
||||
}catch(e){ console.warn('card:drawn handler error', e); }
|
||||
});
|
||||
// when server broadcasts generic room updates, it includes autoDraw flag
|
||||
// ensure draw button visibility is updated inside the room:update handler below
|
||||
// when a card is played by any player, ensure the UI removes it from the owner's hand
|
||||
socket.on('card:played', (data) => {
|
||||
try{
|
||||
|
|
@ -848,6 +871,28 @@
|
|||
row.appendChild(cardEl);
|
||||
});
|
||||
|
||||
// draw button inside the 'Vos cartes' header (visible only when autoDraw is off and it's your turn)
|
||||
try{
|
||||
const drawBtn = document.createElement('button');
|
||||
drawBtn.id = 'drawBtn';
|
||||
drawBtn.type = 'button';
|
||||
drawBtn.className = 'btn-draw';
|
||||
drawBtn.style.display = 'none';
|
||||
drawBtn.textContent = 'Piocher';
|
||||
// attach handler that emits player:draw and temporarily disables the button
|
||||
drawBtn.addEventListener('click', (ev)=>{
|
||||
ev.stopPropagation();
|
||||
if(!socket) return;
|
||||
drawBtn.disabled = true;
|
||||
socket.emit('player:draw', { roomId }, (resp)=>{
|
||||
if(resp && resp.error){ log('player:draw error', resp); alert(resp.error); drawBtn.disabled = false; return; }
|
||||
log('player:draw ok', resp);
|
||||
// server will send room:update which will hide/disable the button as needed
|
||||
});
|
||||
});
|
||||
header.appendChild(drawBtn);
|
||||
}catch(_){ }
|
||||
|
||||
wrap.appendChild(row);
|
||||
handsEl.appendChild(wrap);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,14 @@ a:hover{text-decoration:underline}
|
|||
.btn-ghost{background:transparent;border:1px solid rgba(11,18,32,0.06);padding:8px 10px;border-radius:8px;color:inherit}
|
||||
.btn-cta{background:linear-gradient(90deg,#3b82f6,#60a5fa);box-shadow:0 8px 20px rgba(59,130,246,0.18)}
|
||||
|
||||
/* draw button: compact rectangular CTA used in the cards area */
|
||||
.btn-draw{
|
||||
background:linear-gradient(90deg,#10b981,#059669); /* green gradient */
|
||||
color:white;padding:10px 14px;border-radius:8px;border:none;cursor:pointer;font-weight:800;font-size:14px;
|
||||
min-width:140px;height:44px;display:inline-flex;align-items:center;justify-content:center;box-shadow:0 8px 18px rgba(6,95,70,0.12);
|
||||
}
|
||||
.btn-draw[disabled]{opacity:0.6;cursor:not-allowed}
|
||||
|
||||
/* cancel button shown during pending card selection: white background, red text for clear contrast */
|
||||
.btn-cancel{
|
||||
background: #ffffff;
|
||||
|
|
|
|||
96
server.js
96
server.js
|
|
@ -86,6 +86,21 @@ function sendRoomUpdate(room){
|
|||
});
|
||||
}
|
||||
|
||||
// Helper: at the start of a player's turn, either perform an automatic draw
|
||||
// if room.autoDraw is enabled, or simply broadcast the room state so clients
|
||||
// can update UI. This centralizes the auto-draw toggle behavior.
|
||||
function maybeDrawAtTurnStart(room, playerId){
|
||||
try{
|
||||
if(!room) return;
|
||||
if(room.autoDraw){
|
||||
drawCardForPlayer(room, playerId);
|
||||
} else {
|
||||
// ensure clients get the updated room state even if no draw happens
|
||||
sendRoomUpdate(room);
|
||||
}
|
||||
}catch(e){ console.error('maybeDrawAtTurnStart error', e); sendRoomUpdate(room); }
|
||||
}
|
||||
|
||||
// Compute the set of squares visible to a given player (adjacent to any of their pieces).
|
||||
// Returns a Set of square strings (e.g., 'e4').
|
||||
function visibleSquaresForPlayer(room, playerId){
|
||||
|
|
@ -686,7 +701,7 @@ io.on('connection', (socket) => {
|
|||
const myShort = (myPlayer && myPlayer.color || '')[0];
|
||||
if(myShort === room.boardState.turn){
|
||||
const hasHand = room.hands && room.hands[assignedId] && room.hands[assignedId].length > 0;
|
||||
if(!hasHand) drawCardForPlayer(room, assignedId);
|
||||
if(!hasHand) maybeDrawAtTurnStart(room, assignedId);
|
||||
}
|
||||
}
|
||||
}catch(e){ console.error('post-join draw error', e); }
|
||||
|
|
@ -906,8 +921,8 @@ io.on('connection', (socket) => {
|
|||
const nextColor = board.turn; // 'w' or 'b'
|
||||
const nextPlayer = room.players.find(p => (p.color && p.color[0]) === nextColor);
|
||||
if(nextPlayer){
|
||||
// draw for next player (this will emit card:drawn privately and send personalized room:update)
|
||||
drawCardForPlayer(room, nextPlayer.id);
|
||||
// draw for next player (respect room.autoDraw)
|
||||
maybeDrawAtTurnStart(room, nextPlayer.id);
|
||||
} else {
|
||||
// ensure room state is broadcast
|
||||
sendRoomUpdate(room);
|
||||
|
|
@ -955,7 +970,7 @@ io.on('connection', (socket) => {
|
|||
const firstPlayer = room.players.find(p => (p.color && p.color[0]) === firstColor);
|
||||
if(firstPlayer){
|
||||
const hasHand = room.hands && room.hands[firstPlayer.id] && room.hands[firstPlayer.id].length > 0;
|
||||
if(!hasHand) drawCardForPlayer(room, firstPlayer.id);
|
||||
if(!hasHand) maybeDrawAtTurnStart(room, firstPlayer.id);
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
|
|
@ -1039,6 +1054,75 @@ io.on('connection', (socket) => {
|
|||
cb && cb({ ok: true });
|
||||
});
|
||||
|
||||
// Manual draw by player (consumes the player's turn). Only valid when autoDraw is disabled.
|
||||
socket.on('player:draw', ({ roomId }, cb) => {
|
||||
const room = rooms.get(roomId);
|
||||
if(!room) return cb && cb({ error: 'room not found' });
|
||||
const senderId = socket.data.playerId;
|
||||
if(!senderId) return cb && cb({ error: 'not joined' });
|
||||
const board = room.boardState;
|
||||
if(!board) return cb && cb({ error: 'no board state' });
|
||||
const roomPlayer = room.players.find(p => p.id === senderId);
|
||||
if(!roomPlayer) return cb && cb({ error: 'player not in room' });
|
||||
const playerColorShort = (roomPlayer.color && roomPlayer.color[0]) || null;
|
||||
// must be player's turn
|
||||
if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn' });
|
||||
// manual draw only allowed when autoDraw is disabled
|
||||
if(room.autoDraw) return cb && cb({ error: 'auto_draw_enabled' });
|
||||
// do not allow drawing if player already played a card this turn
|
||||
room._cardPlayedThisTurn = room._cardPlayedThisTurn || {};
|
||||
if(room._cardPlayedThisTurn[senderId]) return cb && cb({ error: 'card_already_played_this_turn' });
|
||||
|
||||
try{
|
||||
// perform the draw (this will emit card:drawn privately and send personalized room:update)
|
||||
const drawn = drawCardForPlayer(room, senderId);
|
||||
if(!drawn){
|
||||
// nothing drawn (hand full or deck empty)
|
||||
return cb && cb({ error: 'no_card_drawn' });
|
||||
}
|
||||
// drawing consumes the player's turn: flip turn and decrement per-turn effects
|
||||
// advance board version
|
||||
board.version = (board.version || 0) + 1;
|
||||
// flip turn
|
||||
board.turn = (board.turn === 'w') ? 'b' : 'w';
|
||||
// decrement remainingTurns for time-limited effects that belong to the player who just finished their turn
|
||||
try{
|
||||
room.activeCardEffects = room.activeCardEffects || [];
|
||||
for(let i = room.activeCardEffects.length - 1; i >= 0; i--){
|
||||
const e = room.activeCardEffects[i];
|
||||
if(typeof e.remainingTurns === 'number'){
|
||||
let shouldDecrement = false;
|
||||
if(e.decrementOn === 'opponent'){
|
||||
shouldDecrement = (e.playerId !== senderId);
|
||||
} else if(e.decrementOn === 'owner'){
|
||||
shouldDecrement = (e.playerId === senderId);
|
||||
} else {
|
||||
shouldDecrement = (e.playerId === senderId);
|
||||
}
|
||||
if(shouldDecrement){
|
||||
e.remainingTurns = e.remainingTurns - 1;
|
||||
try{ io.to(room.id).emit('card:effect:updated', { roomId: room.id, effect: e }); }catch(_){ }
|
||||
if(e.remainingTurns <= 0){ room.activeCardEffects.splice(i,1); try{ io.to(room.id).emit('card:effect:removed', { roomId: room.id, effectId: e.id, type: e.type, playerId: e.playerId }); }catch(_){ } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(e){ console.error('decrement-after-draw error', e); }
|
||||
|
||||
// reset per-turn card-play flags
|
||||
try{ room._cardPlayedThisTurn = {}; }catch(_){ }
|
||||
|
||||
// at this point it's the next player's turn; perform start-of-turn draw if autoDraw enabled
|
||||
const nextColor = board.turn;
|
||||
const nextPlayer = (room.players || []).find(p => (p.color && p.color[0]) === nextColor);
|
||||
if(nextPlayer){ try{ maybeDrawAtTurnStart(room, nextPlayer.id); }catch(_){ sendRoomUpdate(room); } }
|
||||
else { sendRoomUpdate(room); }
|
||||
|
||||
// notify room that the player drew and ended their turn
|
||||
try{ io.to(room.id).emit('player:drew', { roomId: room.id, playerId: senderId, card: drawn }); }catch(_){ }
|
||||
return cb && cb({ ok: true, card: drawn });
|
||||
}catch(err){ console.error('player:draw error', err); return cb && cb({ error: 'server_error' }); }
|
||||
});
|
||||
|
||||
// Simple cards API via sockets: list/play
|
||||
socket.on('card:list', ({ roomId }, cb) => {
|
||||
const room = rooms.get(roomId);
|
||||
|
|
@ -1440,7 +1524,7 @@ io.on('connection', (socket) => {
|
|||
// draw for the next player at the start of their turn
|
||||
const nextColor = board.turn;
|
||||
const nextPlayer = (room.players || []).find(p => (p.color && p.color[0]) === nextColor);
|
||||
if(nextPlayer){ try{ drawCardForPlayer(room, nextPlayer.id); }catch(_){ } }
|
||||
if(nextPlayer){ try{ maybeDrawAtTurnStart(room, nextPlayer.id); }catch(_){ } }
|
||||
}
|
||||
}catch(_){ }
|
||||
}catch(e){ console.error('kamikaz effect error', e); }
|
||||
|
|
@ -1713,7 +1797,7 @@ io.on('connection', (socket) => {
|
|||
// draw for the next player at the start of their turn
|
||||
const nextColor = board.turn;
|
||||
const nextPlayer = (room.players || []).find(p => (p.color && p.color[0]) === nextColor);
|
||||
if(nextPlayer){ try{ drawCardForPlayer(room, nextPlayer.id); }catch(_){ } }
|
||||
if(nextPlayer){ try{ maybeDrawAtTurnStart(room, nextPlayer.id); }catch(_){ } }
|
||||
}
|
||||
}catch(_){ }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue