mission_loader.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. return cond;
  136. }
  137. auto MissionLoader::parseEventTrigger(const QJsonObject &obj) -> EventTrigger {
  138. EventTrigger trigger;
  139. trigger.type = obj["type"].toString();
  140. if (obj.contains("time")) {
  141. trigger.time = static_cast<float>(obj["time"].toDouble());
  142. }
  143. return trigger;
  144. }
  145. auto MissionLoader::parseEventAction(const QJsonObject &obj) -> EventAction {
  146. EventAction action;
  147. action.type = obj["type"].toString();
  148. if (obj.contains("text")) {
  149. action.text = obj["text"].toString();
  150. }
  151. return action;
  152. }
  153. auto MissionLoader::parseGameEvent(const QJsonObject &obj) -> GameEvent {
  154. GameEvent event;
  155. event.trigger = parseEventTrigger(obj["trigger"].toObject());
  156. const QJsonArray actions = obj["actions"].toArray();
  157. for (const auto &action_val : actions) {
  158. event.actions.push_back(parseEventAction(action_val.toObject()));
  159. }
  160. return event;
  161. }
  162. auto MissionLoader::loadFromJsonFile(const QString &file_path,
  163. MissionDefinition &out_mission,
  164. QString *error_msg) -> bool {
  165. QFile file(file_path);
  166. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  167. if (error_msg != nullptr) {
  168. *error_msg = QString("Failed to open file: %1").arg(file_path);
  169. }
  170. return false;
  171. }
  172. QJsonParseError parse_error;
  173. const QJsonDocument doc =
  174. QJsonDocument::fromJson(file.readAll(), &parse_error);
  175. file.close();
  176. if (parse_error.error != QJsonParseError::NoError) {
  177. if (error_msg != nullptr) {
  178. *error_msg =
  179. QString("JSON parse error: %1").arg(parse_error.errorString());
  180. }
  181. return false;
  182. }
  183. if (!doc.isObject()) {
  184. if (error_msg != nullptr) {
  185. *error_msg = "JSON root is not an object";
  186. }
  187. return false;
  188. }
  189. const QJsonObject root = doc.object();
  190. out_mission.id = root["id"].toString();
  191. out_mission.title = root["title"].toString();
  192. out_mission.summary = root["summary"].toString();
  193. out_mission.map_path = root["map_path"].toString();
  194. if (root.contains("teaching_goal")) {
  195. out_mission.teaching_goal = root["teaching_goal"].toString();
  196. }
  197. if (root.contains("narrative_intent")) {
  198. out_mission.narrative_intent = root["narrative_intent"].toString();
  199. }
  200. if (root.contains("historical_context")) {
  201. out_mission.historical_context = root["historical_context"].toString();
  202. }
  203. if (root.contains("terrain_type")) {
  204. out_mission.terrain_type = root["terrain_type"].toString();
  205. }
  206. if (root.contains("player_setup")) {
  207. out_mission.player_setup =
  208. parsePlayerSetup(root["player_setup"].toObject());
  209. }
  210. if (root.contains("ai_setups")) {
  211. const QJsonArray ai_setups = root["ai_setups"].toArray();
  212. for (const auto &ai_val : ai_setups) {
  213. out_mission.ai_setups.push_back(parseAISetup(ai_val.toObject()));
  214. }
  215. }
  216. if (root.contains("victory_conditions")) {
  217. const QJsonArray victory = root["victory_conditions"].toArray();
  218. for (const auto &cond_val : victory) {
  219. out_mission.victory_conditions.push_back(
  220. parseCondition(cond_val.toObject()));
  221. }
  222. }
  223. if (root.contains("defeat_conditions")) {
  224. const QJsonArray defeat = root["defeat_conditions"].toArray();
  225. for (const auto &cond_val : defeat) {
  226. out_mission.defeat_conditions.push_back(
  227. parseCondition(cond_val.toObject()));
  228. }
  229. }
  230. if (root.contains("optional_objectives")) {
  231. const QJsonArray optional = root["optional_objectives"].toArray();
  232. for (const auto &cond_val : optional) {
  233. out_mission.optional_objectives.push_back(
  234. parseCondition(cond_val.toObject()));
  235. }
  236. }
  237. if (root.contains("events")) {
  238. const QJsonArray events = root["events"].toArray();
  239. for (const auto &event_val : events) {
  240. out_mission.events.push_back(parseGameEvent(event_val.toObject()));
  241. }
  242. }
  243. return true;
  244. }
  245. } // namespace Game::Mission