#pragma once #include "Lobby.hpp" #include #include #include #include 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 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> map_; std::vector tanks_; // сначала люди, затем (в coop) появляющиеся AI std::vector bullets_; std::optional base_; std::vector> 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 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; };