serialization.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. #include "serialization.h"
  2. #include "../map/terrain.h"
  3. #include "../map/terrain_service.h"
  4. #include "../systems/nation_id.h"
  5. #include "../units/spawn_type.h"
  6. #include "../units/troop_type.h"
  7. #include "component.h"
  8. #include "entity.h"
  9. #include "world.h"
  10. #include <QByteArray>
  11. #include <QDebug>
  12. #include <QFile>
  13. #include <QJsonArray>
  14. #include <QJsonObject>
  15. #include <QString>
  16. #include <QVector3D>
  17. #include <algorithm>
  18. #include <array>
  19. #include <cstddef>
  20. #include <cstdint>
  21. #include <memory>
  22. #include <qfiledevice.h>
  23. #include <qglobal.h>
  24. #include <qjsonarray.h>
  25. #include <qjsondocument.h>
  26. #include <qjsonobject.h>
  27. #include <qjsonvalue.h>
  28. #include <qstringliteral.h>
  29. #include <qstringview.h>
  30. #include <vector>
  31. #include "../systems/owner_registry.h"
  32. namespace Engine::Core {
  33. namespace {
  34. auto combat_mode_to_string(AttackComponent::CombatMode mode) -> QString {
  35. switch (mode) {
  36. case AttackComponent::CombatMode::Melee:
  37. return "melee";
  38. case AttackComponent::CombatMode::Ranged:
  39. return "ranged";
  40. case AttackComponent::CombatMode::Auto:
  41. default:
  42. return "auto";
  43. }
  44. }
  45. auto combat_mode_from_string(const QString &value)
  46. -> AttackComponent::CombatMode {
  47. if (value == "melee") {
  48. return AttackComponent::CombatMode::Melee;
  49. }
  50. if (value == "ranged") {
  51. return AttackComponent::CombatMode::Ranged;
  52. }
  53. return AttackComponent::CombatMode::Auto;
  54. }
  55. auto serialize_color(const std::array<float, 3> &color) -> QJsonArray {
  56. QJsonArray array;
  57. array.append(color[0]);
  58. array.append(color[1]);
  59. array.append(color[2]);
  60. return array;
  61. }
  62. void deserialize_color(const QJsonArray &array, std::array<float, 3> &color) {
  63. if (array.size() >= 3) {
  64. color[0] = static_cast<float>(array.at(0).toDouble());
  65. color[1] = static_cast<float>(array.at(1).toDouble());
  66. color[2] = static_cast<float>(array.at(2).toDouble());
  67. }
  68. }
  69. } // namespace
  70. auto Serialization::serialize_entity(const Entity *entity) -> QJsonObject {
  71. QJsonObject entity_obj;
  72. entity_obj["id"] = static_cast<qint64>(entity->get_id());
  73. if (const auto *transform = entity->get_component<TransformComponent>()) {
  74. QJsonObject transform_obj;
  75. transform_obj["pos_x"] = transform->position.x;
  76. transform_obj["pos_y"] = transform->position.y;
  77. transform_obj["pos_z"] = transform->position.z;
  78. transform_obj["rot_x"] = transform->rotation.x;
  79. transform_obj["rot_y"] = transform->rotation.y;
  80. transform_obj["rot_z"] = transform->rotation.z;
  81. transform_obj["scale_x"] = transform->scale.x;
  82. transform_obj["scale_y"] = transform->scale.y;
  83. transform_obj["scale_z"] = transform->scale.z;
  84. transform_obj["has_desired_yaw"] = transform->has_desired_yaw;
  85. transform_obj["desired_yaw"] = transform->desired_yaw;
  86. entity_obj["transform"] = transform_obj;
  87. }
  88. if (const auto *renderable = entity->get_component<RenderableComponent>()) {
  89. QJsonObject renderable_obj;
  90. renderable_obj["mesh_path"] = QString::fromStdString(renderable->mesh_path);
  91. renderable_obj["texture_path"] =
  92. QString::fromStdString(renderable->texture_path);
  93. if (!renderable->renderer_id.empty()) {
  94. renderable_obj["renderer_id"] =
  95. QString::fromStdString(renderable->renderer_id);
  96. }
  97. renderable_obj["visible"] = renderable->visible;
  98. renderable_obj["mesh"] = static_cast<int>(renderable->mesh);
  99. renderable_obj["color"] = serialize_color(renderable->color);
  100. entity_obj["renderable"] = renderable_obj;
  101. }
  102. if (const auto *unit = entity->get_component<UnitComponent>()) {
  103. QJsonObject unit_obj;
  104. unit_obj["health"] = unit->health;
  105. unit_obj["max_health"] = unit->max_health;
  106. unit_obj["speed"] = unit->speed;
  107. unit_obj["vision_range"] = unit->vision_range;
  108. unit_obj["unit_type"] = QString::fromStdString(
  109. Game::Units::spawn_typeToString(unit->spawn_type));
  110. unit_obj["owner_id"] = unit->owner_id;
  111. unit_obj["nation_id"] =
  112. Game::Systems::nation_id_to_qstring(unit->nation_id);
  113. entity_obj["unit"] = unit_obj;
  114. }
  115. if (const auto *movement = entity->get_component<MovementComponent>()) {
  116. QJsonObject movement_obj;
  117. movement_obj["has_target"] = movement->has_target;
  118. movement_obj["target_x"] = movement->target_x;
  119. movement_obj["target_y"] = movement->target_y;
  120. movement_obj["goal_x"] = movement->goal_x;
  121. movement_obj["goal_y"] = movement->goal_y;
  122. movement_obj["vx"] = movement->vx;
  123. movement_obj["vz"] = movement->vz;
  124. movement_obj["path_pending"] = movement->path_pending;
  125. movement_obj["pending_request_id"] =
  126. static_cast<qint64>(movement->pending_request_id);
  127. movement_obj["repath_cooldown"] = movement->repath_cooldown;
  128. movement_obj["last_goal_x"] = movement->last_goal_x;
  129. movement_obj["last_goal_y"] = movement->last_goal_y;
  130. movement_obj["time_since_last_path_request"] =
  131. movement->time_since_last_path_request;
  132. movement_obj["path_index"] = static_cast<int>(movement->path_index);
  133. QJsonArray path_array;
  134. for (const auto &waypoint : movement->path) {
  135. QJsonObject waypoint_obj;
  136. waypoint_obj["x"] = waypoint.first;
  137. waypoint_obj["y"] = waypoint.second;
  138. path_array.append(waypoint_obj);
  139. }
  140. movement_obj["path"] = path_array;
  141. entity_obj["movement"] = movement_obj;
  142. }
  143. if (const auto *attack = entity->get_component<AttackComponent>()) {
  144. QJsonObject attack_obj;
  145. attack_obj["range"] = attack->range;
  146. attack_obj["damage"] = attack->damage;
  147. attack_obj["cooldown"] = attack->cooldown;
  148. attack_obj["time_since_last"] = attack->time_since_last;
  149. attack_obj["melee_range"] = attack->melee_range;
  150. attack_obj["melee_damage"] = attack->melee_damage;
  151. attack_obj["melee_cooldown"] = attack->melee_cooldown;
  152. attack_obj["preferred_mode"] =
  153. combat_mode_to_string(attack->preferred_mode);
  154. attack_obj["current_mode"] = combat_mode_to_string(attack->current_mode);
  155. attack_obj["can_melee"] = attack->can_melee;
  156. attack_obj["can_ranged"] = attack->can_ranged;
  157. attack_obj["max_height_difference"] = attack->max_height_difference;
  158. attack_obj["in_melee_lock"] = attack->in_melee_lock;
  159. attack_obj["melee_lock_target_id"] =
  160. static_cast<qint64>(attack->melee_lock_target_id);
  161. entity_obj["attack"] = attack_obj;
  162. }
  163. if (const auto *attack_target =
  164. entity->get_component<AttackTargetComponent>()) {
  165. QJsonObject attack_target_obj;
  166. attack_target_obj["target_id"] =
  167. static_cast<qint64>(attack_target->target_id);
  168. attack_target_obj["should_chase"] = attack_target->should_chase;
  169. entity_obj["attack_target"] = attack_target_obj;
  170. }
  171. if (const auto *patrol = entity->get_component<PatrolComponent>()) {
  172. QJsonObject patrol_obj;
  173. patrol_obj["current_waypoint"] = static_cast<int>(patrol->current_waypoint);
  174. patrol_obj["patrolling"] = patrol->patrolling;
  175. QJsonArray waypoints_array;
  176. for (const auto &waypoint : patrol->waypoints) {
  177. QJsonObject waypoint_obj;
  178. waypoint_obj["x"] = waypoint.first;
  179. waypoint_obj["y"] = waypoint.second;
  180. waypoints_array.append(waypoint_obj);
  181. }
  182. patrol_obj["waypoints"] = waypoints_array;
  183. entity_obj["patrol"] = patrol_obj;
  184. }
  185. if (entity->get_component<BuildingComponent>() != nullptr) {
  186. entity_obj["building"] = true;
  187. }
  188. if (const auto *production = entity->get_component<ProductionComponent>()) {
  189. QJsonObject production_obj;
  190. production_obj["in_progress"] = production->in_progress;
  191. production_obj["build_time"] = production->build_time;
  192. production_obj["time_remaining"] = production->time_remaining;
  193. production_obj["produced_count"] = production->produced_count;
  194. production_obj["max_units"] = production->max_units;
  195. production_obj["product_type"] = QString::fromStdString(
  196. Game::Units::troop_typeToString(production->product_type));
  197. production_obj["rally_x"] = production->rally_x;
  198. production_obj["rally_z"] = production->rally_z;
  199. production_obj["rally_set"] = production->rally_set;
  200. production_obj["villager_cost"] = production->villager_cost;
  201. QJsonArray queue_array;
  202. for (const auto &queued : production->production_queue) {
  203. queue_array.append(
  204. QString::fromStdString(Game::Units::troop_typeToString(queued)));
  205. }
  206. production_obj["queue"] = queue_array;
  207. entity_obj["production"] = production_obj;
  208. }
  209. if (entity->get_component<AIControlledComponent>() != nullptr) {
  210. entity_obj["aiControlled"] = true;
  211. }
  212. if (const auto *capture = entity->get_component<CaptureComponent>()) {
  213. QJsonObject capture_obj;
  214. capture_obj["capturing_player_id"] = capture->capturing_player_id;
  215. capture_obj["capture_progress"] =
  216. static_cast<double>(capture->capture_progress);
  217. capture_obj["required_time"] = static_cast<double>(capture->required_time);
  218. capture_obj["is_being_captured"] = capture->is_being_captured;
  219. entity_obj["capture"] = capture_obj;
  220. }
  221. if (const auto *hold_mode = entity->get_component<HoldModeComponent>()) {
  222. QJsonObject hold_mode_obj;
  223. hold_mode_obj["active"] = hold_mode->active;
  224. hold_mode_obj["exit_cooldown"] =
  225. static_cast<double>(hold_mode->exit_cooldown);
  226. hold_mode_obj["stand_up_duration"] =
  227. static_cast<double>(hold_mode->stand_up_duration);
  228. entity_obj["hold_mode"] = hold_mode_obj;
  229. }
  230. if (const auto *guard_mode = entity->get_component<GuardModeComponent>()) {
  231. QJsonObject guard_mode_obj;
  232. guard_mode_obj["active"] = guard_mode->active;
  233. guard_mode_obj["guarded_entity_id"] =
  234. static_cast<qint64>(guard_mode->guarded_entity_id);
  235. guard_mode_obj["guard_position_x"] =
  236. static_cast<double>(guard_mode->guard_position_x);
  237. guard_mode_obj["guard_position_z"] =
  238. static_cast<double>(guard_mode->guard_position_z);
  239. guard_mode_obj["guard_radius"] =
  240. static_cast<double>(guard_mode->guard_radius);
  241. guard_mode_obj["returning_to_guard_position"] =
  242. guard_mode->returning_to_guard_position;
  243. guard_mode_obj["has_guard_target"] = guard_mode->has_guard_target;
  244. entity_obj["guard_mode"] = guard_mode_obj;
  245. }
  246. if (const auto *healer = entity->get_component<HealerComponent>()) {
  247. QJsonObject healer_obj;
  248. healer_obj["healing_range"] = static_cast<double>(healer->healing_range);
  249. healer_obj["healing_amount"] = healer->healing_amount;
  250. healer_obj["healing_cooldown"] =
  251. static_cast<double>(healer->healing_cooldown);
  252. healer_obj["time_since_last_heal"] =
  253. static_cast<double>(healer->time_since_last_heal);
  254. entity_obj["healer"] = healer_obj;
  255. }
  256. if (const auto *catapult =
  257. entity->get_component<CatapultLoadingComponent>()) {
  258. QJsonObject catapult_obj;
  259. catapult_obj["state"] = static_cast<int>(catapult->state);
  260. catapult_obj["loading_time"] = static_cast<double>(catapult->loading_time);
  261. catapult_obj["loading_duration"] =
  262. static_cast<double>(catapult->loading_duration);
  263. catapult_obj["firing_time"] = static_cast<double>(catapult->firing_time);
  264. catapult_obj["firing_duration"] =
  265. static_cast<double>(catapult->firing_duration);
  266. catapult_obj["target_id"] = static_cast<qint64>(catapult->target_id);
  267. catapult_obj["target_locked_x"] =
  268. static_cast<double>(catapult->target_locked_x);
  269. catapult_obj["target_locked_y"] =
  270. static_cast<double>(catapult->target_locked_y);
  271. catapult_obj["target_locked_z"] =
  272. static_cast<double>(catapult->target_locked_z);
  273. catapult_obj["target_position_locked"] = catapult->target_position_locked;
  274. entity_obj["catapult_loading"] = catapult_obj;
  275. }
  276. if (const auto *elephant = entity->get_component<ElephantComponent>()) {
  277. QJsonObject elephant_obj;
  278. elephant_obj["charge_state"] = static_cast<int>(elephant->charge_state);
  279. elephant_obj["charge_speed_multiplier"] =
  280. static_cast<double>(elephant->charge_speed_multiplier);
  281. elephant_obj["charge_duration"] =
  282. static_cast<double>(elephant->charge_duration);
  283. elephant_obj["charge_cooldown"] =
  284. static_cast<double>(elephant->charge_cooldown);
  285. elephant_obj["trample_radius"] =
  286. static_cast<double>(elephant->trample_radius);
  287. elephant_obj["trample_damage"] = elephant->trample_damage;
  288. elephant_obj["trample_damage_accumulator"] =
  289. static_cast<double>(elephant->trample_damage_accumulator);
  290. elephant_obj["is_panicked"] = elephant->is_panicked;
  291. elephant_obj["panic_duration"] =
  292. static_cast<double>(elephant->panic_duration);
  293. entity_obj["elephant"] = elephant_obj;
  294. }
  295. if (const auto *stomp_impact =
  296. entity->get_component<ElephantStompImpactComponent>()) {
  297. QJsonArray impacts_array;
  298. for (const auto &impact : stomp_impact->impacts) {
  299. QJsonObject impact_obj;
  300. impact_obj["x"] = static_cast<double>(impact.x);
  301. impact_obj["z"] = static_cast<double>(impact.z);
  302. impact_obj["time"] = static_cast<double>(impact.time);
  303. impacts_array.append(impact_obj);
  304. }
  305. entity_obj["elephant_stomp_impacts"] = impacts_array;
  306. }
  307. if (const auto *combat_state =
  308. entity->get_component<CombatStateComponent>()) {
  309. QJsonObject combat_state_obj;
  310. combat_state_obj["animation_state"] =
  311. static_cast<int>(combat_state->animation_state);
  312. combat_state_obj["state_time"] =
  313. static_cast<double>(combat_state->state_time);
  314. combat_state_obj["state_duration"] =
  315. static_cast<double>(combat_state->state_duration);
  316. combat_state_obj["attack_offset"] =
  317. static_cast<double>(combat_state->attack_offset);
  318. combat_state_obj["attack_variant"] =
  319. static_cast<int>(combat_state->attack_variant);
  320. combat_state_obj["is_hit_paused"] = combat_state->is_hit_paused;
  321. combat_state_obj["hit_pause_remaining"] =
  322. static_cast<double>(combat_state->hit_pause_remaining);
  323. entity_obj["combat_state"] = combat_state_obj;
  324. }
  325. if (const auto *hit_feedback =
  326. entity->get_component<HitFeedbackComponent>()) {
  327. QJsonObject hit_feedback_obj;
  328. hit_feedback_obj["is_reacting"] = hit_feedback->is_reacting;
  329. hit_feedback_obj["reaction_time"] =
  330. static_cast<double>(hit_feedback->reaction_time);
  331. hit_feedback_obj["reaction_intensity"] =
  332. static_cast<double>(hit_feedback->reaction_intensity);
  333. hit_feedback_obj["knockback_x"] =
  334. static_cast<double>(hit_feedback->knockback_x);
  335. hit_feedback_obj["knockback_z"] =
  336. static_cast<double>(hit_feedback->knockback_z);
  337. entity_obj["hit_feedback"] = hit_feedback_obj;
  338. }
  339. if (const auto *builder =
  340. entity->get_component<BuilderProductionComponent>()) {
  341. QJsonObject builder_obj;
  342. builder_obj["in_progress"] = builder->in_progress;
  343. builder_obj["build_time"] = static_cast<double>(builder->build_time);
  344. builder_obj["time_remaining"] =
  345. static_cast<double>(builder->time_remaining);
  346. builder_obj["product_type"] = QString::fromStdString(builder->product_type);
  347. builder_obj["construction_complete"] = builder->construction_complete;
  348. builder_obj["has_construction_site"] = builder->has_construction_site;
  349. builder_obj["construction_site_x"] =
  350. static_cast<double>(builder->construction_site_x);
  351. builder_obj["construction_site_z"] =
  352. static_cast<double>(builder->construction_site_z);
  353. builder_obj["at_construction_site"] = builder->at_construction_site;
  354. builder_obj["is_placement_preview"] = builder->is_placement_preview;
  355. builder_obj["bypass_movement_active"] = builder->bypass_movement_active;
  356. builder_obj["bypass_target_x"] =
  357. static_cast<double>(builder->bypass_target_x);
  358. builder_obj["bypass_target_z"] =
  359. static_cast<double>(builder->bypass_target_z);
  360. entity_obj["builder_production"] = builder_obj;
  361. }
  362. if (const auto *formation = entity->get_component<FormationModeComponent>()) {
  363. QJsonObject formation_obj;
  364. formation_obj["active"] = formation->active;
  365. formation_obj["formation_center_x"] =
  366. static_cast<double>(formation->formation_center_x);
  367. formation_obj["formation_center_z"] =
  368. static_cast<double>(formation->formation_center_z);
  369. entity_obj["formation_mode"] = formation_obj;
  370. }
  371. if (const auto *stamina = entity->get_component<StaminaComponent>()) {
  372. QJsonObject stamina_obj;
  373. stamina_obj["stamina"] = static_cast<double>(stamina->stamina);
  374. stamina_obj["max_stamina"] = static_cast<double>(stamina->max_stamina);
  375. stamina_obj["regen_rate"] = static_cast<double>(stamina->regen_rate);
  376. stamina_obj["depletion_rate"] =
  377. static_cast<double>(stamina->depletion_rate);
  378. stamina_obj["is_running"] = stamina->is_running;
  379. stamina_obj["run_requested"] = stamina->run_requested;
  380. entity_obj["stamina"] = stamina_obj;
  381. }
  382. if (const auto *terrain_context =
  383. entity->get_component<TerrainContextComponent>()) {
  384. QJsonObject terrain_context_obj;
  385. terrain_context_obj["is_on_bridge"] = terrain_context->is_on_bridge;
  386. terrain_context_obj["is_at_hill_entrance"] =
  387. terrain_context->is_at_hill_entrance;
  388. terrain_context_obj["audio_cooldown"] =
  389. static_cast<double>(terrain_context->audio_cooldown);
  390. entity_obj["terrain_context"] = terrain_context_obj;
  391. }
  392. if (const auto *home = entity->get_component<HomeComponent>()) {
  393. QJsonObject home_obj;
  394. home_obj["population_contribution"] = home->population_contribution;
  395. home_obj["nearest_barracks_id"] =
  396. static_cast<qint64>(home->nearest_barracks_id);
  397. home_obj["update_cooldown"] = static_cast<double>(home->update_cooldown);
  398. entity_obj["home"] = home_obj;
  399. }
  400. return entity_obj;
  401. }
  402. void Serialization::deserialize_entity(Entity *entity,
  403. const QJsonObject &json) {
  404. if (json.contains("transform")) {
  405. const auto transform_obj = json["transform"].toObject();
  406. auto *transform = entity->add_component<TransformComponent>();
  407. transform->position.x =
  408. static_cast<float>(transform_obj["pos_x"].toDouble());
  409. transform->position.y =
  410. static_cast<float>(transform_obj["pos_y"].toDouble());
  411. transform->position.z =
  412. static_cast<float>(transform_obj["pos_z"].toDouble());
  413. transform->rotation.x =
  414. static_cast<float>(transform_obj["rot_x"].toDouble());
  415. transform->rotation.y =
  416. static_cast<float>(transform_obj["rot_y"].toDouble());
  417. transform->rotation.z =
  418. static_cast<float>(transform_obj["rot_z"].toDouble());
  419. transform->scale.x =
  420. static_cast<float>(transform_obj["scale_x"].toDouble());
  421. transform->scale.y =
  422. static_cast<float>(transform_obj["scale_y"].toDouble());
  423. transform->scale.z =
  424. static_cast<float>(transform_obj["scale_z"].toDouble());
  425. transform->has_desired_yaw = transform_obj["has_desired_yaw"].toBool(false);
  426. transform->desired_yaw =
  427. static_cast<float>(transform_obj["desired_yaw"].toDouble());
  428. }
  429. if (json.contains("renderable")) {
  430. const auto renderable_obj = json["renderable"].toObject();
  431. auto *renderable = entity->add_component<RenderableComponent>("", "");
  432. renderable->mesh_path =
  433. renderable_obj["mesh_path"].toString().toStdString();
  434. renderable->texture_path =
  435. renderable_obj["texture_path"].toString().toStdString();
  436. renderable->renderer_id =
  437. renderable_obj["renderer_id"].toString().toStdString();
  438. renderable->visible = renderable_obj["visible"].toBool(true);
  439. renderable->mesh =
  440. static_cast<RenderableComponent::MeshKind>(renderable_obj["mesh"].toInt(
  441. static_cast<int>(RenderableComponent::MeshKind::Cube)));
  442. if (renderable_obj.contains("color")) {
  443. deserialize_color(renderable_obj["color"].toArray(), renderable->color);
  444. }
  445. }
  446. if (json.contains("unit")) {
  447. const auto unit_obj = json["unit"].toObject();
  448. auto *unit = entity->add_component<UnitComponent>();
  449. unit->health = unit_obj["health"].toInt(Defaults::kUnitDefaultHealth);
  450. unit->max_health =
  451. unit_obj["max_health"].toInt(Defaults::kUnitDefaultHealth);
  452. unit->speed = static_cast<float>(unit_obj["speed"].toDouble());
  453. unit->vision_range = static_cast<float>(unit_obj["vision_range"].toDouble(
  454. static_cast<double>(Defaults::kUnitDefaultVisionRange)));
  455. QString const unit_type_str = unit_obj["unit_type"].toString();
  456. Game::Units::SpawnType spawn_type;
  457. if (Game::Units::tryParseSpawnType(unit_type_str, spawn_type)) {
  458. unit->spawn_type = spawn_type;
  459. } else {
  460. qWarning() << "Unknown spawn type in save file:" << unit_type_str
  461. << "- defaulting to Archer";
  462. unit->spawn_type = Game::Units::SpawnType::Archer;
  463. }
  464. unit->owner_id = unit_obj["owner_id"].toInt(0);
  465. if (unit_obj.contains("nation_id")) {
  466. const QString nation_str = unit_obj["nation_id"].toString();
  467. Game::Systems::NationID nation_id;
  468. if (Game::Systems::try_parse_nation_id(nation_str, nation_id)) {
  469. unit->nation_id = nation_id;
  470. } else {
  471. qWarning() << "Unknown nation ID in save file:" << nation_str
  472. << "- using default";
  473. unit->nation_id = Game::Systems::NationID::RomanRepublic;
  474. }
  475. }
  476. }
  477. if (json.contains("movement")) {
  478. const auto movement_obj = json["movement"].toObject();
  479. auto *movement = entity->add_component<MovementComponent>();
  480. movement->has_target = movement_obj["has_target"].toBool(false);
  481. movement->target_x =
  482. static_cast<float>(movement_obj["target_x"].toDouble());
  483. movement->target_y =
  484. static_cast<float>(movement_obj["target_y"].toDouble());
  485. movement->goal_x = static_cast<float>(movement_obj["goal_x"].toDouble());
  486. movement->goal_y = static_cast<float>(movement_obj["goal_y"].toDouble());
  487. movement->vx = static_cast<float>(movement_obj["vx"].toDouble());
  488. movement->vz = static_cast<float>(movement_obj["vz"].toDouble());
  489. movement->path_pending = movement_obj["path_pending"].toBool(false);
  490. movement->pending_request_id = static_cast<std::uint64_t>(
  491. movement_obj["pending_request_id"].toVariant().toULongLong());
  492. movement->repath_cooldown =
  493. static_cast<float>(movement_obj["repath_cooldown"].toDouble());
  494. movement->last_goal_x =
  495. static_cast<float>(movement_obj["last_goal_x"].toDouble());
  496. movement->last_goal_y =
  497. static_cast<float>(movement_obj["last_goal_y"].toDouble());
  498. movement->time_since_last_path_request = static_cast<float>(
  499. movement_obj["time_since_last_path_request"].toDouble());
  500. movement->clear_path();
  501. const auto path_array = movement_obj["path"].toArray();
  502. movement->path.reserve(path_array.size());
  503. for (const auto &value : path_array) {
  504. const auto waypoint_obj = value.toObject();
  505. movement->path.emplace_back(
  506. static_cast<float>(waypoint_obj["x"].toDouble()),
  507. static_cast<float>(waypoint_obj["y"].toDouble()));
  508. }
  509. if (movement_obj.contains("path_index")) {
  510. movement->path_index =
  511. static_cast<std::size_t>(movement_obj["path_index"].toInt());
  512. }
  513. movement->validate_path_index();
  514. }
  515. if (json.contains("attack")) {
  516. const auto attack_obj = json["attack"].toObject();
  517. auto *attack = entity->add_component<AttackComponent>();
  518. attack->range = static_cast<float>(attack_obj["range"].toDouble());
  519. attack->damage = attack_obj["damage"].toInt(0);
  520. attack->cooldown = static_cast<float>(attack_obj["cooldown"].toDouble());
  521. attack->time_since_last =
  522. static_cast<float>(attack_obj["time_since_last"].toDouble());
  523. attack->melee_range = static_cast<float>(attack_obj["melee_range"].toDouble(
  524. static_cast<double>(Defaults::kAttackMeleeRange)));
  525. attack->melee_damage = attack_obj["melee_damage"].toInt(0);
  526. attack->melee_cooldown =
  527. static_cast<float>(attack_obj["melee_cooldown"].toDouble());
  528. attack->preferred_mode =
  529. combat_mode_from_string(attack_obj["preferred_mode"].toString());
  530. attack->current_mode =
  531. combat_mode_from_string(attack_obj["current_mode"].toString());
  532. attack->can_melee = attack_obj["can_melee"].toBool(true);
  533. attack->can_ranged = attack_obj["can_ranged"].toBool(false);
  534. attack->max_height_difference =
  535. static_cast<float>(attack_obj["max_height_difference"].toDouble(
  536. static_cast<double>(Defaults::kAttackHeightTolerance)));
  537. attack->in_melee_lock = attack_obj["in_melee_lock"].toBool(false);
  538. attack->melee_lock_target_id = static_cast<EntityID>(
  539. attack_obj["melee_lock_target_id"].toVariant().toULongLong());
  540. }
  541. if (json.contains("attack_target")) {
  542. const auto attack_target_obj = json["attack_target"].toObject();
  543. auto *attack_target = entity->add_component<AttackTargetComponent>();
  544. attack_target->target_id = static_cast<EntityID>(
  545. attack_target_obj["target_id"].toVariant().toULongLong());
  546. attack_target->should_chase =
  547. attack_target_obj["should_chase"].toBool(false);
  548. }
  549. if (json.contains("patrol")) {
  550. const auto patrol_obj = json["patrol"].toObject();
  551. auto *patrol = entity->add_component<PatrolComponent>();
  552. patrol->current_waypoint = static_cast<size_t>(
  553. std::max(0, patrol_obj["current_waypoint"].toInt()));
  554. patrol->patrolling = patrol_obj["patrolling"].toBool(false);
  555. patrol->waypoints.clear();
  556. const auto waypoints_array = patrol_obj["waypoints"].toArray();
  557. patrol->waypoints.reserve(waypoints_array.size());
  558. for (const auto &value : waypoints_array) {
  559. const auto waypoint_obj = value.toObject();
  560. patrol->waypoints.emplace_back(
  561. static_cast<float>(waypoint_obj["x"].toDouble()),
  562. static_cast<float>(waypoint_obj["y"].toDouble()));
  563. }
  564. }
  565. if (json.contains("building") && json["building"].toBool()) {
  566. entity->add_component<BuildingComponent>();
  567. }
  568. if (json.contains("production")) {
  569. const auto production_obj = json["production"].toObject();
  570. auto *production = entity->add_component<ProductionComponent>();
  571. production->in_progress = production_obj["in_progress"].toBool(false);
  572. production->build_time =
  573. static_cast<float>(production_obj["build_time"].toDouble());
  574. production->time_remaining =
  575. static_cast<float>(production_obj["time_remaining"].toDouble());
  576. production->produced_count = production_obj["produced_count"].toInt(0);
  577. production->max_units = production_obj["max_units"].toInt(0);
  578. production->product_type = Game::Units::troop_typeFromString(
  579. production_obj["product_type"].toString().toStdString());
  580. production->rally_x =
  581. static_cast<float>(production_obj["rally_x"].toDouble());
  582. production->rally_z =
  583. static_cast<float>(production_obj["rally_z"].toDouble());
  584. production->rally_set = production_obj["rally_set"].toBool(false);
  585. production->villager_cost = production_obj["villager_cost"].toInt(1);
  586. production->production_queue.clear();
  587. const auto queue_array = production_obj["queue"].toArray();
  588. production->production_queue.reserve(queue_array.size());
  589. for (const auto &value : queue_array) {
  590. production->production_queue.push_back(
  591. Game::Units::troop_typeFromString(value.toString().toStdString()));
  592. }
  593. }
  594. if (json.contains("aiControlled") && json["aiControlled"].toBool()) {
  595. entity->add_component<AIControlledComponent>();
  596. }
  597. if (json.contains("capture")) {
  598. const auto capture_obj = json["capture"].toObject();
  599. auto *capture = entity->add_component<CaptureComponent>();
  600. capture->capturing_player_id = capture_obj["capturing_player_id"].toInt(-1);
  601. capture->capture_progress =
  602. static_cast<float>(capture_obj["capture_progress"].toDouble(0.0));
  603. capture->required_time =
  604. static_cast<float>(capture_obj["required_time"].toDouble(
  605. static_cast<double>(Defaults::kCaptureRequiredTime)));
  606. capture->is_being_captured = capture_obj["is_being_captured"].toBool(false);
  607. }
  608. if (json.contains("hold_mode")) {
  609. const auto hold_mode_obj = json["hold_mode"].toObject();
  610. auto *hold_mode = entity->add_component<HoldModeComponent>();
  611. hold_mode->active = hold_mode_obj["active"].toBool(true);
  612. hold_mode->exit_cooldown =
  613. static_cast<float>(hold_mode_obj["exit_cooldown"].toDouble(0.0));
  614. hold_mode->stand_up_duration =
  615. static_cast<float>(hold_mode_obj["stand_up_duration"].toDouble(
  616. static_cast<double>(Defaults::kHoldStandUpDuration)));
  617. }
  618. if (json.contains("guard_mode")) {
  619. const auto guard_mode_obj = json["guard_mode"].toObject();
  620. auto *guard_mode = entity->add_component<GuardModeComponent>();
  621. guard_mode->active = guard_mode_obj["active"].toBool(true);
  622. guard_mode->guarded_entity_id = static_cast<EntityID>(
  623. guard_mode_obj["guarded_entity_id"].toVariant().toULongLong());
  624. guard_mode->guard_position_x =
  625. static_cast<float>(guard_mode_obj["guard_position_x"].toDouble(0.0));
  626. guard_mode->guard_position_z =
  627. static_cast<float>(guard_mode_obj["guard_position_z"].toDouble(0.0));
  628. guard_mode->guard_radius =
  629. static_cast<float>(guard_mode_obj["guard_radius"].toDouble(
  630. static_cast<double>(Defaults::kGuardDefaultRadius)));
  631. guard_mode->returning_to_guard_position =
  632. guard_mode_obj["returning_to_guard_position"].toBool(false);
  633. guard_mode->has_guard_target =
  634. guard_mode_obj["has_guard_target"].toBool(false);
  635. }
  636. if (json.contains("healer")) {
  637. const auto healer_obj = json["healer"].toObject();
  638. auto *healer = entity->add_component<HealerComponent>();
  639. healer->healing_range =
  640. static_cast<float>(healer_obj["healing_range"].toDouble(8.0));
  641. healer->healing_amount = healer_obj["healing_amount"].toInt(5);
  642. healer->healing_cooldown =
  643. static_cast<float>(healer_obj["healing_cooldown"].toDouble(2.0));
  644. healer->time_since_last_heal =
  645. static_cast<float>(healer_obj["time_since_last_heal"].toDouble(0.0));
  646. }
  647. if (json.contains("catapult_loading")) {
  648. const auto catapult_obj = json["catapult_loading"].toObject();
  649. auto *catapult = entity->add_component<CatapultLoadingComponent>();
  650. catapult->state = static_cast<CatapultLoadingComponent::LoadingState>(
  651. catapult_obj["state"].toInt(
  652. static_cast<int>(CatapultLoadingComponent::LoadingState::Idle)));
  653. catapult->loading_time =
  654. static_cast<float>(catapult_obj["loading_time"].toDouble(0.0));
  655. catapult->loading_duration =
  656. static_cast<float>(catapult_obj["loading_duration"].toDouble(2.0));
  657. catapult->firing_time =
  658. static_cast<float>(catapult_obj["firing_time"].toDouble(0.0));
  659. catapult->firing_duration =
  660. static_cast<float>(catapult_obj["firing_duration"].toDouble(0.5));
  661. catapult->target_id = static_cast<EntityID>(
  662. catapult_obj["target_id"].toVariant().toULongLong());
  663. catapult->target_locked_x =
  664. static_cast<float>(catapult_obj["target_locked_x"].toDouble(0.0));
  665. catapult->target_locked_y =
  666. static_cast<float>(catapult_obj["target_locked_y"].toDouble(0.0));
  667. catapult->target_locked_z =
  668. static_cast<float>(catapult_obj["target_locked_z"].toDouble(0.0));
  669. catapult->target_position_locked =
  670. catapult_obj["target_position_locked"].toBool(false);
  671. }
  672. if (json.contains("elephant")) {
  673. const auto elephant_obj = json["elephant"].toObject();
  674. auto *elephant = entity->add_component<ElephantComponent>();
  675. elephant->charge_state = static_cast<ElephantComponent::ChargeState>(
  676. elephant_obj["charge_state"].toInt(
  677. static_cast<int>(ElephantComponent::ChargeState::Idle)));
  678. elephant->charge_speed_multiplier = static_cast<float>(
  679. elephant_obj["charge_speed_multiplier"].toDouble(1.8));
  680. elephant->charge_duration =
  681. static_cast<float>(elephant_obj["charge_duration"].toDouble(0.0));
  682. elephant->charge_cooldown =
  683. static_cast<float>(elephant_obj["charge_cooldown"].toDouble(0.0));
  684. elephant->trample_radius =
  685. static_cast<float>(elephant_obj["trample_radius"].toDouble(2.5));
  686. elephant->trample_damage = elephant_obj["trample_damage"].toInt(40);
  687. elephant->trample_damage_accumulator = static_cast<float>(
  688. elephant_obj["trample_damage_accumulator"].toDouble(0.0));
  689. elephant->is_panicked = elephant_obj["is_panicked"].toBool(false);
  690. elephant->panic_duration =
  691. static_cast<float>(elephant_obj["panic_duration"].toDouble(0.0));
  692. }
  693. if (json.contains("elephant_stomp_impacts")) {
  694. const auto impacts_array = json["elephant_stomp_impacts"].toArray();
  695. auto *stomp_impact = entity->add_component<ElephantStompImpactComponent>();
  696. stomp_impact->impacts.clear();
  697. stomp_impact->impacts.reserve(impacts_array.size());
  698. for (const auto &value : impacts_array) {
  699. const auto impact_obj = value.toObject();
  700. ElephantStompImpactComponent::ImpactRecord impact;
  701. impact.x = static_cast<float>(impact_obj["x"].toDouble(0.0));
  702. impact.z = static_cast<float>(impact_obj["z"].toDouble(0.0));
  703. impact.time = static_cast<float>(impact_obj["time"].toDouble(0.0));
  704. stomp_impact->impacts.push_back(impact);
  705. }
  706. }
  707. if (json.contains("combat_state")) {
  708. const auto combat_state_obj = json["combat_state"].toObject();
  709. auto *combat_state = entity->add_component<CombatStateComponent>();
  710. combat_state->animation_state = static_cast<CombatAnimationState>(
  711. combat_state_obj["animation_state"].toInt(
  712. static_cast<int>(CombatAnimationState::Idle)));
  713. combat_state->state_time =
  714. static_cast<float>(combat_state_obj["state_time"].toDouble(0.0));
  715. combat_state->state_duration =
  716. static_cast<float>(combat_state_obj["state_duration"].toDouble(0.0));
  717. combat_state->attack_offset =
  718. static_cast<float>(combat_state_obj["attack_offset"].toDouble(0.0));
  719. combat_state->attack_variant =
  720. static_cast<std::uint8_t>(combat_state_obj["attack_variant"].toInt(0));
  721. combat_state->is_hit_paused =
  722. combat_state_obj["is_hit_paused"].toBool(false);
  723. combat_state->hit_pause_remaining = static_cast<float>(
  724. combat_state_obj["hit_pause_remaining"].toDouble(0.0));
  725. }
  726. if (json.contains("hit_feedback")) {
  727. const auto hit_feedback_obj = json["hit_feedback"].toObject();
  728. auto *hit_feedback = entity->add_component<HitFeedbackComponent>();
  729. hit_feedback->is_reacting = hit_feedback_obj["is_reacting"].toBool(false);
  730. hit_feedback->reaction_time =
  731. static_cast<float>(hit_feedback_obj["reaction_time"].toDouble(0.0));
  732. hit_feedback->reaction_intensity = static_cast<float>(
  733. hit_feedback_obj["reaction_intensity"].toDouble(0.0));
  734. hit_feedback->knockback_x =
  735. static_cast<float>(hit_feedback_obj["knockback_x"].toDouble(0.0));
  736. hit_feedback->knockback_z =
  737. static_cast<float>(hit_feedback_obj["knockback_z"].toDouble(0.0));
  738. }
  739. if (json.contains("builder_production")) {
  740. const auto builder_obj = json["builder_production"].toObject();
  741. auto *builder = entity->add_component<BuilderProductionComponent>();
  742. builder->in_progress = builder_obj["in_progress"].toBool(false);
  743. builder->build_time =
  744. static_cast<float>(builder_obj["build_time"].toDouble(10.0));
  745. builder->time_remaining =
  746. static_cast<float>(builder_obj["time_remaining"].toDouble(0.0));
  747. builder->product_type =
  748. builder_obj["product_type"].toString().toStdString();
  749. builder->construction_complete =
  750. builder_obj["construction_complete"].toBool(false);
  751. builder->has_construction_site =
  752. builder_obj["has_construction_site"].toBool(false);
  753. builder->construction_site_x =
  754. static_cast<float>(builder_obj["construction_site_x"].toDouble(0.0));
  755. builder->construction_site_z =
  756. static_cast<float>(builder_obj["construction_site_z"].toDouble(0.0));
  757. builder->at_construction_site =
  758. builder_obj["at_construction_site"].toBool(false);
  759. builder->is_placement_preview =
  760. builder_obj["is_placement_preview"].toBool(false);
  761. builder->bypass_movement_active =
  762. builder_obj["bypass_movement_active"].toBool(false);
  763. builder->bypass_target_x =
  764. static_cast<float>(builder_obj["bypass_target_x"].toDouble(0.0));
  765. builder->bypass_target_z =
  766. static_cast<float>(builder_obj["bypass_target_z"].toDouble(0.0));
  767. }
  768. if (json.contains("formation_mode")) {
  769. const auto formation_obj = json["formation_mode"].toObject();
  770. auto *formation = entity->add_component<FormationModeComponent>();
  771. formation->active = formation_obj["active"].toBool(false);
  772. formation->formation_center_x =
  773. static_cast<float>(formation_obj["formation_center_x"].toDouble(0.0));
  774. formation->formation_center_z =
  775. static_cast<float>(formation_obj["formation_center_z"].toDouble(0.0));
  776. }
  777. if (json.contains("stamina")) {
  778. const auto stamina_obj = json["stamina"].toObject();
  779. auto *stamina = entity->add_component<StaminaComponent>();
  780. stamina->stamina = static_cast<float>(stamina_obj["stamina"].toDouble(
  781. static_cast<double>(StaminaComponent::kDefaultMaxStamina)));
  782. stamina->max_stamina =
  783. static_cast<float>(stamina_obj["max_stamina"].toDouble(
  784. static_cast<double>(StaminaComponent::kDefaultMaxStamina)));
  785. stamina->regen_rate = static_cast<float>(stamina_obj["regen_rate"].toDouble(
  786. static_cast<double>(StaminaComponent::kDefaultRegenRate)));
  787. stamina->depletion_rate =
  788. static_cast<float>(stamina_obj["depletion_rate"].toDouble(
  789. static_cast<double>(StaminaComponent::kDefaultDepletionRate)));
  790. stamina->is_running = stamina_obj["is_running"].toBool(false);
  791. stamina->run_requested = stamina_obj["run_requested"].toBool(false);
  792. }
  793. if (json.contains("terrain_context")) {
  794. const auto terrain_context_obj = json["terrain_context"].toObject();
  795. auto *terrain_context = entity->add_component<TerrainContextComponent>();
  796. terrain_context->is_on_bridge =
  797. terrain_context_obj["is_on_bridge"].toBool(false);
  798. terrain_context->is_at_hill_entrance =
  799. terrain_context_obj["is_at_hill_entrance"].toBool(false);
  800. terrain_context->audio_cooldown =
  801. static_cast<float>(terrain_context_obj["audio_cooldown"].toDouble(0.0));
  802. }
  803. if (json.contains("home")) {
  804. const auto home_obj = json["home"].toObject();
  805. auto *home = entity->add_component<HomeComponent>();
  806. home->population_contribution =
  807. home_obj["population_contribution"].toInt(50);
  808. home->nearest_barracks_id = static_cast<EntityID>(
  809. home_obj["nearest_barracks_id"].toVariant().toULongLong());
  810. home->update_cooldown =
  811. static_cast<float>(home_obj["update_cooldown"].toDouble(0.0));
  812. }
  813. }
  814. auto Serialization::serialize_terrain(
  815. const Game::Map::TerrainHeightMap *height_map,
  816. const Game::Map::BiomeSettings &biome,
  817. const std::vector<Game::Map::RoadSegment> &roads) -> QJsonObject {
  818. QJsonObject terrain_obj;
  819. if (height_map == nullptr) {
  820. return terrain_obj;
  821. }
  822. terrain_obj["width"] = height_map->getWidth();
  823. terrain_obj["height"] = height_map->getHeight();
  824. terrain_obj["tile_size"] = height_map->getTileSize();
  825. QJsonArray heights_array;
  826. const auto &heights = height_map->getHeightData();
  827. for (float const h : heights) {
  828. heights_array.append(h);
  829. }
  830. terrain_obj["heights"] = heights_array;
  831. QJsonArray terrain_types_array;
  832. const auto &terrain_types = height_map->getTerrainTypes();
  833. for (auto type : terrain_types) {
  834. terrain_types_array.append(static_cast<int>(type));
  835. }
  836. terrain_obj["terrain_types"] = terrain_types_array;
  837. QJsonArray rivers_array;
  838. const auto &rivers = height_map->getRiverSegments();
  839. for (const auto &river : rivers) {
  840. QJsonObject river_obj;
  841. river_obj["startX"] = river.start.x();
  842. river_obj["startY"] = river.start.y();
  843. river_obj["startZ"] = river.start.z();
  844. river_obj["endX"] = river.end.x();
  845. river_obj["endY"] = river.end.y();
  846. river_obj["endZ"] = river.end.z();
  847. river_obj["width"] = river.width;
  848. rivers_array.append(river_obj);
  849. }
  850. terrain_obj["rivers"] = rivers_array;
  851. QJsonArray bridges_array;
  852. const auto &bridges = height_map->getBridges();
  853. for (const auto &bridge : bridges) {
  854. QJsonObject bridge_obj;
  855. bridge_obj["startX"] = bridge.start.x();
  856. bridge_obj["startY"] = bridge.start.y();
  857. bridge_obj["startZ"] = bridge.start.z();
  858. bridge_obj["endX"] = bridge.end.x();
  859. bridge_obj["endY"] = bridge.end.y();
  860. bridge_obj["endZ"] = bridge.end.z();
  861. bridge_obj["width"] = bridge.width;
  862. bridge_obj["height"] = bridge.height;
  863. bridges_array.append(bridge_obj);
  864. }
  865. terrain_obj["bridges"] = bridges_array;
  866. QJsonArray roads_array;
  867. for (const auto &road : roads) {
  868. QJsonObject road_obj;
  869. road_obj["startX"] = road.start.x();
  870. road_obj["startY"] = road.start.y();
  871. road_obj["startZ"] = road.start.z();
  872. road_obj["endX"] = road.end.x();
  873. road_obj["endY"] = road.end.y();
  874. road_obj["endZ"] = road.end.z();
  875. road_obj["width"] = road.width;
  876. road_obj["style"] = road.style;
  877. roads_array.append(road_obj);
  878. }
  879. terrain_obj["roads"] = roads_array;
  880. QJsonObject biome_obj;
  881. biome_obj["grassPrimaryR"] = biome.grass_primary.x();
  882. biome_obj["grassPrimaryG"] = biome.grass_primary.y();
  883. biome_obj["grassPrimaryB"] = biome.grass_primary.z();
  884. biome_obj["grassSecondaryR"] = biome.grass_secondary.x();
  885. biome_obj["grassSecondaryG"] = biome.grass_secondary.y();
  886. biome_obj["grassSecondaryB"] = biome.grass_secondary.z();
  887. biome_obj["grassDryR"] = biome.grass_dry.x();
  888. biome_obj["grassDryG"] = biome.grass_dry.y();
  889. biome_obj["grassDryB"] = biome.grass_dry.z();
  890. biome_obj["soilColorR"] = biome.soil_color.x();
  891. biome_obj["soilColorG"] = biome.soil_color.y();
  892. biome_obj["soilColorB"] = biome.soil_color.z();
  893. biome_obj["rockLowR"] = biome.rock_low.x();
  894. biome_obj["rockLowG"] = biome.rock_low.y();
  895. biome_obj["rockLowB"] = biome.rock_low.z();
  896. biome_obj["rockHighR"] = biome.rock_high.x();
  897. biome_obj["rockHighG"] = biome.rock_high.y();
  898. biome_obj["rockHighB"] = biome.rock_high.z();
  899. biome_obj["patchDensity"] = biome.patch_density;
  900. biome_obj["patchJitter"] = biome.patch_jitter;
  901. biome_obj["backgroundBladeDensity"] = biome.background_blade_density;
  902. biome_obj["bladeHeightMin"] = biome.blade_height_min;
  903. biome_obj["bladeHeightMax"] = biome.blade_height_max;
  904. biome_obj["bladeWidthMin"] = biome.blade_width_min;
  905. biome_obj["bladeWidthMax"] = biome.blade_width_max;
  906. biome_obj["sway_strength"] = biome.sway_strength;
  907. biome_obj["sway_speed"] = biome.sway_speed;
  908. biome_obj["heightNoiseAmplitude"] = biome.height_noise_amplitude;
  909. biome_obj["heightNoiseFrequency"] = biome.height_noise_frequency;
  910. biome_obj["terrainMacroNoiseScale"] = biome.terrain_macro_noise_scale;
  911. biome_obj["terrainDetailNoiseScale"] = biome.terrain_detail_noise_scale;
  912. biome_obj["terrainSoilHeight"] = biome.terrain_soil_height;
  913. biome_obj["terrainSoilSharpness"] = biome.terrain_soil_sharpness;
  914. biome_obj["terrainRockThreshold"] = biome.terrain_rock_threshold;
  915. biome_obj["terrainRockSharpness"] = biome.terrain_rock_sharpness;
  916. biome_obj["terrainAmbientBoost"] = biome.terrain_ambient_boost;
  917. biome_obj["terrainRockDetailStrength"] = biome.terrain_rock_detail_strength;
  918. biome_obj["backgroundSwayVariance"] = biome.background_sway_variance;
  919. biome_obj["backgroundScatterRadius"] = biome.background_scatter_radius;
  920. biome_obj["plant_density"] = biome.plant_density;
  921. biome_obj["spawnEdgePadding"] = biome.spawn_edge_padding;
  922. biome_obj["seed"] = static_cast<qint64>(biome.seed);
  923. terrain_obj["biome"] = biome_obj;
  924. return terrain_obj;
  925. }
  926. void Serialization::deserialize_terrain(
  927. Game::Map::TerrainHeightMap *height_map, Game::Map::BiomeSettings &biome,
  928. std::vector<Game::Map::RoadSegment> &roads, const QJsonObject &json) {
  929. if ((height_map == nullptr) || json.isEmpty()) {
  930. return;
  931. }
  932. if (json.contains("biome")) {
  933. const auto biome_obj = json["biome"].toObject();
  934. const Game::Map::BiomeSettings default_biome{};
  935. const auto read_color = [&](const QString &base,
  936. const QVector3D &fallback) -> QVector3D {
  937. const auto r_key = base + QStringLiteral("R");
  938. const auto g_key = base + QStringLiteral("G");
  939. const auto b_key = base + QStringLiteral("B");
  940. const float r = static_cast<float>(
  941. biome_obj[r_key].toDouble(static_cast<double>(fallback.x())));
  942. const float g = static_cast<float>(
  943. biome_obj[g_key].toDouble(static_cast<double>(fallback.y())));
  944. const float b = static_cast<float>(
  945. biome_obj[b_key].toDouble(static_cast<double>(fallback.z())));
  946. return {r, g, b};
  947. };
  948. biome.grass_primary =
  949. read_color(QStringLiteral("grassPrimary"), default_biome.grass_primary);
  950. biome.grass_secondary = read_color(QStringLiteral("grassSecondary"),
  951. default_biome.grass_secondary);
  952. biome.grass_dry =
  953. read_color(QStringLiteral("grassDry"), default_biome.grass_dry);
  954. biome.soil_color =
  955. read_color(QStringLiteral("soilColor"), default_biome.soil_color);
  956. biome.rock_low =
  957. read_color(QStringLiteral("rockLow"), default_biome.rock_low);
  958. biome.rock_high =
  959. read_color(QStringLiteral("rockHigh"), default_biome.rock_high);
  960. biome.patch_density = static_cast<float>(
  961. biome_obj["patchDensity"].toDouble(default_biome.patch_density));
  962. biome.patch_jitter = static_cast<float>(
  963. biome_obj["patchJitter"].toDouble(default_biome.patch_jitter));
  964. biome.background_blade_density =
  965. static_cast<float>(biome_obj["backgroundBladeDensity"].toDouble(
  966. default_biome.background_blade_density));
  967. biome.blade_height_min = static_cast<float>(
  968. biome_obj["bladeHeightMin"].toDouble(default_biome.blade_height_min));
  969. biome.blade_height_max = static_cast<float>(
  970. biome_obj["bladeHeightMax"].toDouble(default_biome.blade_height_max));
  971. biome.blade_width_min = static_cast<float>(
  972. biome_obj["bladeWidthMin"].toDouble(default_biome.blade_width_min));
  973. biome.blade_width_max = static_cast<float>(
  974. biome_obj["bladeWidthMax"].toDouble(default_biome.blade_width_max));
  975. biome.sway_strength = static_cast<float>(
  976. biome_obj["sway_strength"].toDouble(default_biome.sway_strength));
  977. biome.sway_speed = static_cast<float>(
  978. biome_obj["sway_speed"].toDouble(default_biome.sway_speed));
  979. biome.height_noise_amplitude =
  980. static_cast<float>(biome_obj["heightNoiseAmplitude"].toDouble(
  981. default_biome.height_noise_amplitude));
  982. biome.height_noise_frequency =
  983. static_cast<float>(biome_obj["heightNoiseFrequency"].toDouble(
  984. default_biome.height_noise_frequency));
  985. biome.terrain_macro_noise_scale =
  986. static_cast<float>(biome_obj["terrainMacroNoiseScale"].toDouble(
  987. default_biome.terrain_macro_noise_scale));
  988. biome.terrain_detail_noise_scale =
  989. static_cast<float>(biome_obj["terrainDetailNoiseScale"].toDouble(
  990. default_biome.terrain_detail_noise_scale));
  991. biome.terrain_soil_height =
  992. static_cast<float>(biome_obj["terrainSoilHeight"].toDouble(
  993. default_biome.terrain_soil_height));
  994. biome.terrain_soil_sharpness =
  995. static_cast<float>(biome_obj["terrainSoilSharpness"].toDouble(
  996. default_biome.terrain_soil_sharpness));
  997. biome.terrain_rock_threshold =
  998. static_cast<float>(biome_obj["terrainRockThreshold"].toDouble(
  999. default_biome.terrain_rock_threshold));
  1000. biome.terrain_rock_sharpness =
  1001. static_cast<float>(biome_obj["terrainRockSharpness"].toDouble(
  1002. default_biome.terrain_rock_sharpness));
  1003. biome.terrain_ambient_boost =
  1004. static_cast<float>(biome_obj["terrainAmbientBoost"].toDouble(
  1005. default_biome.terrain_ambient_boost));
  1006. biome.terrain_rock_detail_strength =
  1007. static_cast<float>(biome_obj["terrainRockDetailStrength"].toDouble(
  1008. default_biome.terrain_rock_detail_strength));
  1009. biome.background_sway_variance =
  1010. static_cast<float>(biome_obj["backgroundSwayVariance"].toDouble(
  1011. default_biome.background_sway_variance));
  1012. biome.background_scatter_radius =
  1013. static_cast<float>(biome_obj["backgroundScatterRadius"].toDouble(
  1014. default_biome.background_scatter_radius));
  1015. biome.plant_density = static_cast<float>(
  1016. biome_obj["plant_density"].toDouble(default_biome.plant_density));
  1017. biome.spawn_edge_padding =
  1018. static_cast<float>(biome_obj["spawnEdgePadding"].toDouble(
  1019. default_biome.spawn_edge_padding));
  1020. if (biome_obj.contains("seed")) {
  1021. biome.seed = static_cast<std::uint32_t>(
  1022. biome_obj["seed"].toVariant().toULongLong());
  1023. } else {
  1024. biome.seed = default_biome.seed;
  1025. }
  1026. }
  1027. std::vector<float> heights;
  1028. if (json.contains("heights")) {
  1029. const auto heights_array = json["heights"].toArray();
  1030. heights.reserve(heights_array.size());
  1031. for (const auto &val : heights_array) {
  1032. heights.push_back(static_cast<float>(val.toDouble(0.0)));
  1033. }
  1034. }
  1035. std::vector<Game::Map::TerrainType> terrain_types;
  1036. if (json.contains("terrain_types")) {
  1037. const auto types_array = json["terrain_types"].toArray();
  1038. terrain_types.reserve(types_array.size());
  1039. for (const auto &val : types_array) {
  1040. terrain_types.push_back(
  1041. static_cast<Game::Map::TerrainType>(val.toInt(0)));
  1042. }
  1043. }
  1044. std::vector<Game::Map::RiverSegment> rivers;
  1045. if (json.contains("rivers")) {
  1046. const auto rivers_array = json["rivers"].toArray();
  1047. rivers.reserve(rivers_array.size());
  1048. const Game::Map::RiverSegment default_river{};
  1049. for (const auto &val : rivers_array) {
  1050. const auto river_obj = val.toObject();
  1051. Game::Map::RiverSegment river;
  1052. river.start =
  1053. QVector3D(static_cast<float>(river_obj["startX"].toDouble(0.0)),
  1054. static_cast<float>(river_obj["startY"].toDouble(0.0)),
  1055. static_cast<float>(river_obj["startZ"].toDouble(0.0)));
  1056. river.end =
  1057. QVector3D(static_cast<float>(river_obj["endX"].toDouble(0.0)),
  1058. static_cast<float>(river_obj["endY"].toDouble(0.0)),
  1059. static_cast<float>(river_obj["endZ"].toDouble(0.0)));
  1060. river.width = static_cast<float>(river_obj["width"].toDouble(
  1061. static_cast<double>(default_river.width)));
  1062. rivers.push_back(river);
  1063. }
  1064. }
  1065. std::vector<Game::Map::Bridge> bridges;
  1066. if (json.contains("bridges")) {
  1067. const auto bridges_array = json["bridges"].toArray();
  1068. bridges.reserve(bridges_array.size());
  1069. const Game::Map::Bridge default_bridge{};
  1070. for (const auto &val : bridges_array) {
  1071. const auto bridge_obj = val.toObject();
  1072. Game::Map::Bridge bridge;
  1073. bridge.start =
  1074. QVector3D(static_cast<float>(bridge_obj["startX"].toDouble(0.0)),
  1075. static_cast<float>(bridge_obj["startY"].toDouble(0.0)),
  1076. static_cast<float>(bridge_obj["startZ"].toDouble(0.0)));
  1077. bridge.end =
  1078. QVector3D(static_cast<float>(bridge_obj["endX"].toDouble(0.0)),
  1079. static_cast<float>(bridge_obj["endY"].toDouble(0.0)),
  1080. static_cast<float>(bridge_obj["endZ"].toDouble(0.0)));
  1081. bridge.width = static_cast<float>(bridge_obj["width"].toDouble(
  1082. static_cast<double>(default_bridge.width)));
  1083. bridge.height = static_cast<float>(bridge_obj["height"].toDouble(
  1084. static_cast<double>(default_bridge.height)));
  1085. bridges.push_back(bridge);
  1086. }
  1087. }
  1088. roads.clear();
  1089. if (json.contains("roads")) {
  1090. const auto roads_array = json["roads"].toArray();
  1091. roads.reserve(roads_array.size());
  1092. const Game::Map::RoadSegment default_road{};
  1093. for (const auto &val : roads_array) {
  1094. const auto road_obj = val.toObject();
  1095. Game::Map::RoadSegment road;
  1096. road.start =
  1097. QVector3D(static_cast<float>(road_obj["startX"].toDouble(0.0)),
  1098. static_cast<float>(road_obj["startY"].toDouble(0.0)),
  1099. static_cast<float>(road_obj["startZ"].toDouble(0.0)));
  1100. road.end = QVector3D(static_cast<float>(road_obj["endX"].toDouble(0.0)),
  1101. static_cast<float>(road_obj["endY"].toDouble(0.0)),
  1102. static_cast<float>(road_obj["endZ"].toDouble(0.0)));
  1103. road.width = static_cast<float>(
  1104. road_obj["width"].toDouble(static_cast<double>(default_road.width)));
  1105. road.style = road_obj["style"].toString(default_road.style);
  1106. roads.push_back(road);
  1107. }
  1108. }
  1109. height_map->restoreFromData(heights, terrain_types, rivers, bridges);
  1110. }
  1111. auto Serialization::serialize_world(const World *world) -> QJsonDocument {
  1112. QJsonObject world_obj;
  1113. QJsonArray entities_array;
  1114. const auto &entities = world->get_entities();
  1115. for (const auto &[id, entity] : entities) {
  1116. QJsonObject const entity_obj = serialize_entity(entity.get());
  1117. entities_array.append(entity_obj);
  1118. }
  1119. world_obj["entities"] = entities_array;
  1120. world_obj["nextEntityId"] = static_cast<qint64>(world->get_next_entity_id());
  1121. world_obj["schemaVersion"] = 1;
  1122. world_obj["owner_registry"] =
  1123. Game::Systems::OwnerRegistry::instance().to_json();
  1124. const auto &terrain_service = Game::Map::TerrainService::instance();
  1125. if (terrain_service.is_initialized() &&
  1126. (terrain_service.get_height_map() != nullptr)) {
  1127. world_obj["terrain"] = serialize_terrain(terrain_service.get_height_map(),
  1128. terrain_service.biome_settings(),
  1129. terrain_service.road_segments());
  1130. }
  1131. return QJsonDocument(world_obj);
  1132. }
  1133. void Serialization::deserialize_world(World *world, const QJsonDocument &doc) {
  1134. auto world_obj = doc.object();
  1135. auto entities_array = world_obj["entities"].toArray();
  1136. for (const auto &value : entities_array) {
  1137. auto entity_obj = value.toObject();
  1138. const auto entity_id =
  1139. static_cast<EntityID>(entity_obj["id"].toVariant().toULongLong());
  1140. auto *entity = entity_id == NULL_ENTITY
  1141. ? world->create_entity()
  1142. : world->create_entity_with_id(entity_id);
  1143. if (entity != nullptr) {
  1144. deserialize_entity(entity, entity_obj);
  1145. }
  1146. }
  1147. if (world_obj.contains("nextEntityId")) {
  1148. const auto next_id = static_cast<EntityID>(
  1149. world_obj["nextEntityId"].toVariant().toULongLong());
  1150. world->set_next_entity_id(next_id);
  1151. }
  1152. if (world_obj.contains("owner_registry")) {
  1153. Game::Systems::OwnerRegistry::instance().from_json(
  1154. world_obj["owner_registry"].toObject());
  1155. }
  1156. if (world_obj.contains("terrain")) {
  1157. const auto terrain_obj = world_obj["terrain"].toObject();
  1158. const int width = terrain_obj["width"].toInt(50);
  1159. const int height = terrain_obj["height"].toInt(50);
  1160. const float tile_size =
  1161. static_cast<float>(terrain_obj["tile_size"].toDouble(1.0));
  1162. Game::Map::BiomeSettings biome;
  1163. std::vector<Game::Map::RoadSegment> roads;
  1164. std::vector<float> const heights;
  1165. std::vector<Game::Map::TerrainType> const terrain_types;
  1166. std::vector<Game::Map::RiverSegment> const rivers;
  1167. std::vector<Game::Map::Bridge> const bridges;
  1168. auto temp_height_map =
  1169. std::make_unique<Game::Map::TerrainHeightMap>(width, height, tile_size);
  1170. deserialize_terrain(temp_height_map.get(), biome, roads, terrain_obj);
  1171. auto &terrain_service = Game::Map::TerrainService::instance();
  1172. terrain_service.restore_from_serialized(
  1173. width, height, tile_size, temp_height_map->getHeightData(),
  1174. temp_height_map->getTerrainTypes(), temp_height_map->getRiverSegments(),
  1175. roads, temp_height_map->getBridges(), biome);
  1176. }
  1177. }
  1178. auto Serialization::save_to_file(const QString &filename,
  1179. const QJsonDocument &doc) -> bool {
  1180. QFile file(filename);
  1181. if (!file.open(QIODevice::WriteOnly)) {
  1182. qWarning() << "Could not open file for writing:" << filename;
  1183. return false;
  1184. }
  1185. file.write(doc.toJson());
  1186. return true;
  1187. }
  1188. auto Serialization::load_from_file(const QString &filename) -> QJsonDocument {
  1189. QFile file(filename);
  1190. if (!file.open(QIODevice::ReadOnly)) {
  1191. qWarning() << "Could not open file for reading:" << filename;
  1192. return {};
  1193. }
  1194. const QByteArray data = file.readAll();
  1195. return QJsonDocument::fromJson(data);
  1196. }
  1197. } // namespace Engine::Core