Звук (Web Audio API) + несколько раскладок coop-карты
Клиент: audio.js синтезирует эффекты без внешних файлов (выстрел, взрыв, разрушение кирпича, победа/поражение) — вписывается в "чистый HTML/CSS/JS" стек. AudioContext разблокируется по клику "готов" (браузеры блокируют автовоспроизведение без жеста). Сервер: 3 варианта раскладки coop-карты (случайный выбор при старте матча) вместо одной фиксированной. Fix: сервер разрушал кирпичи в своей копии карты, но никогда не сообщал об этом клиенту — стена оставалась видимой навсегда, хотя сквозь неё уже можно было стрелять. Теперь game.state включает destroyed_tiles, клиент убирает соответствующий спрайт стены.
This commit is contained in:
+33
-6
@@ -88,7 +88,7 @@ void Game::buildCoopMap() {
|
||||
}
|
||||
}
|
||||
|
||||
// немного укрытий по полю
|
||||
// немного укрытий по полю — несколько вариантов раскладки для разнообразия
|
||||
auto block = [this](int x, int y, int w, int h, int tile) {
|
||||
for (int dy = 0; dy < h; dy++) {
|
||||
for (int dx = 0; dx < w; dx++) {
|
||||
@@ -96,11 +96,29 @@ void Game::buildCoopMap() {
|
||||
}
|
||||
}
|
||||
};
|
||||
block(2, 5, 2, 2, kBrick);
|
||||
block(11, 5, 2, 2, kBrick);
|
||||
block(6, 4, 3, 1, kSteel);
|
||||
block(4, 8, 2, 2, kBrick);
|
||||
block(9, 8, 2, 2, kBrick);
|
||||
switch (std::rand() % 3) {
|
||||
case 0:
|
||||
block(2, 5, 2, 2, kBrick);
|
||||
block(11, 5, 2, 2, kBrick);
|
||||
block(6, 4, 3, 1, kSteel);
|
||||
block(4, 8, 2, 2, kBrick);
|
||||
block(9, 8, 2, 2, kBrick);
|
||||
break;
|
||||
case 1:
|
||||
block(1, 4, 3, 1, kSteel);
|
||||
block(11, 4, 3, 1, kSteel);
|
||||
block(6, 6, 3, 3, kBrick);
|
||||
block(2, 9, 2, 2, kBrick);
|
||||
block(11, 9, 2, 2, kBrick);
|
||||
break;
|
||||
default:
|
||||
block(4, 3, 1, 4, kBrick);
|
||||
block(10, 3, 1, 4, kBrick);
|
||||
block(6, 5, 3, 2, kSteel);
|
||||
block(3, 9, 3, 1, kBrick);
|
||||
block(9, 9, 3, 1, kBrick);
|
||||
break;
|
||||
}
|
||||
|
||||
// база внизу по центру, укрыта кирпичом с трёх сторон
|
||||
int bx = kMapWidth / 2;
|
||||
@@ -320,6 +338,7 @@ void Game::fireBullet(Tank &tank) {
|
||||
}
|
||||
|
||||
void Game::updateBullets() {
|
||||
destroyed_tiles_.clear();
|
||||
double step = kBulletSpeed * kDt;
|
||||
|
||||
for (auto &b : bullets_) {
|
||||
@@ -361,6 +380,7 @@ void Game::updateBullets() {
|
||||
}
|
||||
if (map_[ty][tx] == kBrick) {
|
||||
map_[ty][tx] = kEmpty;
|
||||
destroyed_tiles_.push_back({tx, ty});
|
||||
dead[i] = true;
|
||||
continue;
|
||||
}
|
||||
@@ -518,6 +538,13 @@ void Game::broadcastState() {
|
||||
if (base_) {
|
||||
payload["base"] = {{"x", base_->x}, {"y", base_->y}, {"alive", base_->alive}};
|
||||
}
|
||||
if (!destroyed_tiles_.empty()) {
|
||||
json destroyed = json::array();
|
||||
for (const auto &[tx, ty] : destroyed_tiles_) {
|
||||
destroyed.push_back({tx, ty});
|
||||
}
|
||||
payload["destroyed_tiles"] = destroyed;
|
||||
}
|
||||
for (const auto &t : tanks_) {
|
||||
if (!t.is_ai) {
|
||||
sendTo(t.ws, "game.state", payload);
|
||||
|
||||
@@ -69,6 +69,7 @@ private:
|
||||
std::vector<Bullet> bullets_;
|
||||
std::optional<Base> base_;
|
||||
std::vector<std::pair<double, double>> enemy_spawn_points_;
|
||||
std::vector<std::pair<int, int>> destroyed_tiles_; // за последний тик — для звука/синка стен на клиенте
|
||||
int enemies_spawned_total_ = 0;
|
||||
int enemy_spawn_cooldown_ = 0;
|
||||
int next_bullet_id_ = 1;
|
||||
|
||||
Reference in New Issue
Block a user