Звук (Web Audio API) + несколько раскладок coop-карты
Клиент: audio.js синтезирует эффекты без внешних файлов (выстрел, взрыв, разрушение кирпича, победа/поражение) — вписывается в "чистый HTML/CSS/JS" стек. AudioContext разблокируется по клику "готов" (браузеры блокируют автовоспроизведение без жеста). Сервер: 3 варианта раскладки coop-карты (случайный выбор при старте матча) вместо одной фиксированной. Fix: сервер разрушал кирпичи в своей копии карты, но никогда не сообщал об этом клиенту — стена оставалась видимой навсегда, хотя сквозь неё уже можно было стрелять. Теперь game.state включает destroyed_tiles, клиент убирает соответствующий спрайт стены.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { ClientMessage, ServerMessage } from './protocol.js';
|
||||
import { returnToLobbyContainer, leaveRoomAndReturn } from './lobby.js';
|
||||
import { playShot, playWallHit, playExplosion, playWin, playLose } from './audio.js';
|
||||
|
||||
const TILE_PX = 32;
|
||||
// Quintus транслирует и вращает контекст сам (Sprite.render -> matrix.setContextTransform)
|
||||
@@ -13,6 +14,7 @@ let myPlayerId = null;
|
||||
let stage = null;
|
||||
let tankSprites = new Map();
|
||||
let bulletSprites = new Map();
|
||||
let wallSprites = new Map(); // "x,y" (тайл) -> спрайт, для синка разрушенных стен
|
||||
let baseSprite = null;
|
||||
let lastSentInput = null;
|
||||
let seq = 0;
|
||||
@@ -133,13 +135,16 @@ function buildMap(map) {
|
||||
if (tile === 0) {
|
||||
continue;
|
||||
}
|
||||
stage.insert(
|
||||
new Q.Wall({
|
||||
x: x * TILE_PX + TILE_PX / 2,
|
||||
y: y * TILE_PX + TILE_PX / 2,
|
||||
tile,
|
||||
})
|
||||
);
|
||||
const sprite = new Q.Wall({
|
||||
x: x * TILE_PX + TILE_PX / 2,
|
||||
y: y * TILE_PX + TILE_PX / 2,
|
||||
tile,
|
||||
});
|
||||
stage.insert(sprite);
|
||||
if (tile === 1) {
|
||||
// только кирпич может быть разрушен — только его отслеживаем
|
||||
wallSprites.set(`${x},${y}`, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,6 +205,8 @@ function onState(payload) {
|
||||
});
|
||||
stage.insert(sprite);
|
||||
tankSprites.set(t.id, sprite);
|
||||
} else if (!sprite.p.hidden && !t.alive) {
|
||||
playExplosion();
|
||||
}
|
||||
sprite.p.x = t.x * TILE_PX;
|
||||
sprite.p.y = t.y * TILE_PX;
|
||||
@@ -221,6 +228,7 @@ function onState(payload) {
|
||||
sprite = new Q.Bullet({ x: b.x * TILE_PX, y: b.y * TILE_PX });
|
||||
stage.insert(sprite);
|
||||
bulletSprites.set(b.id, sprite);
|
||||
playShot();
|
||||
}
|
||||
sprite.p.x = b.x * TILE_PX;
|
||||
sprite.p.y = b.y * TILE_PX;
|
||||
@@ -232,6 +240,20 @@ function onState(payload) {
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.destroyed_tiles) {
|
||||
for (const [tx, ty] of payload.destroyed_tiles) {
|
||||
const key = `${tx},${ty}`;
|
||||
const sprite = wallSprites.get(key);
|
||||
if (sprite) {
|
||||
sprite.destroy();
|
||||
wallSprites.delete(key);
|
||||
}
|
||||
}
|
||||
if (payload.destroyed_tiles.length > 0) {
|
||||
playWallHit();
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.base) {
|
||||
if (!baseSprite) {
|
||||
baseSprite = new Q.Base({
|
||||
@@ -241,6 +263,9 @@ function onState(payload) {
|
||||
});
|
||||
stage.insert(baseSprite);
|
||||
} else {
|
||||
if (baseSprite.p.alive && !payload.base.alive) {
|
||||
playExplosion();
|
||||
}
|
||||
baseSprite.p.alive = payload.base.alive;
|
||||
}
|
||||
}
|
||||
@@ -248,6 +273,11 @@ function onState(payload) {
|
||||
|
||||
function onOver(payload) {
|
||||
polling = false;
|
||||
if (payload.result === 'win') {
|
||||
playWin();
|
||||
} else {
|
||||
playLose();
|
||||
}
|
||||
const titles = { win: 'ПОБЕДА', lose: 'ПОРАЖЕНИЕ', draw: 'НИЧЬЯ' };
|
||||
const title = titles[payload.result] || payload.result.toUpperCase();
|
||||
const overlay = document.getElementById('battle-result-overlay');
|
||||
@@ -309,6 +339,7 @@ export function startBattle(networkInstance, payload, ownPlayerId) {
|
||||
Q.stageScene('battle', 0);
|
||||
tankSprites = new Map();
|
||||
bulletSprites = new Map();
|
||||
wallSprites = new Map();
|
||||
baseSprite = null;
|
||||
|
||||
buildMap(payload.map);
|
||||
|
||||
Reference in New Issue
Block a user