Implement DAS/ARR for smooth horizontal piece movement

This commit is contained in:
2026-03-24 17:59:55 -07:00
parent 36965dc887
commit 3a307dbc80
11 changed files with 338 additions and 26 deletions
+99 -3
View File
@@ -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
}));
}
+2
View File
@@ -45,6 +45,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1306,6 +1307,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",