Add hold piece feature

- Added holdPiece and canHold state to TetrisGame class
- Implemented hold() method to swap current piece with held piece
- Added player-hold socket event on server
- Added HOLD preview canvas showing held piece (grayed when unavailable)
- Added C key keyboard shortcut and touch button for hold
- Fixed canHold reset on piece spawn for proper swap functionality

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 08:50:52 -07:00
parent cde1643606
commit 4a49c76cdc
8 changed files with 410 additions and 8 deletions
+37
View File
@@ -109,9 +109,46 @@ function setupKeyboardControls() {
network.sendHardDrop();
e.preventDefault();
break;
case 'c':
case 'C':
network.sendHold();
e.preventDefault();
break;
}
}
});
// Touch controls for mobile
const btnLeft = document.getElementById('btn-left');
const btnRight = document.getElementById('btn-right');
const btnDown = document.getElementById('btn-down');
const btnRotate = document.getElementById('btn-rotate');
const btnDrop = document.getElementById('btn-drop');
const btnHold = document.getElementById('btn-hold');
const handleTouch = (e, action) => {
e.preventDefault();
e.stopPropagation();
if (ui.screens.game.classList.contains('active')) {
action();
}
};
// Use pointerdown for better touch response
btnLeft.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendMove('left')));
btnRight.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendMove('right')));
btnDown.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendDrop()));
btnRotate.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendRotate()));
btnDrop.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendHardDrop()));
btnHold.addEventListener('pointerdown', (e) => handleTouch(e, () => network.sendHold()));
// Prevent double-tap zoom
btnLeft.addEventListener('touchstart', (e) => e.preventDefault());
btnRight.addEventListener('touchstart', (e) => e.preventDefault());
btnDown.addEventListener('touchstart', (e) => e.preventDefault());
btnRotate.addEventListener('touchstart', (e) => e.preventDefault());
btnDrop.addEventListener('touchstart', (e) => e.preventDefault());
btnHold.addEventListener('touchstart', (e) => e.preventDefault());
}
function updateBattleGridLayout(playerCount) {