serialization.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  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) -> AttackComponent::CombatMode {
  46. if (value == "melee") {
  47. return AttackComponent::CombatMode::Melee;
  48. }
  49. if (value == "ranged") {
  50. return AttackComponent::CombatMode::Ranged;
  51. }
  52. return AttackComponent::CombatMode::Auto;
  53. }
  54. auto serialize_color(const std::array<float, 3> &color) -> QJsonArray {
  55. QJsonArray array;
  56. array.append(color[0]);
  57. array.append(color[1]);
  58. array.append(color[2]);
  59. return array;
  60. }
  61. void deserialize_color(const QJsonArray &array, std::array<float, 3> &color) {
  62. if (array.size() >= 3) {
  63. color[0] = static_cast<float>(array.at(0).toDouble());
  64. color[1] = static_cast<float>(array.at(1).toDouble());
  65. color[2] = static_cast<float>(array.at(2).toDouble());
  66. }
  67. }
  68. } // namespace
  69. auto Serialization::serialize_entity(const Entity *entity) -> QJsonObject {
  70. QJsonObject entity_obj;
  71. entity_obj["id"] = static_cast<qint64>(entity->get_id());
  72. if (const auto *transform = entity->get_component<TransformComponent>()) {
  73. QJsonObject transform_obj;
  74. transform_obj["pos_x"] = transform->position.x;
  75. transform_obj["pos_y"] = transform->position.y;
  76. transform_obj["pos_z"] = transform->position.z;
  77. transform_obj["rot_x"] = transform->rotation.x;
  78. transform_obj["rot_y"] = transform->rotation.y;
  79. transform_obj["rot_z"] = transform->rotation.z;
  80. transform_obj["scale_x"] = transform->scale.x;
  81. transform_obj["scale_y"] = transform->scale.y;
  82. transform_obj["scale_z"] = transform->scale.z;
  83. transform_obj["has_desired_yaw"] = transform->has_desired_yaw;
  84. transform_obj["desired_yaw"] = transform->desired_yaw;
  85. entity_obj["transform"] = transform_obj;
  86. }
  87. if (const auto *renderable = entity->get_component<RenderableComponent>()) {
  88. QJsonObject renderable_obj;
  89. renderable_obj["mesh_path"] = QString::fromStdString(renderable->mesh_path);
  90. renderable_obj["texture_path"] =
  91. QString::fromStdString(renderable->texture_path);
  92. if (!renderable->renderer_id.empty()) {
  93. renderable_obj["renderer_id"] =
  94. QString::fromStdString(renderable->renderer_id);
  95. }
  96. renderable_obj["visible"] = renderable->visible;
  97. renderable_obj["mesh"] = static_cast<int>(renderable->mesh);
  98. renderable_obj["color"] = serialize_color(renderable->color);
  99. entity_obj["renderable"] = renderable_obj;
  100. }
  101. if (const auto *unit = entity->get_component<UnitComponent>()) {
  102. QJsonObject unit_obj;
  103. unit_obj["health"] = unit->health;
  104. unit_obj["max_health"] = unit->max_health;
  105. unit_obj["speed"] = unit->speed;
  106. unit_obj["vision_range"] = unit->vision_range;
  107. unit_obj["unit_type"] = QString::fromStdString(
  108. Game::Units::spawn_typeToString(unit->spawn_type));
  109. unit_obj["owner_id"] = unit->owner_id;
  110. unit_obj["nation_id"] = Game::Systems::nationIDToQString(unit->nation_id);
  111. entity_obj["unit"] = unit_obj;
  112. }
  113. if (const auto *movement = entity->get_component<MovementComponent>()) {
  114. QJsonObject movement_obj;
  115. movement_obj["has_target"] = movement->has_target;
  116. movement_obj["target_x"] = movement->target_x;
  117. movement_obj["target_y"] = movement->target_y;
  118. movement_obj["goal_x"] = movement->goal_x;
  119. movement_obj["goal_y"] = movement->goal_y;
  120. movement_obj["vx"] = movement->vx;
  121. movement_obj["vz"] = movement->vz;
  122. movement_obj["path_pending"] = movement->path_pending;
  123. movement_obj["pending_request_id"] =
  124. static_cast<qint64>(movement->pending_request_id);
  125. movement_obj["repath_cooldown"] = movement->repath_cooldown;
  126. movement_obj["last_goal_x"] = movement->last_goal_x;
  127. movement_obj["last_goal_y"] = movement->last_goal_y;
  128. movement_obj["time_since_last_path_request"] =
  129. movement->time_since_last_path_request;
  130. QJsonArray path_array;
  131. for (const auto &waypoint : movement->path) {
  132. QJsonObject waypoint_obj;
  133. waypoint_obj["x"] = waypoint.first;
  134. waypoint_obj["y"] = waypoint.second;
  135. path_array.append(waypoint_obj);
  136. }
  137. movement_obj["path"] = path_array;
  138. entity_obj["movement"] = movement_obj;
  139. }
  140. if (const auto *attack = entity->get_component<AttackComponent>()) {
  141. QJsonObject attack_obj;
  142. attack_obj["range"] = attack->range;
  143. attack_obj["damage"] = attack->damage;
  144. attack_obj["cooldown"] = attack->cooldown;
  145. attack_obj["time_since_last"] = attack->time_since_last;
  146. attack_obj["melee_range"] = attack->melee_range;
  147. attack_obj["melee_damage"] = attack->melee_damage;
  148. attack_obj["melee_cooldown"] = attack->melee_cooldown;
  149. attack_obj["preferred_mode"] = combat_mode_to_string(attack->preferred_mode);
  150. attack_obj["current_mode"] = combat_mode_to_string(attack->current_mode);
  151. attack_obj["can_melee"] = attack->can_melee;
  152. attack_obj["can_ranged"] = attack->can_ranged;
  153. attack_obj["max_height_difference"] = attack->max_height_difference;
  154. attack_obj["in_melee_lock"] = attack->in_melee_lock;
  155. attack_obj["melee_lock_target_id"] =
  156. static_cast<qint64>(attack->melee_lock_target_id);
  157. entity_obj["attack"] = attack_obj;
  158. }
  159. if (const auto *attack_target =
  160. entity->get_component<AttackTargetComponent>()) {
  161. QJsonObject attack_target_obj;
  162. attack_target_obj["target_id"] =
  163. static_cast<qint64>(attack_target->target_id);
  164. attack_target_obj["should_chase"] = attack_target->should_chase;
  165. entity_obj["attack_target"] = attack_target_obj;
  166. }
  167. if (const auto *patrol = entity->get_component<PatrolComponent>()) {
  168. QJsonObject patrol_obj;
  169. patrol_obj["current_waypoint"] = static_cast<int>(patrol->current_waypoint);
  170. patrol_obj["patrolling"] = patrol->patrolling;
  171. QJsonArray waypoints_array;
  172. for (const auto &waypoint : patrol->waypoints) {
  173. QJsonObject waypoint_obj;
  174. waypoint_obj["x"] = waypoint.first;
  175. waypoint_obj["y"] = waypoint.second;
  176. waypoints_array.append(waypoint_obj);
  177. }
  178. patrol_obj["waypoints"] = waypoints_array;
  179. entity_obj["patrol"] = patrol_obj;
  180. }
  181. if (entity->get_component<BuildingComponent>() != nullptr) {
  182. entity_obj["building"] = true;
  183. }
  184. if (const auto *production = entity->get_component<ProductionComponent>()) {
  185. QJsonObject production_obj;
  186. production_obj["in_progress"] = production->in_progress;
  187. production_obj["build_time"] = production->build_time;
  188. production_obj["time_remaining"] = production->time_remaining;
  189. production_obj["produced_count"] = production->produced_count;
  190. production_obj["max_units"] = production->max_units;
  191. production_obj["product_type"] = QString::fromStdString(
  192. Game::Units::troop_typeToString(production->product_type));
  193. production_obj["rally_x"] = production->rally_x;
  194. production_obj["rally_z"] = production->rally_z;
  195. production_obj["rally_set"] = production->rally_set;
  196. production_obj["villager_cost"] = production->villager_cost;
  197. QJsonArray queue_array;
  198. for (const auto &queued : production->production_queue) {
  199. queue_array.append(
  200. QString::fromStdString(Game::Units::troop_typeToString(queued)));
  201. }
  202. production_obj["queue"] = queue_array;
  203. entity_obj["production"] = production_obj;
  204. }
  205. if (entity->get_component<AIControlledComponent>() != nullptr) {
  206. entity_obj["aiControlled"] = true;
  207. }
  208. if (const auto *capture = entity->get_component<CaptureComponent>()) {
  209. QJsonObject capture_obj;
  210. capture_obj["capturing_player_id"] = capture->capturing_player_id;
  211. capture_obj["capture_progress"] =
  212. static_cast<double>(capture->capture_progress);
  213. capture_obj["required_time"] = static_cast<double>(capture->required_time);
  214. capture_obj["is_being_captured"] = capture->is_being_captured;
  215. entity_obj["capture"] = capture_obj;
  216. }
  217. if (const auto *hold_mode = entity->get_component<HoldModeComponent>()) {
  218. QJsonObject hold_mode_obj;
  219. hold_mode_obj["active"] = hold_mode->active;
  220. hold_mode_obj["exit_cooldown"] =
  221. static_cast<double>(hold_mode->exit_cooldown);
  222. hold_mode_obj["stand_up_duration"] =
  223. static_cast<double>(hold_mode->stand_up_duration);
  224. entity_obj["hold_mode"] = hold_mode_obj;
  225. }
  226. if (const auto *healer = entity->get_component<HealerComponent>()) {
  227. QJsonObject healer_obj;
  228. healer_obj["healing_range"] = static_cast<double>(healer->healing_range);
  229. healer_obj["healing_amount"] = healer->healing_amount;
  230. healer_obj["healing_cooldown"] =
  231. static_cast<double>(healer->healing_cooldown);
  232. healer_obj["time_since_last_heal"] =
  233. static_cast<double>(healer->time_since_last_heal);
  234. entity_obj["healer"] = healer_obj;
  235. }
  236. if (const auto *catapult =
  237. entity->get_component<CatapultLoadingComponent>()) {
  238. QJsonObject catapult_obj;
  239. catapult_obj["state"] = static_cast<int>(catapult->state);
  240. catapult_obj["loading_time"] = static_cast<double>(catapult->loading_time);
  241. catapult_obj["loading_duration"] =
  242. static_cast<double>(catapult->loading_duration);
  243. catapult_obj["firing_time"] = static_cast<double>(catapult->firing_time);
  244. catapult_obj["firing_duration"] =
  245. static_cast<double>(catapult->firing_duration);
  246. catapult_obj["target_id"] = static_cast<qint64>(catapult->target_id);
  247. catapult_obj["target_locked_x"] =
  248. static_cast<double>(catapult->target_locked_x);
  249. catapult_obj["target_locked_y"] =
  250. static_cast<double>(catapult->target_locked_y);
  251. catapult_obj["target_locked_z"] =
  252. static_cast<double>(catapult->target_locked_z);
  253. catapult_obj["target_position_locked"] = catapult->target_position_locked;
  254. entity_obj["catapult_loading"] = catapult_obj;
  255. }
  256. return entity_obj;
  257. }
  258. void Serialization::deserialize_entity(Entity *entity, const QJsonObject &json) {
  259. if (json.contains("transform")) {
  260. const auto transform_obj = json["transform"].toObject();
  261. auto *transform = entity->add_component<TransformComponent>();
  262. transform->position.x =
  263. static_cast<float>(transform_obj["pos_x"].toDouble());
  264. transform->position.y =
  265. static_cast<float>(transform_obj["pos_y"].toDouble());
  266. transform->position.z =
  267. static_cast<float>(transform_obj["pos_z"].toDouble());
  268. transform->rotation.x =
  269. static_cast<float>(transform_obj["rot_x"].toDouble());
  270. transform->rotation.y =
  271. static_cast<float>(transform_obj["rot_y"].toDouble());
  272. transform->rotation.z =
  273. static_cast<float>(transform_obj["rot_z"].toDouble());
  274. transform->scale.x =
  275. static_cast<float>(transform_obj["scale_x"].toDouble());
  276. transform->scale.y =
  277. static_cast<float>(transform_obj["scale_y"].toDouble());
  278. transform->scale.z =
  279. static_cast<float>(transform_obj["scale_z"].toDouble());
  280. transform->has_desired_yaw = transform_obj["has_desired_yaw"].toBool(false);
  281. transform->desired_yaw =
  282. static_cast<float>(transform_obj["desired_yaw"].toDouble());
  283. }
  284. if (json.contains("renderable")) {
  285. const auto renderable_obj = json["renderable"].toObject();
  286. auto *renderable = entity->add_component<RenderableComponent>("", "");
  287. renderable->mesh_path =
  288. renderable_obj["mesh_path"].toString().toStdString();
  289. renderable->texture_path =
  290. renderable_obj["texture_path"].toString().toStdString();
  291. renderable->renderer_id =
  292. renderable_obj["renderer_id"].toString().toStdString();
  293. renderable->visible = renderable_obj["visible"].toBool(true);
  294. renderable->mesh =
  295. static_cast<RenderableComponent::MeshKind>(renderable_obj["mesh"].toInt(
  296. static_cast<int>(RenderableComponent::MeshKind::Cube)));
  297. if (renderable_obj.contains("color")) {
  298. deserialize_color(renderable_obj["color"].toArray(), renderable->color);
  299. }
  300. }
  301. if (json.contains("unit")) {
  302. const auto unit_obj = json["unit"].toObject();
  303. auto *unit = entity->add_component<UnitComponent>();
  304. unit->health = unit_obj["health"].toInt(Defaults::kUnitDefaultHealth);
  305. unit->max_health =
  306. unit_obj["max_health"].toInt(Defaults::kUnitDefaultHealth);
  307. unit->speed = static_cast<float>(unit_obj["speed"].toDouble());
  308. unit->vision_range = static_cast<float>(unit_obj["vision_range"].toDouble(
  309. static_cast<double>(Defaults::kUnitDefaultVisionRange)));
  310. QString const unit_type_str = unit_obj["unit_type"].toString();
  311. Game::Units::SpawnType spawn_type;
  312. if (Game::Units::tryParseSpawnType(unit_type_str, spawn_type)) {
  313. unit->spawn_type = spawn_type;
  314. } else {
  315. qWarning() << "Unknown spawn type in save file:" << unit_type_str
  316. << "- defaulting to Archer";
  317. unit->spawn_type = Game::Units::SpawnType::Archer;
  318. }
  319. unit->owner_id = unit_obj["owner_id"].toInt(0);
  320. if (unit_obj.contains("nation_id")) {
  321. const QString nation_str = unit_obj["nation_id"].toString();
  322. Game::Systems::NationID nation_id;
  323. if (Game::Systems::tryParseNationID(nation_str, nation_id)) {
  324. unit->nation_id = nation_id;
  325. } else {
  326. qWarning() << "Unknown nation ID in save file:" << nation_str
  327. << "- using default";
  328. unit->nation_id = Game::Systems::NationID::RomanRepublic;
  329. }
  330. }
  331. }
  332. if (json.contains("movement")) {
  333. const auto movement_obj = json["movement"].toObject();
  334. auto *movement = entity->add_component<MovementComponent>();
  335. movement->has_target = movement_obj["has_target"].toBool(false);
  336. movement->target_x =
  337. static_cast<float>(movement_obj["target_x"].toDouble());
  338. movement->target_y =
  339. static_cast<float>(movement_obj["target_y"].toDouble());
  340. movement->goal_x = static_cast<float>(movement_obj["goal_x"].toDouble());
  341. movement->goal_y = static_cast<float>(movement_obj["goal_y"].toDouble());
  342. movement->vx = static_cast<float>(movement_obj["vx"].toDouble());
  343. movement->vz = static_cast<float>(movement_obj["vz"].toDouble());
  344. movement->path_pending = movement_obj["path_pending"].toBool(false);
  345. movement->pending_request_id = static_cast<std::uint64_t>(
  346. movement_obj["pending_request_id"].toVariant().toULongLong());
  347. movement->repath_cooldown =
  348. static_cast<float>(movement_obj["repath_cooldown"].toDouble());
  349. movement->last_goal_x =
  350. static_cast<float>(movement_obj["last_goal_x"].toDouble());
  351. movement->last_goal_y =
  352. static_cast<float>(movement_obj["last_goal_y"].toDouble());
  353. movement->time_since_last_path_request = static_cast<float>(
  354. movement_obj["time_since_last_path_request"].toDouble());
  355. movement->path.clear();
  356. const auto path_array = movement_obj["path"].toArray();
  357. movement->path.reserve(path_array.size());
  358. for (const auto &value : path_array) {
  359. const auto waypoint_obj = value.toObject();
  360. movement->path.emplace_back(
  361. static_cast<float>(waypoint_obj["x"].toDouble()),
  362. static_cast<float>(waypoint_obj["y"].toDouble()));
  363. }
  364. }
  365. if (json.contains("attack")) {
  366. const auto attack_obj = json["attack"].toObject();
  367. auto *attack = entity->add_component<AttackComponent>();
  368. attack->range = static_cast<float>(attack_obj["range"].toDouble());
  369. attack->damage = attack_obj["damage"].toInt(0);
  370. attack->cooldown = static_cast<float>(attack_obj["cooldown"].toDouble());
  371. attack->time_since_last =
  372. static_cast<float>(attack_obj["time_since_last"].toDouble());
  373. attack->melee_range = static_cast<float>(attack_obj["melee_range"].toDouble(
  374. static_cast<double>(Defaults::kAttackMeleeRange)));
  375. attack->melee_damage = attack_obj["melee_damage"].toInt(0);
  376. attack->melee_cooldown =
  377. static_cast<float>(attack_obj["melee_cooldown"].toDouble());
  378. attack->preferred_mode =
  379. combat_mode_from_string(attack_obj["preferred_mode"].toString());
  380. attack->current_mode =
  381. combat_mode_from_string(attack_obj["current_mode"].toString());
  382. attack->can_melee = attack_obj["can_melee"].toBool(true);
  383. attack->can_ranged = attack_obj["can_ranged"].toBool(false);
  384. attack->max_height_difference =
  385. static_cast<float>(attack_obj["max_height_difference"].toDouble(
  386. static_cast<double>(Defaults::kAttackHeightTolerance)));
  387. attack->in_melee_lock = attack_obj["in_melee_lock"].toBool(false);
  388. attack->melee_lock_target_id = static_cast<EntityID>(
  389. attack_obj["melee_lock_target_id"].toVariant().toULongLong());
  390. }
  391. if (json.contains("attack_target")) {
  392. const auto attack_target_obj = json["attack_target"].toObject();
  393. auto *attack_target = entity->add_component<AttackTargetComponent>();
  394. attack_target->target_id = static_cast<EntityID>(
  395. attack_target_obj["target_id"].toVariant().toULongLong());
  396. attack_target->should_chase =
  397. attack_target_obj["should_chase"].toBool(false);
  398. }
  399. if (json.contains("patrol")) {
  400. const auto patrol_obj = json["patrol"].toObject();
  401. auto *patrol = entity->add_component<PatrolComponent>();
  402. patrol->current_waypoint = static_cast<size_t>(
  403. std::max(0, patrol_obj["current_waypoint"].toInt()));
  404. patrol->patrolling = patrol_obj["patrolling"].toBool(false);
  405. patrol->waypoints.clear();
  406. const auto waypoints_array = patrol_obj["waypoints"].toArray();
  407. patrol->waypoints.reserve(waypoints_array.size());
  408. for (const auto &value : waypoints_array) {
  409. const auto waypoint_obj = value.toObject();
  410. patrol->waypoints.emplace_back(
  411. static_cast<float>(waypoint_obj["x"].toDouble()),
  412. static_cast<float>(waypoint_obj["y"].toDouble()));
  413. }
  414. }
  415. if (json.contains("building") && json["building"].toBool()) {
  416. entity->add_component<BuildingComponent>();
  417. }
  418. if (json.contains("production")) {
  419. const auto production_obj = json["production"].toObject();
  420. auto *production = entity->add_component<ProductionComponent>();
  421. production->in_progress = production_obj["in_progress"].toBool(false);
  422. production->build_time =
  423. static_cast<float>(production_obj["build_time"].toDouble());
  424. production->time_remaining =
  425. static_cast<float>(production_obj["time_remaining"].toDouble());
  426. production->produced_count = production_obj["produced_count"].toInt(0);
  427. production->max_units = production_obj["max_units"].toInt(0);
  428. production->product_type = Game::Units::troop_typeFromString(
  429. production_obj["product_type"].toString().toStdString());
  430. production->rally_x =
  431. static_cast<float>(production_obj["rally_x"].toDouble());
  432. production->rally_z =
  433. static_cast<float>(production_obj["rally_z"].toDouble());
  434. production->rally_set = production_obj["rally_set"].toBool(false);
  435. production->villager_cost = production_obj["villager_cost"].toInt(1);
  436. production->production_queue.clear();
  437. const auto queue_array = production_obj["queue"].toArray();
  438. production->production_queue.reserve(queue_array.size());
  439. for (const auto &value : queue_array) {
  440. production->production_queue.push_back(
  441. Game::Units::troop_typeFromString(value.toString().toStdString()));
  442. }
  443. }
  444. if (json.contains("aiControlled") && json["aiControlled"].toBool()) {
  445. entity->add_component<AIControlledComponent>();
  446. }
  447. if (json.contains("capture")) {
  448. const auto capture_obj = json["capture"].toObject();
  449. auto *capture = entity->add_component<CaptureComponent>();
  450. capture->capturing_player_id = capture_obj["capturing_player_id"].toInt(-1);
  451. capture->capture_progress =
  452. static_cast<float>(capture_obj["capture_progress"].toDouble(0.0));
  453. capture->required_time =
  454. static_cast<float>(capture_obj["required_time"].toDouble(
  455. static_cast<double>(Defaults::kCaptureRequiredTime)));
  456. capture->is_being_captured = capture_obj["is_being_captured"].toBool(false);
  457. }
  458. if (json.contains("hold_mode")) {
  459. const auto hold_mode_obj = json["hold_mode"].toObject();
  460. auto *hold_mode = entity->add_component<HoldModeComponent>();
  461. hold_mode->active = hold_mode_obj["active"].toBool(true);
  462. hold_mode->exit_cooldown =
  463. static_cast<float>(hold_mode_obj["exit_cooldown"].toDouble(0.0));
  464. hold_mode->stand_up_duration =
  465. static_cast<float>(hold_mode_obj["stand_up_duration"].toDouble(
  466. static_cast<double>(Defaults::kHoldStandUpDuration)));
  467. }
  468. if (json.contains("healer")) {
  469. const auto healer_obj = json["healer"].toObject();
  470. auto *healer = entity->add_component<HealerComponent>();
  471. healer->healing_range =
  472. static_cast<float>(healer_obj["healing_range"].toDouble(8.0));
  473. healer->healing_amount = healer_obj["healing_amount"].toInt(5);
  474. healer->healing_cooldown =
  475. static_cast<float>(healer_obj["healing_cooldown"].toDouble(2.0));
  476. healer->time_since_last_heal =
  477. static_cast<float>(healer_obj["time_since_last_heal"].toDouble(0.0));
  478. }
  479. if (json.contains("catapult_loading")) {
  480. const auto catapult_obj = json["catapult_loading"].toObject();
  481. auto *catapult = entity->add_component<CatapultLoadingComponent>();
  482. catapult->state = static_cast<CatapultLoadingComponent::LoadingState>(
  483. catapult_obj["state"].toInt(
  484. static_cast<int>(CatapultLoadingComponent::LoadingState::Idle)));
  485. catapult->loading_time =
  486. static_cast<float>(catapult_obj["loading_time"].toDouble(0.0));
  487. catapult->loading_duration =
  488. static_cast<float>(catapult_obj["loading_duration"].toDouble(2.0));
  489. catapult->firing_time =
  490. static_cast<float>(catapult_obj["firing_time"].toDouble(0.0));
  491. catapult->firing_duration =
  492. static_cast<float>(catapult_obj["firing_duration"].toDouble(0.5));
  493. catapult->target_id = static_cast<EntityID>(
  494. catapult_obj["target_id"].toVariant().toULongLong());
  495. catapult->target_locked_x =
  496. static_cast<float>(catapult_obj["target_locked_x"].toDouble(0.0));
  497. catapult->target_locked_y =
  498. static_cast<float>(catapult_obj["target_locked_y"].toDouble(0.0));
  499. catapult->target_locked_z =
  500. static_cast<float>(catapult_obj["target_locked_z"].toDouble(0.0));
  501. catapult->target_position_locked =
  502. catapult_obj["target_position_locked"].toBool(false);
  503. }
  504. }
  505. auto Serialization::serialize_terrain(
  506. const Game::Map::TerrainHeightMap *height_map,
  507. const Game::Map::BiomeSettings &biome,
  508. const std::vector<Game::Map::RoadSegment> &roads) -> QJsonObject {
  509. QJsonObject terrain_obj;
  510. if (height_map == nullptr) {
  511. return terrain_obj;
  512. }
  513. terrain_obj["width"] = height_map->getWidth();
  514. terrain_obj["height"] = height_map->getHeight();
  515. terrain_obj["tile_size"] = height_map->getTileSize();
  516. QJsonArray heights_array;
  517. const auto &heights = height_map->getHeightData();
  518. for (float const h : heights) {
  519. heights_array.append(h);
  520. }
  521. terrain_obj["heights"] = heights_array;
  522. QJsonArray terrain_types_array;
  523. const auto &terrain_types = height_map->getTerrainTypes();
  524. for (auto type : terrain_types) {
  525. terrain_types_array.append(static_cast<int>(type));
  526. }
  527. terrain_obj["terrain_types"] = terrain_types_array;
  528. QJsonArray rivers_array;
  529. const auto &rivers = height_map->getRiverSegments();
  530. for (const auto &river : rivers) {
  531. QJsonObject river_obj;
  532. river_obj["startX"] = river.start.x();
  533. river_obj["startY"] = river.start.y();
  534. river_obj["startZ"] = river.start.z();
  535. river_obj["endX"] = river.end.x();
  536. river_obj["endY"] = river.end.y();
  537. river_obj["endZ"] = river.end.z();
  538. river_obj["width"] = river.width;
  539. rivers_array.append(river_obj);
  540. }
  541. terrain_obj["rivers"] = rivers_array;
  542. QJsonArray bridges_array;
  543. const auto &bridges = height_map->getBridges();
  544. for (const auto &bridge : bridges) {
  545. QJsonObject bridge_obj;
  546. bridge_obj["startX"] = bridge.start.x();
  547. bridge_obj["startY"] = bridge.start.y();
  548. bridge_obj["startZ"] = bridge.start.z();
  549. bridge_obj["endX"] = bridge.end.x();
  550. bridge_obj["endY"] = bridge.end.y();
  551. bridge_obj["endZ"] = bridge.end.z();
  552. bridge_obj["width"] = bridge.width;
  553. bridge_obj["height"] = bridge.height;
  554. bridges_array.append(bridge_obj);
  555. }
  556. terrain_obj["bridges"] = bridges_array;
  557. QJsonArray roads_array;
  558. for (const auto &road : roads) {
  559. QJsonObject road_obj;
  560. road_obj["startX"] = road.start.x();
  561. road_obj["startY"] = road.start.y();
  562. road_obj["startZ"] = road.start.z();
  563. road_obj["endX"] = road.end.x();
  564. road_obj["endY"] = road.end.y();
  565. road_obj["endZ"] = road.end.z();
  566. road_obj["width"] = road.width;
  567. road_obj["style"] = road.style;
  568. roads_array.append(road_obj);
  569. }
  570. terrain_obj["roads"] = roads_array;
  571. QJsonObject biome_obj;
  572. biome_obj["grassPrimaryR"] = biome.grass_primary.x();
  573. biome_obj["grassPrimaryG"] = biome.grass_primary.y();
  574. biome_obj["grassPrimaryB"] = biome.grass_primary.z();
  575. biome_obj["grassSecondaryR"] = biome.grass_secondary.x();
  576. biome_obj["grassSecondaryG"] = biome.grass_secondary.y();
  577. biome_obj["grassSecondaryB"] = biome.grass_secondary.z();
  578. biome_obj["grassDryR"] = biome.grass_dry.x();
  579. biome_obj["grassDryG"] = biome.grass_dry.y();
  580. biome_obj["grassDryB"] = biome.grass_dry.z();
  581. biome_obj["soilColorR"] = biome.soil_color.x();
  582. biome_obj["soilColorG"] = biome.soil_color.y();
  583. biome_obj["soilColorB"] = biome.soil_color.z();
  584. biome_obj["rockLowR"] = biome.rock_low.x();
  585. biome_obj["rockLowG"] = biome.rock_low.y();
  586. biome_obj["rockLowB"] = biome.rock_low.z();
  587. biome_obj["rockHighR"] = biome.rock_high.x();
  588. biome_obj["rockHighG"] = biome.rock_high.y();
  589. biome_obj["rockHighB"] = biome.rock_high.z();
  590. biome_obj["patchDensity"] = biome.patch_density;
  591. biome_obj["patchJitter"] = biome.patch_jitter;
  592. biome_obj["backgroundBladeDensity"] = biome.background_blade_density;
  593. biome_obj["bladeHeightMin"] = biome.blade_height_min;
  594. biome_obj["bladeHeightMax"] = biome.blade_height_max;
  595. biome_obj["bladeWidthMin"] = biome.blade_width_min;
  596. biome_obj["bladeWidthMax"] = biome.blade_width_max;
  597. biome_obj["sway_strength"] = biome.sway_strength;
  598. biome_obj["sway_speed"] = biome.sway_speed;
  599. biome_obj["heightNoiseAmplitude"] = biome.height_noise_amplitude;
  600. biome_obj["heightNoiseFrequency"] = biome.height_noise_frequency;
  601. biome_obj["terrainMacroNoiseScale"] = biome.terrain_macro_noise_scale;
  602. biome_obj["terrainDetailNoiseScale"] = biome.terrain_detail_noise_scale;
  603. biome_obj["terrainSoilHeight"] = biome.terrain_soil_height;
  604. biome_obj["terrainSoilSharpness"] = biome.terrain_soil_sharpness;
  605. biome_obj["terrainRockThreshold"] = biome.terrain_rock_threshold;
  606. biome_obj["terrainRockSharpness"] = biome.terrain_rock_sharpness;
  607. biome_obj["terrainAmbientBoost"] = biome.terrain_ambient_boost;
  608. biome_obj["terrainRockDetailStrength"] = biome.terrain_rock_detail_strength;
  609. biome_obj["backgroundSwayVariance"] = biome.background_sway_variance;
  610. biome_obj["backgroundScatterRadius"] = biome.background_scatter_radius;
  611. biome_obj["plant_density"] = biome.plant_density;
  612. biome_obj["spawnEdgePadding"] = biome.spawn_edge_padding;
  613. biome_obj["seed"] = static_cast<qint64>(biome.seed);
  614. terrain_obj["biome"] = biome_obj;
  615. return terrain_obj;
  616. }
  617. void Serialization::deserialize_terrain(
  618. Game::Map::TerrainHeightMap *height_map, Game::Map::BiomeSettings &biome,
  619. std::vector<Game::Map::RoadSegment> &roads, const QJsonObject &json) {
  620. if ((height_map == nullptr) || json.isEmpty()) {
  621. return;
  622. }
  623. if (json.contains("biome")) {
  624. const auto biome_obj = json["biome"].toObject();
  625. const Game::Map::BiomeSettings default_biome{};
  626. const auto read_color = [&](const QString &base,
  627. const QVector3D &fallback) -> QVector3D {
  628. const auto r_key = base + QStringLiteral("R");
  629. const auto g_key = base + QStringLiteral("G");
  630. const auto b_key = base + QStringLiteral("B");
  631. const float r = static_cast<float>(
  632. biome_obj[r_key].toDouble(static_cast<double>(fallback.x())));
  633. const float g = static_cast<float>(
  634. biome_obj[g_key].toDouble(static_cast<double>(fallback.y())));
  635. const float b = static_cast<float>(
  636. biome_obj[b_key].toDouble(static_cast<double>(fallback.z())));
  637. return {r, g, b};
  638. };
  639. biome.grass_primary =
  640. read_color(QStringLiteral("grassPrimary"), default_biome.grass_primary);
  641. biome.grass_secondary = read_color(QStringLiteral("grassSecondary"),
  642. default_biome.grass_secondary);
  643. biome.grass_dry =
  644. read_color(QStringLiteral("grassDry"), default_biome.grass_dry);
  645. biome.soil_color =
  646. read_color(QStringLiteral("soilColor"), default_biome.soil_color);
  647. biome.rock_low =
  648. read_color(QStringLiteral("rockLow"), default_biome.rock_low);
  649. biome.rock_high =
  650. read_color(QStringLiteral("rockHigh"), default_biome.rock_high);
  651. biome.patch_density = static_cast<float>(
  652. biome_obj["patchDensity"].toDouble(default_biome.patch_density));
  653. biome.patch_jitter = static_cast<float>(
  654. biome_obj["patchJitter"].toDouble(default_biome.patch_jitter));
  655. biome.background_blade_density =
  656. static_cast<float>(biome_obj["backgroundBladeDensity"].toDouble(
  657. default_biome.background_blade_density));
  658. biome.blade_height_min = static_cast<float>(
  659. biome_obj["bladeHeightMin"].toDouble(default_biome.blade_height_min));
  660. biome.blade_height_max = static_cast<float>(
  661. biome_obj["bladeHeightMax"].toDouble(default_biome.blade_height_max));
  662. biome.blade_width_min = static_cast<float>(
  663. biome_obj["bladeWidthMin"].toDouble(default_biome.blade_width_min));
  664. biome.blade_width_max = static_cast<float>(
  665. biome_obj["bladeWidthMax"].toDouble(default_biome.blade_width_max));
  666. biome.sway_strength = static_cast<float>(
  667. biome_obj["sway_strength"].toDouble(default_biome.sway_strength));
  668. biome.sway_speed = static_cast<float>(
  669. biome_obj["sway_speed"].toDouble(default_biome.sway_speed));
  670. biome.height_noise_amplitude =
  671. static_cast<float>(biome_obj["heightNoiseAmplitude"].toDouble(
  672. default_biome.height_noise_amplitude));
  673. biome.height_noise_frequency =
  674. static_cast<float>(biome_obj["heightNoiseFrequency"].toDouble(
  675. default_biome.height_noise_frequency));
  676. biome.terrain_macro_noise_scale =
  677. static_cast<float>(biome_obj["terrainMacroNoiseScale"].toDouble(
  678. default_biome.terrain_macro_noise_scale));
  679. biome.terrain_detail_noise_scale =
  680. static_cast<float>(biome_obj["terrainDetailNoiseScale"].toDouble(
  681. default_biome.terrain_detail_noise_scale));
  682. biome.terrain_soil_height =
  683. static_cast<float>(biome_obj["terrainSoilHeight"].toDouble(
  684. default_biome.terrain_soil_height));
  685. biome.terrain_soil_sharpness =
  686. static_cast<float>(biome_obj["terrainSoilSharpness"].toDouble(
  687. default_biome.terrain_soil_sharpness));
  688. biome.terrain_rock_threshold =
  689. static_cast<float>(biome_obj["terrainRockThreshold"].toDouble(
  690. default_biome.terrain_rock_threshold));
  691. biome.terrain_rock_sharpness =
  692. static_cast<float>(biome_obj["terrainRockSharpness"].toDouble(
  693. default_biome.terrain_rock_sharpness));
  694. biome.terrain_ambient_boost =
  695. static_cast<float>(biome_obj["terrainAmbientBoost"].toDouble(
  696. default_biome.terrain_ambient_boost));
  697. biome.terrain_rock_detail_strength =
  698. static_cast<float>(biome_obj["terrainRockDetailStrength"].toDouble(
  699. default_biome.terrain_rock_detail_strength));
  700. biome.background_sway_variance =
  701. static_cast<float>(biome_obj["backgroundSwayVariance"].toDouble(
  702. default_biome.background_sway_variance));
  703. biome.background_scatter_radius =
  704. static_cast<float>(biome_obj["backgroundScatterRadius"].toDouble(
  705. default_biome.background_scatter_radius));
  706. biome.plant_density = static_cast<float>(
  707. biome_obj["plant_density"].toDouble(default_biome.plant_density));
  708. biome.spawn_edge_padding =
  709. static_cast<float>(biome_obj["spawnEdgePadding"].toDouble(
  710. default_biome.spawn_edge_padding));
  711. if (biome_obj.contains("seed")) {
  712. biome.seed = static_cast<std::uint32_t>(
  713. biome_obj["seed"].toVariant().toULongLong());
  714. } else {
  715. biome.seed = default_biome.seed;
  716. }
  717. }
  718. std::vector<float> heights;
  719. if (json.contains("heights")) {
  720. const auto heights_array = json["heights"].toArray();
  721. heights.reserve(heights_array.size());
  722. for (const auto &val : heights_array) {
  723. heights.push_back(static_cast<float>(val.toDouble(0.0)));
  724. }
  725. }
  726. std::vector<Game::Map::TerrainType> terrain_types;
  727. if (json.contains("terrain_types")) {
  728. const auto types_array = json["terrain_types"].toArray();
  729. terrain_types.reserve(types_array.size());
  730. for (const auto &val : types_array) {
  731. terrain_types.push_back(
  732. static_cast<Game::Map::TerrainType>(val.toInt(0)));
  733. }
  734. }
  735. std::vector<Game::Map::RiverSegment> rivers;
  736. if (json.contains("rivers")) {
  737. const auto rivers_array = json["rivers"].toArray();
  738. rivers.reserve(rivers_array.size());
  739. const Game::Map::RiverSegment default_river{};
  740. for (const auto &val : rivers_array) {
  741. const auto river_obj = val.toObject();
  742. Game::Map::RiverSegment river;
  743. river.start =
  744. QVector3D(static_cast<float>(river_obj["startX"].toDouble(0.0)),
  745. static_cast<float>(river_obj["startY"].toDouble(0.0)),
  746. static_cast<float>(river_obj["startZ"].toDouble(0.0)));
  747. river.end =
  748. QVector3D(static_cast<float>(river_obj["endX"].toDouble(0.0)),
  749. static_cast<float>(river_obj["endY"].toDouble(0.0)),
  750. static_cast<float>(river_obj["endZ"].toDouble(0.0)));
  751. river.width = static_cast<float>(river_obj["width"].toDouble(
  752. static_cast<double>(default_river.width)));
  753. rivers.push_back(river);
  754. }
  755. }
  756. std::vector<Game::Map::Bridge> bridges;
  757. if (json.contains("bridges")) {
  758. const auto bridges_array = json["bridges"].toArray();
  759. bridges.reserve(bridges_array.size());
  760. const Game::Map::Bridge default_bridge{};
  761. for (const auto &val : bridges_array) {
  762. const auto bridge_obj = val.toObject();
  763. Game::Map::Bridge bridge;
  764. bridge.start =
  765. QVector3D(static_cast<float>(bridge_obj["startX"].toDouble(0.0)),
  766. static_cast<float>(bridge_obj["startY"].toDouble(0.0)),
  767. static_cast<float>(bridge_obj["startZ"].toDouble(0.0)));
  768. bridge.end =
  769. QVector3D(static_cast<float>(bridge_obj["endX"].toDouble(0.0)),
  770. static_cast<float>(bridge_obj["endY"].toDouble(0.0)),
  771. static_cast<float>(bridge_obj["endZ"].toDouble(0.0)));
  772. bridge.width = static_cast<float>(bridge_obj["width"].toDouble(
  773. static_cast<double>(default_bridge.width)));
  774. bridge.height = static_cast<float>(bridge_obj["height"].toDouble(
  775. static_cast<double>(default_bridge.height)));
  776. bridges.push_back(bridge);
  777. }
  778. }
  779. roads.clear();
  780. if (json.contains("roads")) {
  781. const auto roads_array = json["roads"].toArray();
  782. roads.reserve(roads_array.size());
  783. const Game::Map::RoadSegment default_road{};
  784. for (const auto &val : roads_array) {
  785. const auto road_obj = val.toObject();
  786. Game::Map::RoadSegment road;
  787. road.start =
  788. QVector3D(static_cast<float>(road_obj["startX"].toDouble(0.0)),
  789. static_cast<float>(road_obj["startY"].toDouble(0.0)),
  790. static_cast<float>(road_obj["startZ"].toDouble(0.0)));
  791. road.end = QVector3D(static_cast<float>(road_obj["endX"].toDouble(0.0)),
  792. static_cast<float>(road_obj["endY"].toDouble(0.0)),
  793. static_cast<float>(road_obj["endZ"].toDouble(0.0)));
  794. road.width = static_cast<float>(
  795. road_obj["width"].toDouble(static_cast<double>(default_road.width)));
  796. road.style = road_obj["style"].toString(default_road.style);
  797. roads.push_back(road);
  798. }
  799. }
  800. height_map->restoreFromData(heights, terrain_types, rivers, bridges);
  801. }
  802. auto Serialization::serialize_world(const World *world) -> QJsonDocument {
  803. QJsonObject world_obj;
  804. QJsonArray entities_array;
  805. const auto &entities = world->get_entities();
  806. for (const auto &[id, entity] : entities) {
  807. QJsonObject const entity_obj = serialize_entity(entity.get());
  808. entities_array.append(entity_obj);
  809. }
  810. world_obj["entities"] = entities_array;
  811. world_obj["nextEntityId"] = static_cast<qint64>(world->get_next_entity_id());
  812. world_obj["schemaVersion"] = 1;
  813. world_obj["owner_registry"] =
  814. Game::Systems::OwnerRegistry::instance().to_json();
  815. const auto &terrain_service = Game::Map::TerrainService::instance();
  816. if (terrain_service.is_initialized() &&
  817. (terrain_service.get_height_map() != nullptr)) {
  818. world_obj["terrain"] = serialize_terrain(terrain_service.get_height_map(),
  819. terrain_service.biome_settings(),
  820. terrain_service.road_segments());
  821. }
  822. return QJsonDocument(world_obj);
  823. }
  824. void Serialization::deserialize_world(World *world, const QJsonDocument &doc) {
  825. auto world_obj = doc.object();
  826. auto entities_array = world_obj["entities"].toArray();
  827. for (const auto &value : entities_array) {
  828. auto entity_obj = value.toObject();
  829. const auto entity_id =
  830. static_cast<EntityID>(entity_obj["id"].toVariant().toULongLong());
  831. auto *entity = entity_id == NULL_ENTITY
  832. ? world->create_entity()
  833. : world->create_entity_with_id(entity_id);
  834. if (entity != nullptr) {
  835. deserialize_entity(entity, entity_obj);
  836. }
  837. }
  838. if (world_obj.contains("nextEntityId")) {
  839. const auto next_id = static_cast<EntityID>(
  840. world_obj["nextEntityId"].toVariant().toULongLong());
  841. world->set_next_entity_id(next_id);
  842. }
  843. if (world_obj.contains("owner_registry")) {
  844. Game::Systems::OwnerRegistry::instance().from_json(
  845. world_obj["owner_registry"].toObject());
  846. }
  847. if (world_obj.contains("terrain")) {
  848. const auto terrain_obj = world_obj["terrain"].toObject();
  849. const int width = terrain_obj["width"].toInt(50);
  850. const int height = terrain_obj["height"].toInt(50);
  851. const float tile_size =
  852. static_cast<float>(terrain_obj["tile_size"].toDouble(1.0));
  853. Game::Map::BiomeSettings biome;
  854. std::vector<Game::Map::RoadSegment> roads;
  855. std::vector<float> const heights;
  856. std::vector<Game::Map::TerrainType> const terrain_types;
  857. std::vector<Game::Map::RiverSegment> const rivers;
  858. std::vector<Game::Map::Bridge> const bridges;
  859. auto temp_height_map =
  860. std::make_unique<Game::Map::TerrainHeightMap>(width, height, tile_size);
  861. deserialize_terrain(temp_height_map.get(), biome, roads, terrain_obj);
  862. auto &terrain_service = Game::Map::TerrainService::instance();
  863. terrain_service.restore_from_serialized(
  864. width, height, tile_size, temp_height_map->getHeightData(),
  865. temp_height_map->getTerrainTypes(), temp_height_map->getRiverSegments(),
  866. roads, temp_height_map->getBridges(), biome);
  867. }
  868. }
  869. auto Serialization::save_to_file(const QString &filename,
  870. const QJsonDocument &doc) -> bool {
  871. QFile file(filename);
  872. if (!file.open(QIODevice::WriteOnly)) {
  873. qWarning() << "Could not open file for writing:" << filename;
  874. return false;
  875. }
  876. file.write(doc.toJson());
  877. return true;
  878. }
  879. auto Serialization::load_from_file(const QString &filename) -> QJsonDocument {
  880. QFile file(filename);
  881. if (!file.open(QIODevice::ReadOnly)) {
  882. qWarning() << "Could not open file for reading:" << filename;
  883. return {};
  884. }
  885. const QByteArray data = file.readAll();
  886. return QJsonDocument::fromJson(data);
  887. }
  888. } // namespace Engine::Core