game_engine.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. #pragma once
  2. #include "../models/cursor_manager.h"
  3. #include "../models/cursor_mode.h"
  4. #include "../models/hover_tracker.h"
  5. #include "../models/selected_units_model.h"
  6. #include "../utils/engine_view_helpers.h"
  7. #include "../utils/movement_utils.h"
  8. #include "../utils/selection_utils.h"
  9. #include "ambient_state_manager.h"
  10. #include "camera_controller.h"
  11. #include "game/audio/audio_event_handler.h"
  12. #include "game/core/event_manager.h"
  13. #include "game/map/mission_definition.h"
  14. #include "game/systems/game_state_serializer.h"
  15. #include "input_command_handler.h"
  16. #include "minimap_manager.h"
  17. #include "renderer_bootstrap.h"
  18. #include <QElapsedTimer>
  19. #include <QJsonObject>
  20. #include <QList>
  21. #include <QMatrix4x4>
  22. #include <QObject>
  23. #include <QPointF>
  24. #include <QStringList>
  25. #include <QVariant>
  26. #include <QVector3D>
  27. #include <algorithm>
  28. #include <cstdint>
  29. #include <memory>
  30. #include <optional>
  31. #include <vector>
  32. class ProductionManager;
  33. class CampaignManager;
  34. class SelectionQueryService;
  35. namespace Engine::Core {
  36. class World;
  37. using EntityID = unsigned int;
  38. struct MovementComponent;
  39. struct TransformComponent;
  40. struct RenderableComponent;
  41. } // namespace Engine::Core
  42. namespace Render::GL {
  43. class Renderer;
  44. class Camera;
  45. class ResourceManager;
  46. class GroundRenderer;
  47. class TerrainRenderer;
  48. class BiomeRenderer;
  49. class RiverRenderer;
  50. class RoadRenderer;
  51. class RiverbankRenderer;
  52. class BridgeRenderer;
  53. class FogRenderer;
  54. class StoneRenderer;
  55. class PlantRenderer;
  56. class PineRenderer;
  57. class OliveRenderer;
  58. class FireCampRenderer;
  59. class RainRenderer;
  60. struct IRenderPass;
  61. } // namespace Render::GL
  62. namespace Game {
  63. namespace Map::Minimap {
  64. class UnitLayer;
  65. }
  66. namespace Systems {
  67. class SelectionSystem;
  68. class SelectionController;
  69. class ArrowSystem;
  70. class PickingService;
  71. class VictoryService;
  72. class CameraService;
  73. class SaveLoadService;
  74. class RainManager;
  75. } // namespace Systems
  76. namespace Map {
  77. class MapCatalog;
  78. struct MapDefinition;
  79. } // namespace Map
  80. } // namespace Game
  81. namespace App {
  82. namespace Controllers {
  83. class CommandController;
  84. }
  85. namespace Models {
  86. class AudioSystemProxy;
  87. }
  88. } // namespace App
  89. class QQuickWindow;
  90. class LoadingProgressTracker;
  91. struct EntityCache {
  92. int player_troop_count = 0;
  93. bool player_barracks_alive = false;
  94. bool enemy_barracks_alive = false;
  95. int enemy_barracks_count = 0;
  96. void reset() {
  97. player_troop_count = 0;
  98. player_barracks_alive = false;
  99. enemy_barracks_alive = false;
  100. enemy_barracks_count = 0;
  101. }
  102. };
  103. class GameEngine : public QObject {
  104. Q_OBJECT
  105. public:
  106. explicit GameEngine(QObject *parent = nullptr);
  107. ~GameEngine() override;
  108. void cleanup_opengl_resources();
  109. Q_INVOKABLE void load_campaigns();
  110. Q_PROPERTY(QAbstractItemModel *selected_units_model READ selected_units_model
  111. NOTIFY selected_units_changed)
  112. Q_PROPERTY(bool paused READ paused WRITE set_paused)
  113. Q_PROPERTY(float time_scale READ time_scale WRITE set_game_speed)
  114. Q_PROPERTY(
  115. QString victory_state READ victory_state NOTIFY victory_state_changed)
  116. Q_PROPERTY(QString cursor_mode READ cursor_mode WRITE set_cursor_mode NOTIFY
  117. cursor_mode_changed)
  118. Q_PROPERTY(
  119. qreal global_cursor_x READ global_cursor_x NOTIFY global_cursor_changed)
  120. Q_PROPERTY(
  121. qreal global_cursor_y READ global_cursor_y NOTIFY global_cursor_changed)
  122. Q_PROPERTY(bool has_units_selected READ has_units_selected NOTIFY
  123. selected_units_changed)
  124. Q_PROPERTY(
  125. int player_troop_count READ player_troop_count NOTIFY troop_count_changed)
  126. Q_PROPERTY(int max_troops_per_player READ max_troops_per_player NOTIFY
  127. troop_count_changed)
  128. Q_PROPERTY(QVariantList available_maps READ available_maps NOTIFY
  129. available_maps_changed)
  130. Q_PROPERTY(bool maps_loading READ maps_loading NOTIFY maps_loading_changed)
  131. Q_PROPERTY(QVariantList available_nations READ available_nations CONSTANT)
  132. Q_PROPERTY(QVariantList available_campaigns READ available_campaigns NOTIFY
  133. available_campaigns_changed)
  134. Q_PROPERTY(int enemy_troops_defeated READ enemy_troops_defeated NOTIFY
  135. enemy_troops_defeated_changed)
  136. Q_PROPERTY(
  137. QVariantList owner_info READ get_owner_info NOTIFY owner_info_changed)
  138. Q_PROPERTY(int selected_player_id READ selected_player_id WRITE
  139. set_selected_player_id NOTIFY selected_player_id_changed)
  140. Q_PROPERTY(QString last_error READ last_error NOTIFY last_error_changed)
  141. Q_PROPERTY(QObject *audio_system READ audio_system CONSTANT)
  142. Q_PROPERTY(
  143. QImage minimap_image READ minimap_image NOTIFY minimap_image_changed)
  144. Q_PROPERTY(bool is_spectator_mode READ is_spectator_mode NOTIFY
  145. spectator_mode_changed)
  146. Q_PROPERTY(bool is_loading READ is_loading NOTIFY is_loading_changed)
  147. Q_PROPERTY(float loading_progress READ loading_progress NOTIFY
  148. loading_progress_changed)
  149. Q_PROPERTY(QString loading_stage_text READ loading_stage_text NOTIFY
  150. loading_stage_changed)
  151. Q_PROPERTY(bool is_placing_formation READ is_placing_formation NOTIFY
  152. placing_formation_changed)
  153. Q_PROPERTY(bool is_placing_construction READ is_placing_construction NOTIFY
  154. placing_construction_changed)
  155. Q_PROPERTY(bool is_campaign_mission READ is_campaign_mission NOTIFY
  156. campaign_mission_changed)
  157. Q_INVOKABLE void on_map_clicked(qreal sx, qreal sy);
  158. Q_INVOKABLE void on_right_click(qreal sx, qreal sy);
  159. Q_INVOKABLE void on_right_double_click(qreal sx, qreal sy);
  160. Q_INVOKABLE void on_click_select(qreal sx, qreal sy, bool additive = false);
  161. Q_INVOKABLE void on_area_selected(qreal x1, qreal y1, qreal x2, qreal y2,
  162. bool additive = false);
  163. Q_INVOKABLE void select_all_troops();
  164. Q_INVOKABLE void select_unit_by_id(int unitId);
  165. Q_INVOKABLE void set_hover_at_screen(qreal sx, qreal sy);
  166. Q_INVOKABLE void on_attack_click(qreal sx, qreal sy);
  167. Q_INVOKABLE void on_stop_command();
  168. Q_INVOKABLE void on_hold_command();
  169. Q_INVOKABLE void on_guard_command();
  170. Q_INVOKABLE void on_formation_command();
  171. Q_INVOKABLE void on_run_command();
  172. Q_INVOKABLE void on_heal_command();
  173. Q_INVOKABLE void on_build_command();
  174. Q_INVOKABLE void on_guard_click(qreal sx, qreal sy);
  175. Q_INVOKABLE [[nodiscard]] bool any_selected_in_hold_mode() const;
  176. Q_INVOKABLE [[nodiscard]] bool any_selected_in_guard_mode() const;
  177. Q_INVOKABLE [[nodiscard]] bool any_selected_in_formation_mode() const;
  178. Q_INVOKABLE [[nodiscard]] bool any_selected_in_run_mode() const;
  179. Q_INVOKABLE [[nodiscard]] bool is_placing_formation() const;
  180. Q_INVOKABLE [[nodiscard]] bool is_placing_construction() const;
  181. Q_INVOKABLE void on_formation_mouse_move(qreal sx, qreal sy);
  182. Q_INVOKABLE void on_formation_scroll(float delta);
  183. Q_INVOKABLE void on_formation_confirm();
  184. Q_INVOKABLE void on_formation_cancel();
  185. Q_INVOKABLE void on_construction_mouse_move(qreal sx, qreal sy);
  186. Q_INVOKABLE void on_construction_confirm();
  187. Q_INVOKABLE void on_construction_cancel();
  188. Q_INVOKABLE void on_patrol_click(qreal sx, qreal sy);
  189. Q_INVOKABLE void camera_move(float dx, float dz);
  190. Q_INVOKABLE void camera_elevate(float dy);
  191. Q_INVOKABLE void reset_camera();
  192. Q_INVOKABLE void camera_zoom(float delta);
  193. Q_INVOKABLE [[nodiscard]] float camera_distance() const;
  194. Q_INVOKABLE void camera_yaw(float degrees);
  195. Q_INVOKABLE void camera_orbit(float yaw_deg, float pitch_deg);
  196. Q_INVOKABLE void camera_orbit_direction(int direction, bool shift);
  197. Q_INVOKABLE void camera_follow_selection(bool enable);
  198. Q_INVOKABLE void camera_set_follow_lerp(float alpha);
  199. Q_INVOKABLE void on_minimap_left_click(qreal mx, qreal my,
  200. qreal minimap_width,
  201. qreal minimap_height);
  202. Q_INVOKABLE void on_minimap_right_click(qreal mx, qreal my,
  203. qreal minimap_width,
  204. qreal minimap_height);
  205. Q_INVOKABLE void start_loading_maps();
  206. Q_INVOKABLE void set_paused(bool paused) { m_runtime.paused = paused; }
  207. Q_INVOKABLE void set_game_speed(float speed) {
  208. m_runtime.time_scale = std::max(0.0F, speed);
  209. }
  210. [[nodiscard]] bool paused() const { return m_runtime.paused; }
  211. [[nodiscard]] float time_scale() const { return m_runtime.time_scale; }
  212. [[nodiscard]] QString victory_state() const {
  213. return m_runtime.victory_state;
  214. }
  215. [[nodiscard]] QString cursor_mode() const;
  216. void set_cursor_mode(CursorMode mode);
  217. void set_cursor_mode(const QString &mode);
  218. [[nodiscard]] qreal global_cursor_x() const;
  219. [[nodiscard]] qreal global_cursor_y() const;
  220. [[nodiscard]] bool has_units_selected() const;
  221. [[nodiscard]] int player_troop_count() const;
  222. [[nodiscard]] int max_troops_per_player() const {
  223. return m_level.max_troops_per_player;
  224. }
  225. [[nodiscard]] int enemy_troops_defeated() const;
  226. Q_INVOKABLE [[nodiscard]] static QVariantMap get_player_stats(int owner_id);
  227. [[nodiscard]] int selected_player_id() const { return m_selected_player_id; }
  228. void set_selected_player_id(int id) {
  229. if (m_selected_player_id != id) {
  230. m_selected_player_id = id;
  231. emit selected_player_id_changed();
  232. }
  233. }
  234. [[nodiscard]] QString last_error() const { return m_runtime.last_error; }
  235. Q_INVOKABLE void clear_error() {
  236. if (!m_runtime.last_error.isEmpty()) {
  237. m_runtime.last_error = "";
  238. emit last_error_changed();
  239. }
  240. }
  241. Q_INVOKABLE [[nodiscard]] bool has_selected_type(const QString &type) const;
  242. Q_INVOKABLE void recruit_near_selected(const QString &unit_type);
  243. Q_INVOKABLE void start_building_placement(const QString &building_type);
  244. Q_INVOKABLE void place_building_at_screen(qreal sx, qreal sy);
  245. Q_INVOKABLE void cancel_building_placement();
  246. Q_INVOKABLE [[nodiscard]] QString pending_building_type() const;
  247. Q_INVOKABLE [[nodiscard]] QVariantMap get_selected_production_state() const;
  248. Q_INVOKABLE [[nodiscard]] QVariantMap
  249. get_selected_builder_production_state() const;
  250. Q_INVOKABLE void start_builder_construction(const QString &item_type);
  251. Q_INVOKABLE [[nodiscard]] QVariantMap
  252. get_unit_production_info(const QString &unit_type,
  253. const QString &nation_id) const;
  254. Q_INVOKABLE [[nodiscard]] QString get_selected_units_command_mode() const;
  255. Q_INVOKABLE [[nodiscard]] QVariantMap
  256. get_selected_units_mode_availability() const;
  257. Q_INVOKABLE void set_rally_at_screen(qreal sx, qreal sy);
  258. Q_INVOKABLE [[nodiscard]] QVariantList available_maps() const;
  259. [[nodiscard]] QVariantList available_nations() const;
  260. [[nodiscard]] QVariantList available_campaigns() const;
  261. [[nodiscard]] bool maps_loading() const { return m_maps_loading; }
  262. Q_INVOKABLE void
  263. start_skirmish(const QString &map_path,
  264. const QVariantList &playerConfigs = QVariantList());
  265. Q_INVOKABLE void start_campaign_mission(const QString &campaign_id);
  266. Q_INVOKABLE void mark_current_mission_completed();
  267. Q_INVOKABLE [[nodiscard]] QVariantMap get_current_mission_objectives() const;
  268. Q_INVOKABLE void open_settings();
  269. Q_INVOKABLE void load_save();
  270. Q_INVOKABLE void save_game(const QString &filename = "savegame.json");
  271. Q_INVOKABLE void save_game_to_slot(const QString &slot_name);
  272. Q_INVOKABLE void load_game_from_slot(const QString &slot_name);
  273. Q_INVOKABLE [[nodiscard]] QVariantList get_save_slots() const;
  274. Q_INVOKABLE void refresh_save_slots();
  275. Q_INVOKABLE bool delete_save_slot(const QString &slot_name);
  276. Q_INVOKABLE void exit_game();
  277. Q_INVOKABLE [[nodiscard]] QVariantList get_owner_info() const;
  278. Q_INVOKABLE [[nodiscard]] QImage
  279. generate_map_preview(const QString &map_path,
  280. const QVariantList &player_configs) const;
  281. [[nodiscard]] QImage minimap_image() const;
  282. [[nodiscard]] bool is_spectator_mode() const {
  283. return m_level.is_spectator_mode;
  284. }
  285. [[nodiscard]] bool is_loading() const {
  286. return m_runtime.loading || m_loading_overlay_active;
  287. }
  288. [[nodiscard]] float loading_progress() const;
  289. [[nodiscard]] QString loading_stage_text() const;
  290. [[nodiscard]] bool is_campaign_mission() const;
  291. QObject *audio_system();
  292. void setWindow(QQuickWindow *w) { m_window = w; }
  293. void ensure_initialized();
  294. void update(float dt);
  295. void render(int pixelWidth, int pixelHeight);
  296. void get_selected_unit_ids(std::vector<Engine::Core::EntityID> &out) const;
  297. bool get_unit_type_key(Engine::Core::EntityID id, QString &type_key) const;
  298. bool get_unit_info(Engine::Core::EntityID id, QString &name, int &health,
  299. int &max_health, bool &is_building, bool &alive,
  300. QString &nation) const;
  301. bool get_unit_stamina_info(Engine::Core::EntityID id, float &stamina_ratio,
  302. bool &is_running, bool &can_run) const;
  303. [[nodiscard]] bool has_patrol_preview_waypoint() const;
  304. [[nodiscard]] QVector3D get_patrol_preview_waypoint() const;
  305. private:
  306. struct RuntimeState {
  307. bool initialized = false;
  308. bool paused = false;
  309. bool loading = false;
  310. float time_scale = 1.0F;
  311. int local_owner_id = 1;
  312. QString victory_state = "";
  313. CursorMode cursor_mode{CursorMode::Normal};
  314. QString last_error = "";
  315. Qt::CursorShape current_cursor = Qt::ArrowCursor;
  316. int last_troop_count = 0;
  317. std::uint64_t visibility_version = 0;
  318. float visibility_update_accumulator = 0.0F;
  319. qreal last_cursor_x = -1.0;
  320. qreal last_cursor_y = -1.0;
  321. int selection_refresh_counter = 0;
  322. };
  323. bool screen_to_ground(const QPointF &screenPt, QVector3D &outWorld);
  324. bool world_to_screen(const QVector3D &world, QPointF &outScreen) const;
  325. void sync_selection_flags();
  326. static void reset_movement(Engine::Core::Entity *entity);
  327. QAbstractItemModel *selected_units_model();
  328. void on_unit_spawned(const Engine::Core::UnitSpawnedEvent &event);
  329. void on_unit_died(const Engine::Core::UnitDiedEvent &event);
  330. void rebuild_entity_cache();
  331. void rebuild_registries_after_load();
  332. void rebuild_building_collisions();
  333. void restore_environment_from_metadata(const QJsonObject &metadata);
  334. void update_cursor(Qt::CursorShape newCursor);
  335. void set_error(const QString &errorMessage);
  336. bool load_from_slot(const QString &slot);
  337. bool save_to_slot(const QString &slot, const QString &title);
  338. [[nodiscard]] Game::Systems::RuntimeSnapshot to_runtime_snapshot() const;
  339. void apply_runtime_snapshot(const Game::Systems::RuntimeSnapshot &snapshot);
  340. [[nodiscard]] QByteArray capture_screenshot() const;
  341. void start_skirmish_internal(const QString &map_path,
  342. const QVariantList &playerConfigs,
  343. bool set_skirmish_context);
  344. void perform_skirmish_load(const QString &map_path,
  345. const QVariantList &playerConfigs);
  346. void apply_mission_setup();
  347. void configure_mission_victory_conditions();
  348. void configure_rain_system();
  349. void finalize_skirmish_load();
  350. void render_game_effects();
  351. void update_loading_overlay();
  352. void update_cursor_position();
  353. std::unique_ptr<Engine::Core::World> m_world;
  354. std::unique_ptr<Render::GL::Renderer> m_renderer;
  355. std::unique_ptr<Render::GL::Camera> m_camera;
  356. std::shared_ptr<Render::GL::ResourceManager> m_resources;
  357. std::unique_ptr<Render::GL::GroundRenderer> m_ground;
  358. std::unique_ptr<Render::GL::TerrainRenderer> m_terrain;
  359. std::unique_ptr<Render::GL::BiomeRenderer> m_biome;
  360. std::unique_ptr<Render::GL::RiverRenderer> m_river;
  361. std::unique_ptr<Render::GL::RoadRenderer> m_road;
  362. std::unique_ptr<Render::GL::RiverbankRenderer> m_riverbank;
  363. std::unique_ptr<Render::GL::BridgeRenderer> m_bridge;
  364. std::unique_ptr<Render::GL::FogRenderer> m_fog;
  365. std::unique_ptr<Render::GL::StoneRenderer> m_stone;
  366. std::unique_ptr<Render::GL::PlantRenderer> m_plant;
  367. std::unique_ptr<Render::GL::PineRenderer> m_pine;
  368. std::unique_ptr<Render::GL::OliveRenderer> m_olive;
  369. std::unique_ptr<Render::GL::FireCampRenderer> m_firecamp;
  370. std::unique_ptr<Render::GL::RainRenderer> m_rain;
  371. std::unique_ptr<Game::Systems::RainManager> m_rainManager;
  372. std::vector<Render::GL::IRenderPass *> m_passes;
  373. std::unique_ptr<Game::Systems::PickingService> m_pickingService;
  374. std::unique_ptr<Game::Systems::VictoryService> m_victoryService;
  375. std::unique_ptr<Game::Systems::SaveLoadService> m_saveLoadService;
  376. std::unique_ptr<CursorManager> m_cursor_manager;
  377. std::unique_ptr<HoverTracker> m_hoverTracker;
  378. std::unique_ptr<Game::Systems::CameraService> m_cameraService;
  379. std::unique_ptr<Game::Systems::SelectionController> m_selectionController;
  380. std::unique_ptr<App::Controllers::CommandController> m_commandController;
  381. std::unique_ptr<Game::Map::MapCatalog> m_mapCatalog;
  382. std::unique_ptr<Game::Audio::AudioEventHandler> m_audioEventHandler;
  383. std::unique_ptr<App::Models::AudioSystemProxy> m_audio_systemProxy;
  384. std::unique_ptr<MinimapManager> m_minimap_manager;
  385. std::unique_ptr<AmbientStateManager> m_ambient_state_manager;
  386. std::unique_ptr<InputCommandHandler> m_input_handler;
  387. std::unique_ptr<CameraController> m_camera_controller;
  388. std::unique_ptr<LoadingProgressTracker> m_loading_progress_tracker;
  389. std::unique_ptr<ProductionManager> m_production_manager;
  390. std::unique_ptr<CampaignManager> m_campaign_manager;
  391. std::unique_ptr<SelectionQueryService> m_selection_query_service;
  392. QQuickWindow *m_window = nullptr;
  393. RuntimeState m_runtime;
  394. ViewportState m_viewport;
  395. bool m_followSelectionEnabled = false;
  396. Game::Systems::LevelSnapshot m_level;
  397. SelectedUnitsModel *m_selectedUnitsModel = nullptr;
  398. int m_enemyTroopsDefeated = 0;
  399. int m_selected_player_id = 1;
  400. QVariantList m_available_maps;
  401. bool m_maps_loading = false;
  402. bool m_loading_overlay_active = false;
  403. bool m_loading_overlay_wait_for_first_frame = false;
  404. int m_loading_overlay_frames_remaining = 0;
  405. qint64 m_loading_overlay_min_duration_ms = 0;
  406. QElapsedTimer m_loading_overlay_timer;
  407. bool m_finalize_progress_after_overlay = false;
  408. bool m_show_objectives_after_loading = false;
  409. Engine::Core::ScopedEventSubscription<Engine::Core::UnitDiedEvent>
  410. m_unit_died_subscription;
  411. Engine::Core::ScopedEventSubscription<Engine::Core::UnitSpawnedEvent>
  412. m_unit_spawned_subscription;
  413. EntityCache m_entity_cache;
  414. signals:
  415. void selected_units_changed();
  416. void selected_units_data_changed();
  417. void enemy_troops_defeated_changed();
  418. void victory_state_changed();
  419. void cursor_mode_changed();
  420. void global_cursor_changed();
  421. void troop_count_changed();
  422. void available_maps_changed();
  423. void available_campaigns_changed();
  424. void owner_info_changed();
  425. void selected_player_id_changed();
  426. void last_error_changed();
  427. void maps_loading_changed();
  428. void minimap_image_changed();
  429. void save_slots_changed();
  430. void hold_mode_changed(bool active);
  431. void guard_mode_changed(bool active);
  432. void formation_mode_changed(bool active);
  433. void run_mode_changed(bool active);
  434. void spectator_mode_changed();
  435. void is_loading_changed();
  436. void loading_progress_changed(float progress);
  437. void loading_stage_changed(QString stage_text);
  438. void placing_formation_changed();
  439. void placing_construction_changed();
  440. void campaign_mission_changed();
  441. };