skirmish_loader.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "skirmish_loader.h"
  2. #include "game/core/component.h"
  3. #include "game/core/world.h"
  4. #include "game/map/level_loader.h"
  5. #include "game/map/map_transformer.h"
  6. #include "game/map/terrain_service.h"
  7. #include "game/map/visibility_service.h"
  8. #include "game/systems/building_collision_registry.h"
  9. #include "game/systems/command_service.h"
  10. #include "game/systems/global_stats_registry.h"
  11. #include "game/systems/owner_registry.h"
  12. #include "game/systems/selection_system.h"
  13. #include "game/systems/troop_count_registry.h"
  14. #include "game/visuals/team_colors.h"
  15. #include "render/ground/biome_renderer.h"
  16. #include "render/ground/bridge_renderer.h"
  17. #include "render/ground/fog_renderer.h"
  18. #include "render/ground/ground_renderer.h"
  19. #include "render/ground/pine_renderer.h"
  20. #include "render/ground/plant_renderer.h"
  21. #include "render/ground/river_renderer.h"
  22. #include "render/ground/riverbank_renderer.h"
  23. #include "render/ground/stone_renderer.h"
  24. #include "render/ground/terrain_renderer.h"
  25. #include "render/scene_renderer.h"
  26. #include <QDebug>
  27. #include <QFile>
  28. #include <QJsonArray>
  29. #include <QJsonDocument>
  30. #include <QJsonObject>
  31. #include <QSet>
  32. #include <algorithm>
  33. #include <set>
  34. #include <unordered_map>
  35. namespace Game {
  36. namespace Map {
  37. SkirmishLoader::SkirmishLoader(Engine::Core::World &world,
  38. Render::GL::Renderer &renderer,
  39. Render::GL::Camera &camera)
  40. : m_world(world), m_renderer(renderer), m_camera(camera) {}
  41. void SkirmishLoader::resetGameState() {
  42. if (auto *selectionSystem =
  43. m_world.getSystem<Game::Systems::SelectionSystem>()) {
  44. selectionSystem->clearSelection();
  45. }
  46. m_renderer.pause();
  47. m_renderer.lockWorldForModification();
  48. m_renderer.setSelectedEntities({});
  49. m_renderer.setHoveredEntityId(0);
  50. m_world.clear();
  51. Game::Systems::BuildingCollisionRegistry::instance().clear();
  52. auto &ownerRegistry = Game::Systems::OwnerRegistry::instance();
  53. ownerRegistry.clear();
  54. auto &visibilityService = Game::Map::VisibilityService::instance();
  55. visibilityService.reset();
  56. auto &terrainService = Game::Map::TerrainService::instance();
  57. terrainService.clear();
  58. auto &statsRegistry = Game::Systems::GlobalStatsRegistry::instance();
  59. statsRegistry.clear();
  60. auto &troopRegistry = Game::Systems::TroopCountRegistry::instance();
  61. troopRegistry.clear();
  62. if (m_fog) {
  63. m_fog->updateMask(0, 0, 1.0f, {});
  64. }
  65. }
  66. SkirmishLoadResult SkirmishLoader::start(const QString &mapPath,
  67. const QVariantList &playerConfigs,
  68. int selectedPlayerId,
  69. int &outSelectedPlayerId) {
  70. SkirmishLoadResult result;
  71. resetGameState();
  72. QSet<int> mapPlayerIds;
  73. QFile mapFile(mapPath);
  74. if (mapFile.open(QIODevice::ReadOnly)) {
  75. QByteArray data = mapFile.readAll();
  76. mapFile.close();
  77. QJsonParseError err;
  78. QJsonDocument doc = QJsonDocument::fromJson(data, &err);
  79. if (err.error == QJsonParseError::NoError && doc.isObject()) {
  80. QJsonObject obj = doc.object();
  81. if (obj.contains("spawns") && obj["spawns"].isArray()) {
  82. QJsonArray spawns = obj["spawns"].toArray();
  83. for (const QJsonValue &spawnVal : spawns) {
  84. if (spawnVal.isObject()) {
  85. QJsonObject spawn = spawnVal.toObject();
  86. if (spawn.contains("playerId")) {
  87. int playerId = spawn["playerId"].toInt();
  88. if (playerId > 0) {
  89. mapPlayerIds.insert(playerId);
  90. }
  91. }
  92. }
  93. }
  94. }
  95. }
  96. } else {
  97. qWarning() << "Could not open map file for reading player IDs:" << mapPath;
  98. }
  99. auto &ownerRegistry = Game::Systems::OwnerRegistry::instance();
  100. int playerOwnerId = selectedPlayerId;
  101. if (!mapPlayerIds.contains(playerOwnerId)) {
  102. if (!mapPlayerIds.isEmpty()) {
  103. QList<int> sortedIds = mapPlayerIds.values();
  104. std::sort(sortedIds.begin(), sortedIds.end());
  105. playerOwnerId = sortedIds.first();
  106. qWarning() << "Selected player ID" << selectedPlayerId
  107. << "not found in map spawns. Using" << playerOwnerId
  108. << "instead.";
  109. outSelectedPlayerId = playerOwnerId;
  110. } else {
  111. qWarning() << "No valid player spawns found in map. Using default "
  112. "player ID"
  113. << playerOwnerId;
  114. }
  115. }
  116. ownerRegistry.setLocalPlayerId(playerOwnerId);
  117. std::unordered_map<int, int> teamOverrides;
  118. QVariantList savedPlayerConfigs;
  119. std::set<int> processedPlayerIds;
  120. if (!playerConfigs.isEmpty()) {
  121. for (const QVariant &configVar : playerConfigs) {
  122. QVariantMap config = configVar.toMap();
  123. int playerId = config.value("playerId", -1).toInt();
  124. int teamId = config.value("teamId", 0).toInt();
  125. QString colorHex = config.value("colorHex", "#FFFFFF").toString();
  126. bool isHuman = config.value("isHuman", false).toBool();
  127. if (isHuman && playerId != playerOwnerId) {
  128. playerId = playerOwnerId;
  129. }
  130. if (processedPlayerIds.count(playerId) > 0) {
  131. continue;
  132. }
  133. if (playerId >= 0) {
  134. processedPlayerIds.insert(playerId);
  135. teamOverrides[playerId] = teamId;
  136. QVariantMap updatedConfig = config;
  137. updatedConfig["playerId"] = playerId;
  138. savedPlayerConfigs.append(updatedConfig);
  139. }
  140. }
  141. }
  142. Game::Map::MapTransformer::setLocalOwnerId(playerOwnerId);
  143. Game::Map::MapTransformer::setPlayerTeamOverrides(teamOverrides);
  144. auto lr = Game::Map::LevelLoader::loadFromAssets(mapPath, m_world, m_renderer,
  145. m_camera);
  146. if (!lr.ok && !lr.errorMessage.isEmpty()) {
  147. result.errorMessage = lr.errorMessage;
  148. m_renderer.unlockWorldForModification();
  149. m_renderer.resume();
  150. return result;
  151. }
  152. if (!savedPlayerConfigs.isEmpty()) {
  153. for (const QVariant &configVar : savedPlayerConfigs) {
  154. QVariantMap config = configVar.toMap();
  155. int playerId = config.value("playerId", -1).toInt();
  156. QString colorHex = config.value("colorHex", "#FFFFFF").toString();
  157. if (playerId >= 0 && colorHex.startsWith("#") && colorHex.length() == 7) {
  158. bool ok;
  159. int r = colorHex.mid(1, 2).toInt(&ok, 16);
  160. int g = colorHex.mid(3, 2).toInt(&ok, 16);
  161. int b = colorHex.mid(5, 2).toInt(&ok, 16);
  162. ownerRegistry.setOwnerColor(playerId, r / 255.0f, g / 255.0f,
  163. b / 255.0f);
  164. }
  165. }
  166. auto entities = m_world.getEntitiesWith<Engine::Core::UnitComponent>();
  167. std::unordered_map<int, int> ownerEntityCount;
  168. for (auto *entity : entities) {
  169. auto *unit = entity->getComponent<Engine::Core::UnitComponent>();
  170. auto *renderable =
  171. entity->getComponent<Engine::Core::RenderableComponent>();
  172. if (unit && renderable) {
  173. QVector3D tc = Game::Visuals::teamColorForOwner(unit->ownerId);
  174. renderable->color[0] = tc.x();
  175. renderable->color[1] = tc.y();
  176. renderable->color[2] = tc.z();
  177. ownerEntityCount[unit->ownerId]++;
  178. }
  179. }
  180. }
  181. if (m_onOwnersUpdated) {
  182. m_onOwnersUpdated();
  183. }
  184. auto &terrainService = Game::Map::TerrainService::instance();
  185. if (m_ground) {
  186. if (lr.ok)
  187. m_ground->configure(lr.tileSize, lr.gridWidth, lr.gridHeight);
  188. else
  189. m_ground->configureExtent(50.0f);
  190. if (terrainService.isInitialized())
  191. m_ground->setBiome(terrainService.biomeSettings());
  192. }
  193. if (m_terrain) {
  194. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  195. m_terrain->configure(*terrainService.getHeightMap(),
  196. terrainService.biomeSettings());
  197. }
  198. }
  199. if (m_biome) {
  200. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  201. m_biome->configure(*terrainService.getHeightMap(),
  202. terrainService.biomeSettings());
  203. }
  204. }
  205. if (m_river) {
  206. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  207. m_river->configure(terrainService.getHeightMap()->getRiverSegments(),
  208. terrainService.getHeightMap()->getTileSize());
  209. }
  210. }
  211. if (m_riverbank) {
  212. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  213. m_riverbank->configure(terrainService.getHeightMap()->getRiverSegments(),
  214. *terrainService.getHeightMap());
  215. }
  216. }
  217. if (m_bridge) {
  218. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  219. m_bridge->configure(terrainService.getHeightMap()->getBridges(),
  220. terrainService.getHeightMap()->getTileSize());
  221. }
  222. }
  223. if (m_stone) {
  224. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  225. m_stone->configure(*terrainService.getHeightMap(),
  226. terrainService.biomeSettings());
  227. }
  228. }
  229. if (m_plant) {
  230. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  231. m_plant->configure(*terrainService.getHeightMap(),
  232. terrainService.biomeSettings());
  233. }
  234. }
  235. if (m_pine) {
  236. if (terrainService.isInitialized() && terrainService.getHeightMap()) {
  237. m_pine->configure(*terrainService.getHeightMap(),
  238. terrainService.biomeSettings());
  239. }
  240. }
  241. int mapWidth = lr.ok ? lr.gridWidth : 100;
  242. int mapHeight = lr.ok ? lr.gridHeight : 100;
  243. Game::Systems::CommandService::initialize(mapWidth, mapHeight);
  244. auto &visibilityService = Game::Map::VisibilityService::instance();
  245. visibilityService.initialize(mapWidth, mapHeight, lr.tileSize);
  246. visibilityService.computeImmediate(m_world, playerOwnerId);
  247. if (m_fog && visibilityService.isInitialized()) {
  248. m_fog->updateMask(
  249. visibilityService.getWidth(), visibilityService.getHeight(),
  250. visibilityService.getTileSize(), visibilityService.snapshotCells());
  251. if (m_onVisibilityMaskReady) {
  252. m_onVisibilityMaskReady();
  253. }
  254. }
  255. if (m_biome) {
  256. m_biome->refreshGrass();
  257. }
  258. m_renderer.unlockWorldForModification();
  259. m_renderer.resume();
  260. Engine::Core::Entity *focusEntity = nullptr;
  261. auto candidates = m_world.getEntitiesWith<Engine::Core::UnitComponent>();
  262. for (auto *e : candidates) {
  263. if (!e)
  264. continue;
  265. auto *u = e->getComponent<Engine::Core::UnitComponent>();
  266. if (!u)
  267. continue;
  268. if (u->unitType == "barracks" && u->ownerId == playerOwnerId &&
  269. u->health > 0) {
  270. focusEntity = e;
  271. break;
  272. }
  273. }
  274. if (!focusEntity && lr.playerUnitId != 0) {
  275. focusEntity = m_world.getEntity(lr.playerUnitId);
  276. }
  277. if (focusEntity) {
  278. if (auto *t =
  279. focusEntity->getComponent<Engine::Core::TransformComponent>()) {
  280. result.focusPosition =
  281. QVector3D(t->position.x, t->position.y, t->position.z);
  282. result.hasFocusPosition = true;
  283. }
  284. }
  285. result.ok = true;
  286. result.mapName = lr.mapName;
  287. result.playerUnitId = lr.playerUnitId;
  288. result.camFov = lr.camFov;
  289. result.camNear = lr.camNear;
  290. result.camFar = lr.camFar;
  291. result.gridWidth = lr.gridWidth;
  292. result.gridHeight = lr.gridHeight;
  293. result.tileSize = lr.tileSize;
  294. result.maxTroopsPerPlayer = lr.maxTroopsPerPlayer;
  295. result.victoryConfig = lr.victoryConfig;
  296. return result;
  297. }
  298. } // namespace Map
  299. } // namespace Game