mission_loader.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. #include "mission_loader.h"
  2. #include <QFile>
  3. #include <QJsonArray>
  4. #include <QJsonDocument>
  5. #include <QJsonObject>
  6. namespace Game::Mission {
  7. auto MissionLoader::parsePosition(const QJsonObject &obj) -> Position {
  8. Position pos;
  9. pos.x = static_cast<float>(obj["x"].toDouble(0.0));
  10. pos.z = static_cast<float>(obj["z"].toDouble(0.0));
  11. return pos;
  12. }
  13. auto MissionLoader::parseUnitSetup(const QJsonObject &obj) -> UnitSetup {
  14. UnitSetup unit;
  15. unit.type = obj["type"].toString();
  16. unit.count = obj["count"].toInt(1);
  17. unit.position = parsePosition(obj["position"].toObject());
  18. return unit;
  19. }
  20. auto MissionLoader::parseBuildingSetup(const QJsonObject &obj)
  21. -> BuildingSetup {
  22. BuildingSetup building;
  23. building.type = obj["type"].toString();
  24. building.position = parsePosition(obj["position"].toObject());
  25. building.max_population = obj["max_population"].toInt(100);
  26. return building;
  27. }
  28. auto MissionLoader::parseResources(const QJsonObject &obj) -> Resources {
  29. Resources res;
  30. res.gold = obj["gold"].toInt(0);
  31. res.food = obj["food"].toInt(0);
  32. return res;
  33. }
  34. auto MissionLoader::parsePlayerSetup(const QJsonObject &obj) -> PlayerSetup {
  35. PlayerSetup setup;
  36. setup.nation = obj["nation"].toString();
  37. setup.faction = obj["faction"].toString();
  38. setup.color = obj["color"].toString();
  39. const QJsonArray units = obj["starting_units"].toArray();
  40. for (const auto &unit_val : units) {
  41. setup.starting_units.push_back(parseUnitSetup(unit_val.toObject()));
  42. }
  43. const QJsonArray buildings = obj["starting_buildings"].toArray();
  44. for (const auto &building_val : buildings) {
  45. setup.starting_buildings.push_back(
  46. parseBuildingSetup(building_val.toObject()));
  47. }
  48. if (obj.contains("starting_resources")) {
  49. setup.starting_resources =
  50. parseResources(obj["starting_resources"].toObject());
  51. }
  52. return setup;
  53. }
  54. auto MissionLoader::parseAIPersonality(const QJsonObject &obj)
  55. -> AIPersonality {
  56. AIPersonality personality;
  57. personality.aggression = static_cast<float>(obj["aggression"].toDouble(0.5));
  58. personality.defense = static_cast<float>(obj["defense"].toDouble(0.5));
  59. personality.harassment = static_cast<float>(obj["harassment"].toDouble(0.5));
  60. return personality;
  61. }
  62. auto MissionLoader::parseWaveComposition(const QJsonObject &obj)
  63. -> WaveComposition {
  64. WaveComposition comp;
  65. comp.type = obj["type"].toString();
  66. comp.count = obj["count"].toInt(1);
  67. return comp;
  68. }
  69. auto MissionLoader::parseWave(const QJsonObject &obj) -> Wave {
  70. Wave wave;
  71. wave.timing = static_cast<float>(obj["timing"].toDouble(0.0));
  72. wave.entry_point = parsePosition(obj["entry_point"].toObject());
  73. const QJsonArray composition = obj["composition"].toArray();
  74. for (const auto &comp_val : composition) {
  75. wave.composition.push_back(parseWaveComposition(comp_val.toObject()));
  76. }
  77. return wave;
  78. }
  79. auto MissionLoader::parseAISetup(const QJsonObject &obj) -> AISetup {
  80. AISetup setup;
  81. setup.id = obj["id"].toString();
  82. setup.nation = obj["nation"].toString();
  83. setup.faction = obj["faction"].toString();
  84. setup.color = obj["color"].toString();
  85. setup.difficulty = obj["difficulty"].toString();
  86. if (obj.contains("team_id")) {
  87. setup.team_id = obj["team_id"].toInt();
  88. }
  89. if (obj.contains("strategy")) {
  90. setup.strategy = obj["strategy"].toString();
  91. }
  92. if (obj.contains("personality")) {
  93. setup.personality = parseAIPersonality(obj["personality"].toObject());
  94. }
  95. if (obj.contains("starting_units")) {
  96. const QJsonArray units = obj["starting_units"].toArray();
  97. for (const auto &unit_val : units) {
  98. setup.starting_units.push_back(parseUnitSetup(unit_val.toObject()));
  99. }
  100. }
  101. if (obj.contains("starting_buildings")) {
  102. const QJsonArray buildings = obj["starting_buildings"].toArray();
  103. for (const auto &building_val : buildings) {
  104. setup.starting_buildings.push_back(
  105. parseBuildingSetup(building_val.toObject()));
  106. }
  107. }
  108. if (obj.contains("waves")) {
  109. const QJsonArray waves = obj["waves"].toArray();
  110. for (const auto &wave_val : waves) {
  111. setup.waves.push_back(parseWave(wave_val.toObject()));
  112. }
  113. }
  114. return setup;
  115. }
  116. auto MissionLoader::parseCondition(const QJsonObject &obj) -> Condition {
  117. Condition cond;
  118. cond.type = obj["type"].toString();
  119. cond.description = obj["description"].toString();
  120. if (obj.contains("duration")) {
  121. cond.duration = static_cast<float>(obj["duration"].toDouble());
  122. }
  123. if (obj.contains("structure_type")) {
  124. cond.structure_type = obj["structure_type"].toString();
  125. }
  126. if (obj.contains("structure_types") && obj["structure_types"].isArray()) {
  127. const QJsonArray types = obj["structure_types"].toArray();
  128. for (const auto &type_val : types) {
  129. cond.structure_types.push_back(type_val.toString());
  130. }
  131. }
  132. if (obj.contains("min_count")) {
  133. cond.min_count = obj["min_count"].toInt();
  134. }
  135. if (obj.contains("wave_count")) {
  136. cond.wave_count = obj["wave_count"].toInt();
  137. }
  138. return cond;
  139. }
  140. auto MissionLoader::parseEventTrigger(const QJsonObject &obj) -> EventTrigger {
  141. EventTrigger trigger;
  142. trigger.type = obj["type"].toString();
  143. if (obj.contains("time")) {
  144. trigger.time = static_cast<float>(obj["time"].toDouble());
  145. }
  146. return trigger;
  147. }
  148. auto MissionLoader::parseEventAction(const QJsonObject &obj) -> EventAction {
  149. EventAction action;
  150. action.type = obj["type"].toString();
  151. if (obj.contains("text")) {
  152. action.text = obj["text"].toString();
  153. }
  154. return action;
  155. }
  156. auto MissionLoader::parseGameEvent(const QJsonObject &obj) -> GameEvent {
  157. GameEvent event;
  158. event.trigger = parseEventTrigger(obj["trigger"].toObject());
  159. const QJsonArray actions = obj["actions"].toArray();
  160. for (const auto &action_val : actions) {
  161. event.actions.push_back(parseEventAction(action_val.toObject()));
  162. }
  163. return event;
  164. }
  165. auto MissionLoader::loadFromJsonFile(const QString &file_path,
  166. MissionDefinition &out_mission,
  167. QString *error_msg) -> bool {
  168. QFile file(file_path);
  169. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  170. if (error_msg != nullptr) {
  171. *error_msg = QString("Failed to open file: %1").arg(file_path);
  172. }
  173. return false;
  174. }
  175. QJsonParseError parse_error;
  176. const QJsonDocument doc =
  177. QJsonDocument::fromJson(file.readAll(), &parse_error);
  178. file.close();
  179. if (parse_error.error != QJsonParseError::NoError) {
  180. if (error_msg != nullptr) {
  181. *error_msg =
  182. QString("JSON parse error: %1").arg(parse_error.errorString());
  183. }
  184. return false;
  185. }
  186. if (!doc.isObject()) {
  187. if (error_msg != nullptr) {
  188. *error_msg = "JSON root is not an object";
  189. }
  190. return false;
  191. }
  192. const QJsonObject root = doc.object();
  193. out_mission.id = root["id"].toString();
  194. out_mission.title = root["title"].toString();
  195. out_mission.summary = root["summary"].toString();
  196. out_mission.map_path = root["map_path"].toString();
  197. if (root.contains("teaching_goal")) {
  198. out_mission.teaching_goal = root["teaching_goal"].toString();
  199. }
  200. if (root.contains("narrative_intent")) {
  201. out_mission.narrative_intent = root["narrative_intent"].toString();
  202. }
  203. if (root.contains("historical_context")) {
  204. out_mission.historical_context = root["historical_context"].toString();
  205. }
  206. if (root.contains("terrain_type")) {
  207. out_mission.terrain_type = root["terrain_type"].toString();
  208. }
  209. if (root.contains("player_setup")) {
  210. out_mission.player_setup =
  211. parsePlayerSetup(root["player_setup"].toObject());
  212. }
  213. if (root.contains("ai_setups")) {
  214. const QJsonArray ai_setups = root["ai_setups"].toArray();
  215. for (const auto &ai_val : ai_setups) {
  216. out_mission.ai_setups.push_back(parseAISetup(ai_val.toObject()));
  217. }
  218. }
  219. if (root.contains("victory_conditions")) {
  220. const QJsonArray victory = root["victory_conditions"].toArray();
  221. for (const auto &cond_val : victory) {
  222. out_mission.victory_conditions.push_back(
  223. parseCondition(cond_val.toObject()));
  224. }
  225. }
  226. if (root.contains("defeat_conditions")) {
  227. const QJsonArray defeat = root["defeat_conditions"].toArray();
  228. for (const auto &cond_val : defeat) {
  229. out_mission.defeat_conditions.push_back(
  230. parseCondition(cond_val.toObject()));
  231. }
  232. }
  233. if (root.contains("optional_objectives")) {
  234. const QJsonArray optional = root["optional_objectives"].toArray();
  235. for (const auto &cond_val : optional) {
  236. out_mission.optional_objectives.push_back(
  237. parseCondition(cond_val.toObject()));
  238. }
  239. }
  240. if (root.contains("events")) {
  241. const QJsonArray events = root["events"].toArray();
  242. for (const auto &event_val : events) {
  243. out_mission.events.push_back(parseGameEvent(event_val.toObject()));
  244. }
  245. }
  246. return true;
  247. }
  248. } // namespace Game::Mission