Files
cactoz.su/gameserver/src/Game.hpp
T
cacto 84da9b49e2 Coop с AI/базой (соло-старт), реванш, чистые URL, доработки клиента
Сервер (Game.hpp/cpp):
- coop-режим: AI-противники (волна 8, до 3 одновременно), простое
  преследование базы с рандомизацией, база с укрытием из кирпича.
- Комната стартует соло (1 игрок), второй может подключиться позже.
- Fix: хитбокс пули по базе брал радиус танка вместо пули.
- Fix: AI при столкновении со стеной теперь сразу перерешает направление,
  а не долбит в одну точку до 3с — без этого база падала за секунды.

Клиент:
- Танк: гусеницы, башня, заметный ствол (было — незаметный нубик).
- Рендер базы (укрытие/флаг), AI-танки — отдельным оттенком зелёного.
- Экран победы/поражения — крупный glitch-текст на весь оверлей вместо
  строки текста; кнопки "играть ещё" (без reload) и "в лобби".
- Блок кнопок управления (D-pad + огонь) справа от игрового поля.
- Лобби: coop стартует по кнопке "готов" в одиночку.

nginx: try_files с fallback на .html — чистые URL (/projects, /game)
без расширения, старые /projects.html и т.п. продолжают работать.
2026-08-06 11:39:09 +05:00

103 lines
3.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "Lobby.hpp"
#include <functional>
#include <optional>
#include <string>
#include <vector>
struct us_timer_t;
// Authoritative-игра для одной комнаты: pvp (этап 4) и coop с AI (этап 5).
class Game {
public:
static constexpr int kTickMs = 50; // 20 Гц
static constexpr int kMapWidth = 15;
static constexpr int kMapHeight = 15;
static constexpr int kStartLives = 3;
static constexpr int kMaxEnemiesTotal = 8; // размер волны в coop
static constexpr int kMaxConcurrentEnemies = 3;
static constexpr int kEnemySpawnCooldownTicks = 60; // 3с
enum Tile { kEmpty = 0, kBrick = 1, kSteel = 2 };
enum class Dir { Up, Right, Down, Left };
struct Tank {
std::string id; // player_id для людей, "ai-N" для AI
WS *ws = nullptr; // nullptr для AI
bool is_ai = false;
double x, y;
Dir dir = Dir::Up;
int lives = kStartLives;
int fire_cooldown = 0;
bool up = false, down = false, left = false, right = false, fire = false;
bool alive = true;
// состояние простого AI (используется только если is_ai)
Dir ai_dir = Dir::Down;
int ai_redecide_ticks = 0;
};
struct Bullet {
int id;
std::string owner_id;
bool owner_is_ai;
double x, y;
Dir dir;
};
struct Base {
double x, y;
bool alive = true;
};
// room.players.size() — 2 для pvp, 1 или 2 для coop.
Game(Room &room, std::function<void()> onFinished);
~Game();
void start();
void handleInput(const std::string &player_id, const json &payload);
// pvp: технический проигрыш отключившегося, второй побеждает.
// coop: игрок выбывает; если людей не осталось — поражение.
void handleDisconnect(WS *ws);
json buildStartPayload(const std::string &mode) const;
private:
std::string mode_;
std::vector<std::vector<int>> map_;
std::vector<Tank> tanks_; // сначала люди, затем (в coop) появляющиеся AI
std::vector<Bullet> bullets_;
std::optional<Base> base_;
std::vector<std::pair<double, double>> enemy_spawn_points_;
int enemies_spawned_total_ = 0;
int enemy_spawn_cooldown_ = 0;
int next_bullet_id_ = 1;
int next_enemy_id_ = 1;
int tick_count_ = 0;
bool finished_ = false;
us_timer_t *timer_ = nullptr;
std::function<void()> onFinished_;
void buildPvpMap();
void buildCoopMap();
void tick();
void applyInput(Tank &tank);
void updateAi(Tank &tank);
void spawnEnemiesIfNeeded();
void updateBullets();
bool tileSolid(int tx, int ty) const;
bool rectHitsSolid(double x, double y) const;
bool rectHitsTank(double x, double y, const Tank &self) const;
bool rectHitsBase(double x, double y, double half) const;
void fireBullet(Tank &tank);
void endGamePvp(int winnerIndex); // -1 = ничья
void endGameCoop(bool win, const std::string &reason);
void checkCoopEndConditions();
void broadcastState();
void sendTo(WS *ws, const std::string &type, json payload);
static json dirToJson(Dir d);
json serializeTank(const Tank &t) const;
json serializeBullet(const Bullet &b) const;
};