Implement DAS/ARR for smooth horizontal piece movement
This commit is contained in:
+99
-3
@@ -55,6 +55,11 @@ app.use(express.static(path.join(__dirname, '../public')));
|
||||
* @property {number} dropCounter - Frame counter for auto-drop
|
||||
* @property {number} dropInterval - Milliseconds between auto-drops
|
||||
* @property {object[]} garbageReceived - History of garbage received
|
||||
* @property {number} zoneMeter - Zone meter (0-100), increases by line clears
|
||||
* @property {boolean} zoneActive - Whether Zone is currently active
|
||||
* @property {number} zoneStartTime - Timestamp when Zone started
|
||||
* @property {number} zoneLineCount - Lines cleared during current Zone
|
||||
* @property {number} zoneTotalDuration - Total Zone duration in ms (20000ms = 20 seconds)
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -138,7 +143,12 @@ io.on('connection', (socket) => {
|
||||
ready: false,
|
||||
dropCounter: 0,
|
||||
dropInterval: 1000,
|
||||
garbageReceived: []
|
||||
garbageReceived: [],
|
||||
zoneMeter: 0,
|
||||
zoneActive: false,
|
||||
zoneStartTime: null,
|
||||
zoneLineCount: 0,
|
||||
zoneTotalDuration: 20000
|
||||
};
|
||||
|
||||
lobby.players.set(socket.id, player);
|
||||
@@ -328,6 +338,24 @@ io.on('connection', (socket) => {
|
||||
broadcastState();
|
||||
});
|
||||
|
||||
socket.on('zone-activate', ({ playerId }) => {
|
||||
if (!lobby.gameStarted) return;
|
||||
|
||||
const player = lobby.players.get(playerId);
|
||||
if (!player || player.eliminated || player.zoneActive) return;
|
||||
|
||||
// Only activate if Zone meter is full
|
||||
if (player.zoneMeter < 100) return;
|
||||
|
||||
player.zoneActive = true;
|
||||
player.zoneStartTime = Date.now();
|
||||
player.zoneLineCount = 0;
|
||||
player.zoneMeter = 0;
|
||||
|
||||
console.log(`[ZONE] ${player.name} activated Zone!`);
|
||||
broadcastState();
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
const player = lobby.players.get(socket.id);
|
||||
if (player) {
|
||||
@@ -464,7 +492,22 @@ function lockPiece(player) {
|
||||
|
||||
function clearRows(player) {
|
||||
const result = clearRowsFromBoard(player.board);
|
||||
const rowsCleared = result.rowsCleared;
|
||||
let rowsCleared = result.rowsCleared;
|
||||
|
||||
// During Zone, lines cascade to bottom instead of disappearing - reapply them
|
||||
if (player.zoneActive && rowsCleared > 0) {
|
||||
player.zoneLineCount += rowsCleared;
|
||||
// Keep the lines on board for cascade effect (they stay, don't disappear)
|
||||
player.board = result.board;
|
||||
// Add cleared lines back to bottom for visual cascade
|
||||
for (let i = 0; i < rowsCleared; i++) {
|
||||
player.board.push(Array(BOARD_WIDTH).fill('#999999')); // Cleared line color
|
||||
}
|
||||
// Shift other lines up to make room
|
||||
player.board = player.board.slice(-BOARD_HEIGHT);
|
||||
return rowsCleared; // Still count for scoring
|
||||
}
|
||||
|
||||
player.board = result.board;
|
||||
|
||||
if (rowsCleared > 0) {
|
||||
@@ -473,7 +516,12 @@ function clearRows(player) {
|
||||
player.score += points[rowsCleared] * player.level;
|
||||
player.level = Math.floor(player.lines / 10) + 1;
|
||||
player.dropInterval = Math.max(100, 1000 - (player.level - 1) * 100);
|
||||
|
||||
// Fill Zone meter: different amounts based on lines cleared
|
||||
const zoneMeterIncrease = [0, 15, 25, 35, 50][Math.min(4, rowsCleared)] || 0;
|
||||
player.zoneMeter = Math.min(100, player.zoneMeter + zoneMeterIncrease);
|
||||
}
|
||||
|
||||
return rowsCleared;
|
||||
}
|
||||
|
||||
@@ -498,6 +546,27 @@ function sendGarbage(sender, rowsCleared) {
|
||||
}
|
||||
}
|
||||
|
||||
function sendZoneAttack(sender, zoneLineCount) {
|
||||
// Zone attack sends bonus garbage based on lines cleared during Zone
|
||||
// Formula: lines * 1.5 (rounded) = garbage rows
|
||||
// Example: 4 Zone lines = 6 garbage rows, 8 Zone lines = 12 garbage rows
|
||||
const totalGarbageLines = Math.round(zoneLineCount * 1.5);
|
||||
|
||||
const garbageLog = [];
|
||||
for (let i = 0; i < totalGarbageLines; i++) {
|
||||
const opponents = Array.from(lobby.players.values()).filter(p => p.id !== sender.id && !p.eliminated);
|
||||
if (opponents.length === 0) break;
|
||||
|
||||
const target = opponents[Math.floor(Math.random() * opponents.length)];
|
||||
garbageLog.push(target.name);
|
||||
addGarbageToPlayer(target, `${sender.name} (ZONE)`);
|
||||
}
|
||||
|
||||
if (garbageLog.length > 0) {
|
||||
console.log(`[ZONE ATTACK] ${sender.name} sent ${totalGarbageLines} garbage rows from ${zoneLineCount} Zone lines -> targets: ${garbageLog.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
function addGarbageToPlayer(player, senderName) {
|
||||
// Use the pure function to add garbage row
|
||||
const result = addGarbageRow(player.board, player.currentPiece);
|
||||
@@ -528,6 +597,10 @@ function startGame() {
|
||||
player.garbageReceived = [];
|
||||
player.currentPiece = null;
|
||||
player.nextPiece = null;
|
||||
player.zoneMeter = 0;
|
||||
player.zoneActive = false;
|
||||
player.zoneStartTime = null;
|
||||
player.zoneLineCount = 0;
|
||||
lobby.playerSequenceIndex.set(player.id, 0);
|
||||
}
|
||||
|
||||
@@ -556,6 +629,25 @@ function gameTick() {
|
||||
for (const player of lobby.players.values()) {
|
||||
if (player.eliminated || !player.currentPiece) continue;
|
||||
|
||||
// Handle Zone time management
|
||||
if (player.zoneActive) {
|
||||
const elapsedTime = Date.now() - player.zoneStartTime;
|
||||
if (elapsedTime >= player.zoneTotalDuration) {
|
||||
// Zone ended - convert zone lines to attack
|
||||
player.zoneActive = false;
|
||||
console.log(`[ZONE END] ${player.name} cleared ${player.zoneLineCount} lines during Zone`);
|
||||
if (player.zoneLineCount > 0) {
|
||||
sendZoneAttack(player, player.zoneLineCount);
|
||||
}
|
||||
player.zoneLineCount = 0;
|
||||
} else {
|
||||
// During Zone - gravity doesn't apply, let pieces sort themselves out
|
||||
// Players still control manually, pieces still lock after a delay if touching stack
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal gravity (not in Zone)
|
||||
player.dropCounter += 50;
|
||||
if (player.dropCounter >= player.dropInterval) {
|
||||
player.dropCounter = 0;
|
||||
@@ -588,7 +680,11 @@ function getStates() {
|
||||
level: p.level,
|
||||
eliminated: p.eliminated,
|
||||
sequenceIndex: lobby.playerSequenceIndex.get(p.id) || 0,
|
||||
garbageReceived: p.garbageReceived ? JSON.parse(JSON.stringify(p.garbageReceived)) : []
|
||||
garbageReceived: p.garbageReceived ? JSON.parse(JSON.stringify(p.garbageReceived)) : [],
|
||||
zoneMeter: p.zoneMeter,
|
||||
zoneActive: p.zoneActive,
|
||||
zoneTimeRemaining: p.zoneActive ? Math.max(0, p.zoneTotalDuration - (Date.now() - p.zoneStartTime)) : 0,
|
||||
zoneLineCount: p.zoneLineCount
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user