serialization_test.cpp 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566
  1. #include "core/component.h"
  2. #include "core/entity.h"
  3. #include "core/serialization.h"
  4. #include "core/world.h"
  5. #include "systems/nation_id.h"
  6. #include "systems/owner_registry.h"
  7. #include "units/spawn_type.h"
  8. #include "units/troop_type.h"
  9. #include <QJsonArray>
  10. #include <QJsonDocument>
  11. #include <QJsonObject>
  12. #include <QTemporaryFile>
  13. #include <gtest/gtest.h>
  14. using namespace Engine::Core;
  15. class SerializationTest : public ::testing::Test {
  16. protected:
  17. void SetUp() override { world = std::make_unique<World>(); }
  18. void TearDown() override { world.reset(); }
  19. std::unique_ptr<World> world;
  20. };
  21. TEST_F(SerializationTest, EntitySerializationBasic) {
  22. auto *entity = world->create_entity();
  23. ASSERT_NE(entity, nullptr);
  24. auto entity_id = entity->get_id();
  25. QJsonObject json = Serialization::serialize_entity(entity);
  26. EXPECT_TRUE(json.contains("id"));
  27. EXPECT_EQ(json["id"].toVariant().toULongLong(),
  28. static_cast<qulonglong>(entity_id));
  29. }
  30. TEST_F(SerializationTest, TransformComponentSerialization) {
  31. auto *entity = world->create_entity();
  32. auto *transform = entity->add_component<TransformComponent>();
  33. transform->position.x = 10.5F;
  34. transform->position.y = 20.3F;
  35. transform->position.z = 30.1F;
  36. transform->rotation.x = 0.5F;
  37. transform->rotation.y = 1.0F;
  38. transform->rotation.z = 1.5F;
  39. transform->scale.x = 2.0F;
  40. transform->scale.y = 2.5F;
  41. transform->scale.z = 3.0F;
  42. transform->has_desired_yaw = true;
  43. transform->desired_yaw = 45.0F;
  44. QJsonObject json = Serialization::serialize_entity(entity);
  45. ASSERT_TRUE(json.contains("transform"));
  46. QJsonObject transform_obj = json["transform"].toObject();
  47. EXPECT_FLOAT_EQ(transform_obj["pos_x"].toDouble(), 10.5);
  48. EXPECT_FLOAT_EQ(transform_obj["pos_y"].toDouble(), 20.3);
  49. EXPECT_FLOAT_EQ(transform_obj["pos_z"].toDouble(), 30.1);
  50. EXPECT_FLOAT_EQ(transform_obj["rot_x"].toDouble(), 0.5);
  51. EXPECT_FLOAT_EQ(transform_obj["rot_y"].toDouble(), 1.0);
  52. EXPECT_FLOAT_EQ(transform_obj["rot_z"].toDouble(), 1.5);
  53. EXPECT_FLOAT_EQ(transform_obj["scale_x"].toDouble(), 2.0);
  54. EXPECT_FLOAT_EQ(transform_obj["scale_y"].toDouble(), 2.5);
  55. EXPECT_FLOAT_EQ(transform_obj["scale_z"].toDouble(), 3.0);
  56. EXPECT_TRUE(transform_obj["has_desired_yaw"].toBool());
  57. EXPECT_FLOAT_EQ(transform_obj["desired_yaw"].toDouble(), 45.0);
  58. }
  59. TEST_F(SerializationTest, TransformComponentRoundTrip) {
  60. auto *original_entity = world->create_entity();
  61. auto *transform = original_entity->add_component<TransformComponent>();
  62. transform->position.x = 15.0F;
  63. transform->position.y = 25.0F;
  64. transform->position.z = 35.0F;
  65. transform->rotation.x = 1.0F;
  66. transform->rotation.y = 2.0F;
  67. transform->rotation.z = 3.0F;
  68. transform->scale.x = 1.5F;
  69. transform->scale.y = 2.5F;
  70. transform->scale.z = 3.5F;
  71. transform->has_desired_yaw = true;
  72. transform->desired_yaw = 90.0F;
  73. QJsonObject json = Serialization::serialize_entity(original_entity);
  74. auto *new_entity = world->create_entity();
  75. Serialization::deserialize_entity(new_entity, json);
  76. auto *deserialized = new_entity->get_component<TransformComponent>();
  77. ASSERT_NE(deserialized, nullptr);
  78. EXPECT_FLOAT_EQ(deserialized->position.x, 15.0F);
  79. EXPECT_FLOAT_EQ(deserialized->position.y, 25.0F);
  80. EXPECT_FLOAT_EQ(deserialized->position.z, 35.0F);
  81. EXPECT_FLOAT_EQ(deserialized->rotation.x, 1.0F);
  82. EXPECT_FLOAT_EQ(deserialized->rotation.y, 2.0F);
  83. EXPECT_FLOAT_EQ(deserialized->rotation.z, 3.0F);
  84. EXPECT_FLOAT_EQ(deserialized->scale.x, 1.5F);
  85. EXPECT_FLOAT_EQ(deserialized->scale.y, 2.5F);
  86. EXPECT_FLOAT_EQ(deserialized->scale.z, 3.5F);
  87. EXPECT_TRUE(deserialized->has_desired_yaw);
  88. EXPECT_FLOAT_EQ(deserialized->desired_yaw, 90.0F);
  89. }
  90. TEST_F(SerializationTest, UnitComponentSerialization) {
  91. auto *entity = world->create_entity();
  92. auto *unit = entity->add_component<UnitComponent>();
  93. unit->health = 80;
  94. unit->max_health = 100;
  95. unit->speed = 5.5F;
  96. unit->vision_range = 15.0F;
  97. unit->spawn_type = Game::Units::SpawnType::Archer;
  98. unit->owner_id = 1;
  99. unit->nation_id = Game::Systems::NationID::RomanRepublic;
  100. QJsonObject json = Serialization::serialize_entity(entity);
  101. ASSERT_TRUE(json.contains("unit"));
  102. QJsonObject unit_obj = json["unit"].toObject();
  103. EXPECT_EQ(unit_obj["health"].toInt(), 80);
  104. EXPECT_EQ(unit_obj["max_health"].toInt(), 100);
  105. EXPECT_FLOAT_EQ(unit_obj["speed"].toDouble(), 5.5);
  106. EXPECT_FLOAT_EQ(unit_obj["vision_range"].toDouble(), 15.0);
  107. EXPECT_EQ(unit_obj["unit_type"].toString(), QString("archer"));
  108. EXPECT_EQ(unit_obj["owner_id"].toInt(), 1);
  109. EXPECT_EQ(unit_obj["nation_id"].toString(), QString("roman_republic"));
  110. }
  111. TEST_F(SerializationTest, UnitComponentRoundTrip) {
  112. auto *original_entity = world->create_entity();
  113. auto *unit = original_entity->add_component<UnitComponent>();
  114. unit->health = 75;
  115. unit->max_health = 150;
  116. unit->speed = 7.5F;
  117. unit->vision_range = 20.0F;
  118. unit->spawn_type = Game::Units::SpawnType::Spearman;
  119. unit->owner_id = 2;
  120. unit->nation_id = Game::Systems::NationID::Carthage;
  121. QJsonObject json = Serialization::serialize_entity(original_entity);
  122. auto *new_entity = world->create_entity();
  123. Serialization::deserialize_entity(new_entity, json);
  124. auto *deserialized = new_entity->get_component<UnitComponent>();
  125. ASSERT_NE(deserialized, nullptr);
  126. EXPECT_EQ(deserialized->health, 75);
  127. EXPECT_EQ(deserialized->max_health, 150);
  128. EXPECT_FLOAT_EQ(deserialized->speed, 7.5F);
  129. EXPECT_FLOAT_EQ(deserialized->vision_range, 20.0F);
  130. EXPECT_EQ(deserialized->spawn_type, Game::Units::SpawnType::Spearman);
  131. EXPECT_EQ(deserialized->owner_id, 2);
  132. EXPECT_EQ(deserialized->nation_id, Game::Systems::NationID::Carthage);
  133. }
  134. TEST_F(SerializationTest, MovementComponentSerialization) {
  135. auto *entity = world->create_entity();
  136. auto *movement = entity->add_component<MovementComponent>();
  137. movement->has_target = true;
  138. movement->target_x = 50.0F;
  139. movement->target_y = 60.0F;
  140. movement->goal_x = 55.0F;
  141. movement->goal_y = 65.0F;
  142. movement->vx = 1.5F;
  143. movement->vz = 2.0F;
  144. movement->path_pending = false;
  145. movement->pending_request_id = 42;
  146. movement->repath_cooldown = 1.0F;
  147. movement->last_goal_x = 45.0F;
  148. movement->last_goal_y = 55.0F;
  149. movement->time_since_last_path_request = 0.5F;
  150. movement->path.emplace_back(10.0F, 20.0F);
  151. movement->path.emplace_back(30.0F, 40.0F);
  152. QJsonObject json = Serialization::serialize_entity(entity);
  153. ASSERT_TRUE(json.contains("movement"));
  154. QJsonObject movement_obj = json["movement"].toObject();
  155. EXPECT_TRUE(movement_obj["has_target"].toBool());
  156. EXPECT_FLOAT_EQ(movement_obj["target_x"].toDouble(), 50.0);
  157. EXPECT_FLOAT_EQ(movement_obj["target_y"].toDouble(), 60.0);
  158. EXPECT_FLOAT_EQ(movement_obj["goal_x"].toDouble(), 55.0);
  159. EXPECT_FLOAT_EQ(movement_obj["goal_y"].toDouble(), 65.0);
  160. EXPECT_FLOAT_EQ(movement_obj["vx"].toDouble(), 1.5);
  161. EXPECT_FLOAT_EQ(movement_obj["vz"].toDouble(), 2.0);
  162. EXPECT_FALSE(movement_obj["path_pending"].toBool());
  163. EXPECT_EQ(movement_obj["pending_request_id"].toVariant().toULongLong(),
  164. 42ULL);
  165. ASSERT_TRUE(movement_obj.contains("path"));
  166. QJsonArray path_array = movement_obj["path"].toArray();
  167. ASSERT_EQ(path_array.size(), 2);
  168. QJsonObject waypoint1 = path_array[0].toObject();
  169. EXPECT_FLOAT_EQ(waypoint1["x"].toDouble(), 10.0);
  170. EXPECT_FLOAT_EQ(waypoint1["y"].toDouble(), 20.0);
  171. QJsonObject waypoint2 = path_array[1].toObject();
  172. EXPECT_FLOAT_EQ(waypoint2["x"].toDouble(), 30.0);
  173. EXPECT_FLOAT_EQ(waypoint2["y"].toDouble(), 40.0);
  174. }
  175. TEST_F(SerializationTest, AttackComponentSerialization) {
  176. auto *entity = world->create_entity();
  177. auto *attack = entity->add_component<AttackComponent>();
  178. attack->range = 10.0F;
  179. attack->damage = 25;
  180. attack->cooldown = 2.0F;
  181. attack->time_since_last = 0.5F;
  182. attack->melee_range = 2.0F;
  183. attack->melee_damage = 15;
  184. attack->melee_cooldown = 1.5F;
  185. attack->preferred_mode = AttackComponent::CombatMode::Ranged;
  186. attack->current_mode = AttackComponent::CombatMode::Ranged;
  187. attack->can_melee = true;
  188. attack->can_ranged = true;
  189. attack->max_height_difference = 5.0F;
  190. attack->in_melee_lock = false;
  191. attack->melee_lock_target_id = 0;
  192. QJsonObject json = Serialization::serialize_entity(entity);
  193. ASSERT_TRUE(json.contains("attack"));
  194. QJsonObject attack_obj = json["attack"].toObject();
  195. EXPECT_FLOAT_EQ(attack_obj["range"].toDouble(), 10.0);
  196. EXPECT_EQ(attack_obj["damage"].toInt(), 25);
  197. EXPECT_FLOAT_EQ(attack_obj["cooldown"].toDouble(), 2.0);
  198. EXPECT_FLOAT_EQ(attack_obj["time_since_last"].toDouble(), 0.5);
  199. EXPECT_FLOAT_EQ(attack_obj["melee_range"].toDouble(), 2.0);
  200. EXPECT_EQ(attack_obj["melee_damage"].toInt(), 15);
  201. EXPECT_FLOAT_EQ(attack_obj["melee_cooldown"].toDouble(), 1.5);
  202. EXPECT_EQ(attack_obj["preferred_mode"].toString(), QString("ranged"));
  203. EXPECT_EQ(attack_obj["current_mode"].toString(), QString("ranged"));
  204. EXPECT_TRUE(attack_obj["can_melee"].toBool());
  205. EXPECT_TRUE(attack_obj["can_ranged"].toBool());
  206. EXPECT_FLOAT_EQ(attack_obj["max_height_difference"].toDouble(), 5.0);
  207. EXPECT_FALSE(attack_obj["in_melee_lock"].toBool());
  208. }
  209. TEST_F(SerializationTest, EntityDeserializationRoundTrip) {
  210. auto *original_entity = world->create_entity();
  211. auto *transform = original_entity->add_component<TransformComponent>();
  212. transform->position.x = 100.0F;
  213. transform->position.y = 200.0F;
  214. transform->position.z = 300.0F;
  215. auto *unit = original_entity->add_component<UnitComponent>();
  216. unit->health = 75;
  217. unit->max_health = 100;
  218. unit->speed = 6.0F;
  219. QJsonObject json = Serialization::serialize_entity(original_entity);
  220. auto *new_entity = world->create_entity();
  221. Serialization::deserialize_entity(new_entity, json);
  222. auto *deserialized_transform =
  223. new_entity->get_component<TransformComponent>();
  224. ASSERT_NE(deserialized_transform, nullptr);
  225. EXPECT_FLOAT_EQ(deserialized_transform->position.x, 100.0F);
  226. EXPECT_FLOAT_EQ(deserialized_transform->position.y, 200.0F);
  227. EXPECT_FLOAT_EQ(deserialized_transform->position.z, 300.0F);
  228. auto *deserialized_unit = new_entity->get_component<UnitComponent>();
  229. ASSERT_NE(deserialized_unit, nullptr);
  230. EXPECT_EQ(deserialized_unit->health, 75);
  231. EXPECT_EQ(deserialized_unit->max_health, 100);
  232. EXPECT_FLOAT_EQ(deserialized_unit->speed, 6.0F);
  233. }
  234. TEST_F(SerializationTest, DeserializationWithMissingFields) {
  235. QJsonObject json;
  236. json["id"] = 1;
  237. QJsonObject unit_obj;
  238. unit_obj["health"] = 50;
  239. json["unit"] = unit_obj;
  240. auto *entity = world->create_entity();
  241. Serialization::deserialize_entity(entity, json);
  242. auto *unit = entity->get_component<UnitComponent>();
  243. ASSERT_NE(unit, nullptr);
  244. EXPECT_EQ(unit->health, 50);
  245. EXPECT_EQ(unit->max_health, Defaults::kUnitDefaultHealth);
  246. }
  247. TEST_F(SerializationTest, DeserializationWithMalformedJSON) {
  248. QJsonObject json;
  249. json["id"] = 1;
  250. QJsonObject transform_obj;
  251. transform_obj["pos_x"] = "not_a_number";
  252. json["transform"] = transform_obj;
  253. auto *entity = world->create_entity();
  254. EXPECT_NO_THROW({ Serialization::deserialize_entity(entity, json); });
  255. auto *transform = entity->get_component<TransformComponent>();
  256. ASSERT_NE(transform, nullptr);
  257. EXPECT_FLOAT_EQ(transform->position.x, 0.0F);
  258. }
  259. TEST_F(SerializationTest, WorldSerializationRoundTrip) {
  260. auto *entity1 = world->create_entity();
  261. auto *transform1 = entity1->add_component<TransformComponent>();
  262. transform1->position.x = 10.0F;
  263. auto *entity2 = world->create_entity();
  264. auto *transform2 = entity2->add_component<TransformComponent>();
  265. transform2->position.x = 20.0F;
  266. QJsonDocument doc = Serialization::serialize_world(world.get());
  267. ASSERT_TRUE(doc.isObject());
  268. QJsonObject world_obj = doc.object();
  269. EXPECT_TRUE(world_obj.contains("entities"));
  270. EXPECT_TRUE(world_obj.contains("nextEntityId"));
  271. EXPECT_TRUE(world_obj.contains("schemaVersion"));
  272. auto new_world = std::make_unique<World>();
  273. Serialization::deserialize_world(new_world.get(), doc);
  274. const auto &entities = new_world->get_entities();
  275. EXPECT_EQ(entities.size(), 2UL);
  276. }
  277. TEST_F(SerializationTest, SaveAndLoadFromFile) {
  278. auto *entity = world->create_entity();
  279. auto *transform = entity->add_component<TransformComponent>();
  280. transform->position.x = 42.0F;
  281. transform->position.y = 43.0F;
  282. transform->position.z = 44.0F;
  283. QJsonDocument doc = Serialization::serialize_world(world.get());
  284. QTemporaryFile temp_file;
  285. ASSERT_TRUE(temp_file.open());
  286. QString filename = temp_file.fileName();
  287. temp_file.close();
  288. EXPECT_TRUE(Serialization::save_to_file(filename, doc));
  289. QJsonDocument loaded_doc = Serialization::load_from_file(filename);
  290. EXPECT_FALSE(loaded_doc.isNull());
  291. auto new_world = std::make_unique<World>();
  292. Serialization::deserialize_world(new_world.get(), loaded_doc);
  293. const auto &entities = new_world->get_entities();
  294. EXPECT_EQ(entities.size(), 1UL);
  295. if (!entities.empty()) {
  296. auto *loaded_entity = entities.begin()->second.get();
  297. auto *loaded_transform = loaded_entity->get_component<TransformComponent>();
  298. ASSERT_NE(loaded_transform, nullptr);
  299. EXPECT_FLOAT_EQ(loaded_transform->position.x, 42.0F);
  300. EXPECT_FLOAT_EQ(loaded_transform->position.y, 43.0F);
  301. EXPECT_FLOAT_EQ(loaded_transform->position.z, 44.0F);
  302. }
  303. }
  304. TEST_F(SerializationTest, ProductionComponentSerialization) {
  305. auto *entity = world->create_entity();
  306. auto *production = entity->add_component<ProductionComponent>();
  307. production->in_progress = true;
  308. production->build_time = 10.0F;
  309. production->time_remaining = 5.0F;
  310. production->produced_count = 3;
  311. production->max_units = 10;
  312. production->product_type = Game::Units::TroopType::Archer;
  313. production->rally_x = 100.0F;
  314. production->rally_z = 200.0F;
  315. production->rally_set = true;
  316. production->villager_cost = 2;
  317. production->production_queue.push_back(Game::Units::TroopType::Spearman);
  318. production->production_queue.push_back(Game::Units::TroopType::Archer);
  319. QJsonObject json = Serialization::serialize_entity(entity);
  320. ASSERT_TRUE(json.contains("production"));
  321. QJsonObject prod_obj = json["production"].toObject();
  322. EXPECT_TRUE(prod_obj["in_progress"].toBool());
  323. EXPECT_FLOAT_EQ(prod_obj["build_time"].toDouble(), 10.0);
  324. EXPECT_FLOAT_EQ(prod_obj["time_remaining"].toDouble(), 5.0);
  325. EXPECT_EQ(prod_obj["produced_count"].toInt(), 3);
  326. EXPECT_EQ(prod_obj["max_units"].toInt(), 10);
  327. EXPECT_EQ(prod_obj["product_type"].toString(), QString("archer"));
  328. EXPECT_FLOAT_EQ(prod_obj["rally_x"].toDouble(), 100.0);
  329. EXPECT_FLOAT_EQ(prod_obj["rally_z"].toDouble(), 200.0);
  330. EXPECT_TRUE(prod_obj["rally_set"].toBool());
  331. EXPECT_EQ(prod_obj["villager_cost"].toInt(), 2);
  332. ASSERT_TRUE(prod_obj.contains("queue"));
  333. QJsonArray queue = prod_obj["queue"].toArray();
  334. EXPECT_EQ(queue.size(), 2);
  335. EXPECT_EQ(queue[0].toString(), QString("spearman"));
  336. EXPECT_EQ(queue[1].toString(), QString("archer"));
  337. }
  338. TEST_F(SerializationTest, PatrolComponentSerialization) {
  339. auto *entity = world->create_entity();
  340. auto *patrol = entity->add_component<PatrolComponent>();
  341. patrol->current_waypoint = 1;
  342. patrol->patrolling = true;
  343. patrol->waypoints.emplace_back(10.0F, 20.0F);
  344. patrol->waypoints.emplace_back(30.0F, 40.0F);
  345. patrol->waypoints.emplace_back(50.0F, 60.0F);
  346. QJsonObject json = Serialization::serialize_entity(entity);
  347. ASSERT_TRUE(json.contains("patrol"));
  348. QJsonObject patrol_obj = json["patrol"].toObject();
  349. EXPECT_EQ(patrol_obj["current_waypoint"].toInt(), 1);
  350. EXPECT_TRUE(patrol_obj["patrolling"].toBool());
  351. ASSERT_TRUE(patrol_obj.contains("waypoints"));
  352. QJsonArray waypoints = patrol_obj["waypoints"].toArray();
  353. EXPECT_EQ(waypoints.size(), 3);
  354. QJsonObject wp0 = waypoints[0].toObject();
  355. EXPECT_FLOAT_EQ(wp0["x"].toDouble(), 10.0);
  356. EXPECT_FLOAT_EQ(wp0["y"].toDouble(), 20.0);
  357. }
  358. TEST_F(SerializationTest, PatrolComponentRoundTrip) {
  359. auto *original_entity = world->create_entity();
  360. auto *patrol = original_entity->add_component<PatrolComponent>();
  361. patrol->current_waypoint = 2;
  362. patrol->patrolling = true;
  363. patrol->waypoints.emplace_back(15.0F, 25.0F);
  364. patrol->waypoints.emplace_back(35.0F, 45.0F);
  365. QJsonObject json = Serialization::serialize_entity(original_entity);
  366. auto *new_entity = world->create_entity();
  367. Serialization::deserialize_entity(new_entity, json);
  368. auto *deserialized = new_entity->get_component<PatrolComponent>();
  369. ASSERT_NE(deserialized, nullptr);
  370. EXPECT_EQ(deserialized->current_waypoint, 2UL);
  371. EXPECT_TRUE(deserialized->patrolling);
  372. EXPECT_EQ(deserialized->waypoints.size(), 2UL);
  373. EXPECT_FLOAT_EQ(deserialized->waypoints[0].first, 15.0F);
  374. EXPECT_FLOAT_EQ(deserialized->waypoints[0].second, 25.0F);
  375. }
  376. TEST_F(SerializationTest, MovementComponentRoundTrip) {
  377. auto *original_entity = world->create_entity();
  378. auto *movement = original_entity->add_component<MovementComponent>();
  379. movement->has_target = true;
  380. movement->target_x = 100.0F;
  381. movement->target_y = 200.0F;
  382. movement->goal_x = 150.0F;
  383. movement->goal_y = 250.0F;
  384. movement->vx = 1.5F;
  385. movement->vz = 2.5F;
  386. movement->path.emplace_back(10.0F, 20.0F);
  387. movement->path.emplace_back(30.0F, 40.0F);
  388. QJsonObject json = Serialization::serialize_entity(original_entity);
  389. auto *new_entity = world->create_entity();
  390. Serialization::deserialize_entity(new_entity, json);
  391. auto *deserialized = new_entity->get_component<MovementComponent>();
  392. ASSERT_NE(deserialized, nullptr);
  393. EXPECT_TRUE(deserialized->has_target);
  394. EXPECT_FLOAT_EQ(deserialized->target_x, 100.0F);
  395. EXPECT_FLOAT_EQ(deserialized->target_y, 200.0F);
  396. EXPECT_FLOAT_EQ(deserialized->goal_x, 150.0F);
  397. EXPECT_FLOAT_EQ(deserialized->goal_y, 250.0F);
  398. EXPECT_FLOAT_EQ(deserialized->vx, 1.5F);
  399. EXPECT_FLOAT_EQ(deserialized->vz, 2.5F);
  400. EXPECT_EQ(deserialized->path.size(), 2UL);
  401. }
  402. TEST_F(SerializationTest, AttackComponentRoundTrip) {
  403. auto *original_entity = world->create_entity();
  404. auto *attack = original_entity->add_component<AttackComponent>();
  405. attack->range = 15.0F;
  406. attack->damage = 30;
  407. attack->cooldown = 2.5F;
  408. attack->melee_range = 3.0F;
  409. attack->melee_damage = 20;
  410. attack->preferred_mode = AttackComponent::CombatMode::Ranged;
  411. attack->current_mode = AttackComponent::CombatMode::Melee;
  412. attack->can_melee = true;
  413. attack->can_ranged = true;
  414. attack->in_melee_lock = true;
  415. attack->melee_lock_target_id = 42;
  416. QJsonObject json = Serialization::serialize_entity(original_entity);
  417. auto *new_entity = world->create_entity();
  418. Serialization::deserialize_entity(new_entity, json);
  419. auto *deserialized = new_entity->get_component<AttackComponent>();
  420. ASSERT_NE(deserialized, nullptr);
  421. EXPECT_FLOAT_EQ(deserialized->range, 15.0F);
  422. EXPECT_EQ(deserialized->damage, 30);
  423. EXPECT_FLOAT_EQ(deserialized->cooldown, 2.5F);
  424. EXPECT_FLOAT_EQ(deserialized->melee_range, 3.0F);
  425. EXPECT_EQ(deserialized->melee_damage, 20);
  426. EXPECT_EQ(deserialized->preferred_mode, AttackComponent::CombatMode::Ranged);
  427. EXPECT_EQ(deserialized->current_mode, AttackComponent::CombatMode::Melee);
  428. EXPECT_TRUE(deserialized->can_melee);
  429. EXPECT_TRUE(deserialized->can_ranged);
  430. EXPECT_TRUE(deserialized->in_melee_lock);
  431. EXPECT_EQ(deserialized->melee_lock_target_id, 42U);
  432. }
  433. TEST_F(SerializationTest, ProductionComponentRoundTrip) {
  434. auto *original_entity = world->create_entity();
  435. auto *production = original_entity->add_component<ProductionComponent>();
  436. production->in_progress = true;
  437. production->build_time = 15.0F;
  438. production->time_remaining = 7.5F;
  439. production->produced_count = 5;
  440. production->max_units = 20;
  441. production->product_type = Game::Units::TroopType::Spearman;
  442. production->rally_x = 150.0F;
  443. production->rally_z = 250.0F;
  444. production->rally_set = true;
  445. production->villager_cost = 3;
  446. production->production_queue.push_back(Game::Units::TroopType::Archer);
  447. QJsonObject json = Serialization::serialize_entity(original_entity);
  448. auto *new_entity = world->create_entity();
  449. Serialization::deserialize_entity(new_entity, json);
  450. auto *deserialized = new_entity->get_component<ProductionComponent>();
  451. ASSERT_NE(deserialized, nullptr);
  452. EXPECT_TRUE(deserialized->in_progress);
  453. EXPECT_FLOAT_EQ(deserialized->build_time, 15.0F);
  454. EXPECT_FLOAT_EQ(deserialized->time_remaining, 7.5F);
  455. EXPECT_EQ(deserialized->produced_count, 5);
  456. EXPECT_EQ(deserialized->max_units, 20);
  457. EXPECT_EQ(deserialized->product_type, Game::Units::TroopType::Spearman);
  458. EXPECT_FLOAT_EQ(deserialized->rally_x, 150.0F);
  459. EXPECT_FLOAT_EQ(deserialized->rally_z, 250.0F);
  460. EXPECT_TRUE(deserialized->rally_set);
  461. EXPECT_EQ(deserialized->villager_cost, 3);
  462. EXPECT_EQ(deserialized->production_queue.size(), 1UL);
  463. EXPECT_EQ(deserialized->production_queue[0], Game::Units::TroopType::Archer);
  464. }
  465. TEST_F(SerializationTest, RenderableComponentSerialization) {
  466. auto *entity = world->create_entity();
  467. auto *renderable =
  468. entity->add_component<RenderableComponent>("mesh.obj", "texture.png");
  469. renderable->mesh_path = "models/archer.obj";
  470. renderable->texture_path = "textures/archer_diffuse.png";
  471. renderable->renderer_id = "archer_renderer";
  472. renderable->visible = true;
  473. renderable->mesh = RenderableComponent::MeshKind::Capsule;
  474. renderable->color = {0.8F, 0.2F, 0.5F};
  475. QJsonObject json = Serialization::serialize_entity(entity);
  476. ASSERT_TRUE(json.contains("renderable"));
  477. QJsonObject renderable_obj = json["renderable"].toObject();
  478. EXPECT_EQ(renderable_obj["mesh_path"].toString(),
  479. QString("models/archer.obj"));
  480. EXPECT_EQ(renderable_obj["texture_path"].toString(),
  481. QString("textures/archer_diffuse.png"));
  482. EXPECT_EQ(renderable_obj["renderer_id"].toString(),
  483. QString("archer_renderer"));
  484. EXPECT_TRUE(renderable_obj["visible"].toBool());
  485. EXPECT_EQ(renderable_obj["mesh"].toInt(),
  486. static_cast<int>(RenderableComponent::MeshKind::Capsule));
  487. ASSERT_TRUE(renderable_obj.contains("color"));
  488. QJsonArray color = renderable_obj["color"].toArray();
  489. EXPECT_EQ(color.size(), 3);
  490. EXPECT_FLOAT_EQ(color[0].toDouble(), 0.8);
  491. EXPECT_FLOAT_EQ(color[1].toDouble(), 0.2);
  492. EXPECT_FLOAT_EQ(color[2].toDouble(), 0.5);
  493. }
  494. TEST_F(SerializationTest, RenderableComponentRoundTrip) {
  495. auto *original_entity = world->create_entity();
  496. auto *renderable = original_entity->add_component<RenderableComponent>(
  497. "test.obj", "test.png");
  498. renderable->mesh_path = "models/building.obj";
  499. renderable->texture_path = "textures/building.png";
  500. renderable->visible = false;
  501. renderable->mesh = RenderableComponent::MeshKind::Quad;
  502. renderable->color = {1.0F, 0.5F, 0.25F};
  503. QJsonObject json = Serialization::serialize_entity(original_entity);
  504. auto *new_entity = world->create_entity();
  505. Serialization::deserialize_entity(new_entity, json);
  506. auto *deserialized = new_entity->get_component<RenderableComponent>();
  507. ASSERT_NE(deserialized, nullptr);
  508. EXPECT_EQ(deserialized->mesh_path, "models/building.obj");
  509. EXPECT_EQ(deserialized->texture_path, "textures/building.png");
  510. EXPECT_FALSE(deserialized->visible);
  511. EXPECT_EQ(deserialized->mesh, RenderableComponent::MeshKind::Quad);
  512. EXPECT_FLOAT_EQ(deserialized->color[0], 1.0F);
  513. EXPECT_FLOAT_EQ(deserialized->color[1], 0.5F);
  514. EXPECT_FLOAT_EQ(deserialized->color[2], 0.25F);
  515. }
  516. TEST_F(SerializationTest, AttackTargetComponentSerialization) {
  517. auto *entity = world->create_entity();
  518. auto *attack_target = entity->add_component<AttackTargetComponent>();
  519. attack_target->target_id = 42;
  520. attack_target->should_chase = true;
  521. QJsonObject json = Serialization::serialize_entity(entity);
  522. ASSERT_TRUE(json.contains("attack_target"));
  523. QJsonObject attack_target_obj = json["attack_target"].toObject();
  524. EXPECT_EQ(attack_target_obj["target_id"].toVariant().toULongLong(), 42ULL);
  525. EXPECT_TRUE(attack_target_obj["should_chase"].toBool());
  526. }
  527. TEST_F(SerializationTest, AttackTargetComponentRoundTrip) {
  528. auto *original_entity = world->create_entity();
  529. auto *attack_target = original_entity->add_component<AttackTargetComponent>();
  530. attack_target->target_id = 123;
  531. attack_target->should_chase = false;
  532. QJsonObject json = Serialization::serialize_entity(original_entity);
  533. auto *new_entity = world->create_entity();
  534. Serialization::deserialize_entity(new_entity, json);
  535. auto *deserialized = new_entity->get_component<AttackTargetComponent>();
  536. ASSERT_NE(deserialized, nullptr);
  537. EXPECT_EQ(deserialized->target_id, 123U);
  538. EXPECT_FALSE(deserialized->should_chase);
  539. }
  540. TEST_F(SerializationTest, BuildingComponentSerialization) {
  541. auto *entity = world->create_entity();
  542. entity->add_component<BuildingComponent>();
  543. QJsonObject json = Serialization::serialize_entity(entity);
  544. ASSERT_TRUE(json.contains("building"));
  545. EXPECT_TRUE(json["building"].toBool());
  546. }
  547. TEST_F(SerializationTest, BuildingComponentRoundTrip) {
  548. auto *original_entity = world->create_entity();
  549. original_entity->add_component<BuildingComponent>();
  550. QJsonObject json = Serialization::serialize_entity(original_entity);
  551. auto *new_entity = world->create_entity();
  552. Serialization::deserialize_entity(new_entity, json);
  553. auto *deserialized = new_entity->get_component<BuildingComponent>();
  554. ASSERT_NE(deserialized, nullptr);
  555. }
  556. TEST_F(SerializationTest, AIControlledComponentSerialization) {
  557. auto *entity = world->create_entity();
  558. entity->add_component<AIControlledComponent>();
  559. QJsonObject json = Serialization::serialize_entity(entity);
  560. ASSERT_TRUE(json.contains("aiControlled"));
  561. EXPECT_TRUE(json["aiControlled"].toBool());
  562. }
  563. TEST_F(SerializationTest, AIControlledComponentRoundTrip) {
  564. auto *original_entity = world->create_entity();
  565. original_entity->add_component<AIControlledComponent>();
  566. QJsonObject json = Serialization::serialize_entity(original_entity);
  567. auto *new_entity = world->create_entity();
  568. Serialization::deserialize_entity(new_entity, json);
  569. auto *deserialized = new_entity->get_component<AIControlledComponent>();
  570. ASSERT_NE(deserialized, nullptr);
  571. }
  572. TEST_F(SerializationTest, CaptureComponentSerialization) {
  573. auto *entity = world->create_entity();
  574. auto *capture = entity->add_component<CaptureComponent>();
  575. capture->capturing_player_id = 2;
  576. capture->capture_progress = 7.5F;
  577. capture->required_time = 15.0F;
  578. capture->is_being_captured = true;
  579. QJsonObject json = Serialization::serialize_entity(entity);
  580. ASSERT_TRUE(json.contains("capture"));
  581. QJsonObject capture_obj = json["capture"].toObject();
  582. EXPECT_EQ(capture_obj["capturing_player_id"].toInt(), 2);
  583. EXPECT_FLOAT_EQ(capture_obj["capture_progress"].toDouble(), 7.5);
  584. EXPECT_FLOAT_EQ(capture_obj["required_time"].toDouble(), 15.0);
  585. EXPECT_TRUE(capture_obj["is_being_captured"].toBool());
  586. }
  587. TEST_F(SerializationTest, CaptureComponentRoundTrip) {
  588. auto *original_entity = world->create_entity();
  589. auto *capture = original_entity->add_component<CaptureComponent>();
  590. capture->capturing_player_id = 3;
  591. capture->capture_progress = 10.0F;
  592. capture->required_time = 20.0F;
  593. capture->is_being_captured = false;
  594. QJsonObject json = Serialization::serialize_entity(original_entity);
  595. auto *new_entity = world->create_entity();
  596. Serialization::deserialize_entity(new_entity, json);
  597. auto *deserialized = new_entity->get_component<CaptureComponent>();
  598. ASSERT_NE(deserialized, nullptr);
  599. EXPECT_EQ(deserialized->capturing_player_id, 3);
  600. EXPECT_FLOAT_EQ(deserialized->capture_progress, 10.0F);
  601. EXPECT_FLOAT_EQ(deserialized->required_time, 20.0F);
  602. EXPECT_FALSE(deserialized->is_being_captured);
  603. }
  604. TEST_F(SerializationTest, CompleteEntityWithAllComponents) {
  605. auto *entity = world->create_entity();
  606. auto *transform = entity->add_component<TransformComponent>();
  607. transform->position.x = 50.0F;
  608. transform->position.y = 10.0F;
  609. transform->position.z = 30.0F;
  610. auto *renderable =
  611. entity->add_component<RenderableComponent>("mesh.obj", "tex.png");
  612. renderable->visible = true;
  613. auto *unit = entity->add_component<UnitComponent>();
  614. unit->health = 100;
  615. unit->max_health = 100;
  616. auto *movement = entity->add_component<MovementComponent>();
  617. movement->has_target = true;
  618. movement->target_x = 100.0F;
  619. auto *attack = entity->add_component<AttackComponent>();
  620. attack->damage = 25;
  621. auto *attack_target = entity->add_component<AttackTargetComponent>();
  622. attack_target->target_id = 99;
  623. entity->add_component<BuildingComponent>();
  624. auto *production = entity->add_component<ProductionComponent>();
  625. production->in_progress = true;
  626. entity->add_component<AIControlledComponent>();
  627. auto *capture = entity->add_component<CaptureComponent>();
  628. capture->is_being_captured = true;
  629. auto *hold_mode = entity->add_component<HoldModeComponent>();
  630. hold_mode->active = true;
  631. auto *guard_mode = entity->add_component<GuardModeComponent>();
  632. guard_mode->active = false;
  633. guard_mode->guard_position_x = 25.0F;
  634. guard_mode->guard_position_z = 35.0F;
  635. auto *healer = entity->add_component<HealerComponent>();
  636. healer->healing_amount = 10;
  637. auto *catapult = entity->add_component<CatapultLoadingComponent>();
  638. catapult->state = CatapultLoadingComponent::LoadingState::Idle;
  639. QJsonObject json = Serialization::serialize_entity(entity);
  640. EXPECT_TRUE(json.contains("transform"));
  641. EXPECT_TRUE(json.contains("renderable"));
  642. EXPECT_TRUE(json.contains("unit"));
  643. EXPECT_TRUE(json.contains("movement"));
  644. EXPECT_TRUE(json.contains("attack"));
  645. EXPECT_TRUE(json.contains("attack_target"));
  646. EXPECT_TRUE(json.contains("building"));
  647. EXPECT_TRUE(json.contains("production"));
  648. EXPECT_TRUE(json.contains("aiControlled"));
  649. EXPECT_TRUE(json.contains("capture"));
  650. EXPECT_TRUE(json.contains("hold_mode"));
  651. EXPECT_TRUE(json.contains("guard_mode"));
  652. EXPECT_TRUE(json.contains("healer"));
  653. EXPECT_TRUE(json.contains("catapult_loading"));
  654. auto *new_entity = world->create_entity();
  655. Serialization::deserialize_entity(new_entity, json);
  656. EXPECT_NE(new_entity->get_component<TransformComponent>(), nullptr);
  657. EXPECT_NE(new_entity->get_component<RenderableComponent>(), nullptr);
  658. EXPECT_NE(new_entity->get_component<UnitComponent>(), nullptr);
  659. EXPECT_NE(new_entity->get_component<MovementComponent>(), nullptr);
  660. EXPECT_NE(new_entity->get_component<AttackComponent>(), nullptr);
  661. EXPECT_NE(new_entity->get_component<AttackTargetComponent>(), nullptr);
  662. EXPECT_NE(new_entity->get_component<BuildingComponent>(), nullptr);
  663. EXPECT_NE(new_entity->get_component<ProductionComponent>(), nullptr);
  664. EXPECT_NE(new_entity->get_component<AIControlledComponent>(), nullptr);
  665. EXPECT_NE(new_entity->get_component<CaptureComponent>(), nullptr);
  666. EXPECT_NE(new_entity->get_component<HoldModeComponent>(), nullptr);
  667. EXPECT_NE(new_entity->get_component<GuardModeComponent>(), nullptr);
  668. EXPECT_NE(new_entity->get_component<HealerComponent>(), nullptr);
  669. EXPECT_NE(new_entity->get_component<CatapultLoadingComponent>(), nullptr);
  670. }
  671. TEST_F(SerializationTest, EmptyWorldSerialization) {
  672. QJsonDocument doc = Serialization::serialize_world(world.get());
  673. ASSERT_TRUE(doc.isObject());
  674. QJsonObject world_obj = doc.object();
  675. EXPECT_TRUE(world_obj.contains("entities"));
  676. QJsonArray entities = world_obj["entities"].toArray();
  677. EXPECT_EQ(entities.size(), 0);
  678. }
  679. TEST_F(SerializationTest, HoldModeComponentSerialization) {
  680. auto *entity = world->create_entity();
  681. auto *hold_mode = entity->add_component<HoldModeComponent>();
  682. hold_mode->active = false;
  683. hold_mode->exit_cooldown = 1.5F;
  684. hold_mode->stand_up_duration = 3.0F;
  685. QJsonObject json = Serialization::serialize_entity(entity);
  686. ASSERT_TRUE(json.contains("hold_mode"));
  687. QJsonObject hold_mode_obj = json["hold_mode"].toObject();
  688. EXPECT_FALSE(hold_mode_obj["active"].toBool());
  689. EXPECT_FLOAT_EQ(hold_mode_obj["exit_cooldown"].toDouble(), 1.5);
  690. EXPECT_FLOAT_EQ(hold_mode_obj["stand_up_duration"].toDouble(), 3.0);
  691. }
  692. TEST_F(SerializationTest, HoldModeComponentRoundTrip) {
  693. auto *original_entity = world->create_entity();
  694. auto *hold_mode = original_entity->add_component<HoldModeComponent>();
  695. hold_mode->active = true;
  696. hold_mode->exit_cooldown = 2.5F;
  697. hold_mode->stand_up_duration = 4.0F;
  698. QJsonObject json = Serialization::serialize_entity(original_entity);
  699. auto *new_entity = world->create_entity();
  700. Serialization::deserialize_entity(new_entity, json);
  701. auto *deserialized = new_entity->get_component<HoldModeComponent>();
  702. ASSERT_NE(deserialized, nullptr);
  703. EXPECT_TRUE(deserialized->active);
  704. EXPECT_FLOAT_EQ(deserialized->exit_cooldown, 2.5F);
  705. EXPECT_FLOAT_EQ(deserialized->stand_up_duration, 4.0F);
  706. }
  707. TEST_F(SerializationTest, GuardModeComponentSerialization) {
  708. auto *entity = world->create_entity();
  709. auto *guard_mode = entity->add_component<GuardModeComponent>();
  710. guard_mode->active = true;
  711. guard_mode->guarded_entity_id = 42;
  712. guard_mode->guard_position_x = 100.0F;
  713. guard_mode->guard_position_z = 200.0F;
  714. guard_mode->guard_radius = 15.0F;
  715. guard_mode->returning_to_guard_position = true;
  716. guard_mode->has_guard_target = true;
  717. QJsonObject json = Serialization::serialize_entity(entity);
  718. ASSERT_TRUE(json.contains("guard_mode"));
  719. QJsonObject guard_mode_obj = json["guard_mode"].toObject();
  720. EXPECT_TRUE(guard_mode_obj["active"].toBool());
  721. EXPECT_EQ(guard_mode_obj["guarded_entity_id"].toVariant().toULongLong(),
  722. 42ULL);
  723. EXPECT_FLOAT_EQ(guard_mode_obj["guard_position_x"].toDouble(), 100.0);
  724. EXPECT_FLOAT_EQ(guard_mode_obj["guard_position_z"].toDouble(), 200.0);
  725. EXPECT_FLOAT_EQ(guard_mode_obj["guard_radius"].toDouble(), 15.0);
  726. EXPECT_TRUE(guard_mode_obj["returning_to_guard_position"].toBool());
  727. EXPECT_TRUE(guard_mode_obj["has_guard_target"].toBool());
  728. }
  729. TEST_F(SerializationTest, GuardModeComponentRoundTrip) {
  730. auto *original_entity = world->create_entity();
  731. auto *guard_mode = original_entity->add_component<GuardModeComponent>();
  732. guard_mode->active = true;
  733. guard_mode->guarded_entity_id = 99;
  734. guard_mode->guard_position_x = 50.0F;
  735. guard_mode->guard_position_z = 75.0F;
  736. guard_mode->guard_radius = 12.0F;
  737. guard_mode->returning_to_guard_position = false;
  738. guard_mode->has_guard_target = true;
  739. QJsonObject json = Serialization::serialize_entity(original_entity);
  740. auto *new_entity = world->create_entity();
  741. Serialization::deserialize_entity(new_entity, json);
  742. auto *deserialized = new_entity->get_component<GuardModeComponent>();
  743. ASSERT_NE(deserialized, nullptr);
  744. EXPECT_TRUE(deserialized->active);
  745. EXPECT_EQ(deserialized->guarded_entity_id, 99U);
  746. EXPECT_FLOAT_EQ(deserialized->guard_position_x, 50.0F);
  747. EXPECT_FLOAT_EQ(deserialized->guard_position_z, 75.0F);
  748. EXPECT_FLOAT_EQ(deserialized->guard_radius, 12.0F);
  749. EXPECT_FALSE(deserialized->returning_to_guard_position);
  750. EXPECT_TRUE(deserialized->has_guard_target);
  751. }
  752. TEST_F(SerializationTest, HealerComponentSerialization) {
  753. auto *entity = world->create_entity();
  754. auto *healer = entity->add_component<HealerComponent>();
  755. healer->healing_range = 12.0F;
  756. healer->healing_amount = 10;
  757. healer->healing_cooldown = 3.0F;
  758. healer->time_since_last_heal = 1.0F;
  759. QJsonObject json = Serialization::serialize_entity(entity);
  760. ASSERT_TRUE(json.contains("healer"));
  761. QJsonObject healer_obj = json["healer"].toObject();
  762. EXPECT_FLOAT_EQ(healer_obj["healing_range"].toDouble(), 12.0);
  763. EXPECT_EQ(healer_obj["healing_amount"].toInt(), 10);
  764. EXPECT_FLOAT_EQ(healer_obj["healing_cooldown"].toDouble(), 3.0);
  765. EXPECT_FLOAT_EQ(healer_obj["time_since_last_heal"].toDouble(), 1.0);
  766. }
  767. TEST_F(SerializationTest, HealerComponentRoundTrip) {
  768. auto *original_entity = world->create_entity();
  769. auto *healer = original_entity->add_component<HealerComponent>();
  770. healer->healing_range = 15.0F;
  771. healer->healing_amount = 8;
  772. healer->healing_cooldown = 4.0F;
  773. healer->time_since_last_heal = 2.0F;
  774. QJsonObject json = Serialization::serialize_entity(original_entity);
  775. auto *new_entity = world->create_entity();
  776. Serialization::deserialize_entity(new_entity, json);
  777. auto *deserialized = new_entity->get_component<HealerComponent>();
  778. ASSERT_NE(deserialized, nullptr);
  779. EXPECT_FLOAT_EQ(deserialized->healing_range, 15.0F);
  780. EXPECT_EQ(deserialized->healing_amount, 8);
  781. EXPECT_FLOAT_EQ(deserialized->healing_cooldown, 4.0F);
  782. EXPECT_FLOAT_EQ(deserialized->time_since_last_heal, 2.0F);
  783. }
  784. TEST_F(SerializationTest, CatapultLoadingComponentSerialization) {
  785. auto *entity = world->create_entity();
  786. auto *catapult = entity->add_component<CatapultLoadingComponent>();
  787. catapult->state = CatapultLoadingComponent::LoadingState::Loading;
  788. catapult->loading_time = 1.0F;
  789. catapult->loading_duration = 3.0F;
  790. catapult->firing_time = 0.0F;
  791. catapult->firing_duration = 1.0F;
  792. catapult->target_id = 42;
  793. catapult->target_locked_x = 100.0F;
  794. catapult->target_locked_y = 50.0F;
  795. catapult->target_locked_z = 200.0F;
  796. catapult->target_position_locked = true;
  797. QJsonObject json = Serialization::serialize_entity(entity);
  798. ASSERT_TRUE(json.contains("catapult_loading"));
  799. QJsonObject catapult_obj = json["catapult_loading"].toObject();
  800. EXPECT_EQ(catapult_obj["state"].toInt(),
  801. static_cast<int>(CatapultLoadingComponent::LoadingState::Loading));
  802. EXPECT_FLOAT_EQ(catapult_obj["loading_time"].toDouble(), 1.0);
  803. EXPECT_FLOAT_EQ(catapult_obj["loading_duration"].toDouble(), 3.0);
  804. EXPECT_FLOAT_EQ(catapult_obj["firing_time"].toDouble(), 0.0);
  805. EXPECT_FLOAT_EQ(catapult_obj["firing_duration"].toDouble(), 1.0);
  806. EXPECT_EQ(catapult_obj["target_id"].toVariant().toULongLong(), 42ULL);
  807. EXPECT_FLOAT_EQ(catapult_obj["target_locked_x"].toDouble(), 100.0);
  808. EXPECT_FLOAT_EQ(catapult_obj["target_locked_y"].toDouble(), 50.0);
  809. EXPECT_FLOAT_EQ(catapult_obj["target_locked_z"].toDouble(), 200.0);
  810. EXPECT_TRUE(catapult_obj["target_position_locked"].toBool());
  811. }
  812. TEST_F(SerializationTest, CatapultLoadingComponentRoundTrip) {
  813. auto *original_entity = world->create_entity();
  814. auto *catapult = original_entity->add_component<CatapultLoadingComponent>();
  815. catapult->state = CatapultLoadingComponent::LoadingState::ReadyToFire;
  816. catapult->loading_time = 2.0F;
  817. catapult->loading_duration = 4.0F;
  818. catapult->firing_time = 0.25F;
  819. catapult->firing_duration = 0.75F;
  820. catapult->target_id = 99;
  821. catapult->target_locked_x = 150.0F;
  822. catapult->target_locked_y = 75.0F;
  823. catapult->target_locked_z = 250.0F;
  824. catapult->target_position_locked = false;
  825. QJsonObject json = Serialization::serialize_entity(original_entity);
  826. auto *new_entity = world->create_entity();
  827. Serialization::deserialize_entity(new_entity, json);
  828. auto *deserialized = new_entity->get_component<CatapultLoadingComponent>();
  829. ASSERT_NE(deserialized, nullptr);
  830. EXPECT_EQ(deserialized->state,
  831. CatapultLoadingComponent::LoadingState::ReadyToFire);
  832. EXPECT_FLOAT_EQ(deserialized->loading_time, 2.0F);
  833. EXPECT_FLOAT_EQ(deserialized->loading_duration, 4.0F);
  834. EXPECT_FLOAT_EQ(deserialized->firing_time, 0.25F);
  835. EXPECT_FLOAT_EQ(deserialized->firing_duration, 0.75F);
  836. EXPECT_EQ(deserialized->target_id, 99U);
  837. EXPECT_FLOAT_EQ(deserialized->target_locked_x, 150.0F);
  838. EXPECT_FLOAT_EQ(deserialized->target_locked_y, 75.0F);
  839. EXPECT_FLOAT_EQ(deserialized->target_locked_z, 250.0F);
  840. EXPECT_FALSE(deserialized->target_position_locked);
  841. }
  842. // ============================================================================
  843. // Integration Tests: Multi-Unit Battlefield State Preservation
  844. // ============================================================================
  845. TEST_F(SerializationTest, MultipleUnitsPositionsAndHealthPreserved) {
  846. // Create a battlefield with multiple units at different positions
  847. struct UnitData {
  848. float x, y, z;
  849. int health;
  850. int max_health;
  851. int owner_id;
  852. Game::Units::SpawnType spawn_type;
  853. };
  854. std::vector<UnitData> original_units = {
  855. {10.0F, 0.0F, 20.0F, 80, 100, 1, Game::Units::SpawnType::Archer},
  856. {15.5F, 1.0F, 25.5F, 45, 100, 1, Game::Units::SpawnType::Spearman},
  857. {30.0F, 0.0F, 40.0F, 100, 100, 2, Game::Units::SpawnType::Knight},
  858. {35.5F, 2.0F, 45.5F, 60, 150, 2, Game::Units::SpawnType::HorseArcher},
  859. {50.0F, 0.5F, 60.0F, 25, 80, 1, Game::Units::SpawnType::Catapult},
  860. };
  861. std::vector<EntityID> entity_ids;
  862. for (const auto &unit_data : original_units) {
  863. auto *entity = world->create_entity();
  864. entity_ids.push_back(entity->get_id());
  865. auto *transform = entity->add_component<TransformComponent>();
  866. transform->position.x = unit_data.x;
  867. transform->position.y = unit_data.y;
  868. transform->position.z = unit_data.z;
  869. auto *unit = entity->add_component<UnitComponent>();
  870. unit->health = unit_data.health;
  871. unit->max_health = unit_data.max_health;
  872. unit->owner_id = unit_data.owner_id;
  873. unit->spawn_type = unit_data.spawn_type;
  874. }
  875. // Serialize and deserialize the world
  876. QJsonDocument doc = Serialization::serialize_world(world.get());
  877. auto restored_world = std::make_unique<World>();
  878. Serialization::deserialize_world(restored_world.get(), doc);
  879. // Verify all units are restored with exact positions and health
  880. const auto &entities = restored_world->get_entities();
  881. EXPECT_EQ(entities.size(), original_units.size());
  882. for (size_t i = 0; i < entity_ids.size(); ++i) {
  883. auto *entity = restored_world->get_entity(entity_ids[i]);
  884. ASSERT_NE(entity, nullptr) << "Entity " << i << " not found";
  885. auto *transform = entity->get_component<TransformComponent>();
  886. ASSERT_NE(transform, nullptr);
  887. EXPECT_FLOAT_EQ(transform->position.x, original_units[i].x)
  888. << "Unit " << i << " X position mismatch";
  889. EXPECT_FLOAT_EQ(transform->position.y, original_units[i].y)
  890. << "Unit " << i << " Y position mismatch";
  891. EXPECT_FLOAT_EQ(transform->position.z, original_units[i].z)
  892. << "Unit " << i << " Z position mismatch";
  893. auto *unit = entity->get_component<UnitComponent>();
  894. ASSERT_NE(unit, nullptr);
  895. EXPECT_EQ(unit->health, original_units[i].health)
  896. << "Unit " << i << " health mismatch";
  897. EXPECT_EQ(unit->max_health, original_units[i].max_health)
  898. << "Unit " << i << " max_health mismatch";
  899. EXPECT_EQ(unit->owner_id, original_units[i].owner_id)
  900. << "Unit " << i << " owner_id mismatch";
  901. EXPECT_EQ(unit->spawn_type, original_units[i].spawn_type)
  902. << "Unit " << i << " spawn_type mismatch";
  903. }
  904. }
  905. TEST_F(SerializationTest, OwnerRegistryTeamsAndColorsPreserved) {
  906. // Setup owner registry with teams and custom colors
  907. auto &registry = Game::Systems::OwnerRegistry::instance();
  908. registry.clear();
  909. // Register players with specific teams and colors
  910. int player1 =
  911. registry.register_owner(Game::Systems::OwnerType::Player, "Blue Kingdom");
  912. int player2 =
  913. registry.register_owner(Game::Systems::OwnerType::AI, "Red Empire");
  914. int player3 = registry.register_owner(Game::Systems::OwnerType::Player,
  915. "Green Alliance");
  916. // Set teams (player1 and player3 are allies)
  917. registry.set_owner_team(player1, 1);
  918. registry.set_owner_team(player2, 2);
  919. registry.set_owner_team(player3, 1);
  920. // Set custom colors
  921. registry.set_owner_color(player1, 0.1F, 0.2F, 0.9F);
  922. registry.set_owner_color(player2, 0.9F, 0.1F, 0.1F);
  923. registry.set_owner_color(player3, 0.1F, 0.9F, 0.2F);
  924. registry.set_local_player_id(player1);
  925. // Create some entities owned by these players
  926. for (int i = 0; i < 3; ++i) {
  927. auto *entity = world->create_entity();
  928. auto *unit = entity->add_component<UnitComponent>();
  929. unit->owner_id = player1;
  930. }
  931. for (int i = 0; i < 2; ++i) {
  932. auto *entity = world->create_entity();
  933. auto *unit = entity->add_component<UnitComponent>();
  934. unit->owner_id = player2;
  935. }
  936. // Serialize world (includes owner_registry)
  937. QJsonDocument doc = Serialization::serialize_world(world.get());
  938. // Clear registry and restore
  939. registry.clear();
  940. auto restored_world = std::make_unique<World>();
  941. Serialization::deserialize_world(restored_world.get(), doc);
  942. // Verify owner registry state is preserved
  943. EXPECT_EQ(registry.get_local_player_id(), player1);
  944. // Verify teams are preserved
  945. EXPECT_EQ(registry.get_owner_team(player1), 1);
  946. EXPECT_EQ(registry.get_owner_team(player2), 2);
  947. EXPECT_EQ(registry.get_owner_team(player3), 1);
  948. // Verify alliances are preserved
  949. EXPECT_TRUE(registry.are_allies(player1, player3));
  950. EXPECT_TRUE(registry.are_enemies(player1, player2));
  951. EXPECT_TRUE(registry.are_enemies(player2, player3));
  952. // Verify colors are preserved
  953. auto color1 = registry.get_owner_color(player1);
  954. EXPECT_FLOAT_EQ(color1[0], 0.1F);
  955. EXPECT_FLOAT_EQ(color1[1], 0.2F);
  956. EXPECT_FLOAT_EQ(color1[2], 0.9F);
  957. auto color2 = registry.get_owner_color(player2);
  958. EXPECT_FLOAT_EQ(color2[0], 0.9F);
  959. EXPECT_FLOAT_EQ(color2[1], 0.1F);
  960. EXPECT_FLOAT_EQ(color2[2], 0.1F);
  961. auto color3 = registry.get_owner_color(player3);
  962. EXPECT_FLOAT_EQ(color3[0], 0.1F);
  963. EXPECT_FLOAT_EQ(color3[1], 0.9F);
  964. EXPECT_FLOAT_EQ(color3[2], 0.2F);
  965. // Verify owner names are preserved
  966. EXPECT_EQ(registry.get_owner_name(player1), "Blue Kingdom");
  967. EXPECT_EQ(registry.get_owner_name(player2), "Red Empire");
  968. EXPECT_EQ(registry.get_owner_name(player3), "Green Alliance");
  969. // Verify owner types are preserved
  970. EXPECT_TRUE(registry.is_player(player1));
  971. EXPECT_TRUE(registry.is_ai(player2));
  972. EXPECT_TRUE(registry.is_player(player3));
  973. // Clean up
  974. registry.clear();
  975. }
  976. TEST_F(SerializationTest, BuildingOwnershipAndCaptureStatePreserved) {
  977. // Create buildings (barracks/villages) with different ownership and capture
  978. // states
  979. struct BuildingData {
  980. float x, z;
  981. int owner_id;
  982. int capturing_player_id;
  983. float capture_progress;
  984. bool is_being_captured;
  985. };
  986. std::vector<BuildingData> buildings = {
  987. {100.0F, 100.0F, 1, -1, 0.0F,
  988. false}, // Owned by player 1, not being captured
  989. {200.0F, 200.0F, 2, 1, 7.5F,
  990. true}, // Owned by player 2, being captured by player 1
  991. {300.0F, 300.0F, 1, 2, 15.0F,
  992. true}, // Owned by player 1, being captured by player 2
  993. {400.0F, 400.0F, -1, -1, 0.0F, false}, // Neutral building
  994. };
  995. std::vector<EntityID> building_ids;
  996. for (const auto &bldg : buildings) {
  997. auto *entity = world->create_entity();
  998. building_ids.push_back(entity->get_id());
  999. auto *transform = entity->add_component<TransformComponent>();
  1000. transform->position.x = bldg.x;
  1001. transform->position.z = bldg.z;
  1002. entity->add_component<BuildingComponent>();
  1003. auto *unit = entity->add_component<UnitComponent>();
  1004. unit->owner_id = bldg.owner_id;
  1005. auto *capture = entity->add_component<CaptureComponent>();
  1006. capture->capturing_player_id = bldg.capturing_player_id;
  1007. capture->capture_progress = bldg.capture_progress;
  1008. capture->is_being_captured = bldg.is_being_captured;
  1009. }
  1010. // Serialize and restore
  1011. QJsonDocument doc = Serialization::serialize_world(world.get());
  1012. auto restored_world = std::make_unique<World>();
  1013. Serialization::deserialize_world(restored_world.get(), doc);
  1014. // Verify all buildings are restored with correct ownership and capture state
  1015. for (size_t i = 0; i < building_ids.size(); ++i) {
  1016. auto *entity = restored_world->get_entity(building_ids[i]);
  1017. ASSERT_NE(entity, nullptr) << "Building " << i << " not found";
  1018. auto *transform = entity->get_component<TransformComponent>();
  1019. ASSERT_NE(transform, nullptr);
  1020. EXPECT_FLOAT_EQ(transform->position.x, buildings[i].x)
  1021. << "Building " << i << " X position mismatch";
  1022. EXPECT_FLOAT_EQ(transform->position.z, buildings[i].z)
  1023. << "Building " << i << " Z position mismatch";
  1024. EXPECT_NE(entity->get_component<BuildingComponent>(), nullptr);
  1025. auto *unit = entity->get_component<UnitComponent>();
  1026. ASSERT_NE(unit, nullptr);
  1027. EXPECT_EQ(unit->owner_id, buildings[i].owner_id)
  1028. << "Building " << i << " owner mismatch";
  1029. auto *capture = entity->get_component<CaptureComponent>();
  1030. ASSERT_NE(capture, nullptr);
  1031. EXPECT_EQ(capture->capturing_player_id, buildings[i].capturing_player_id)
  1032. << "Building " << i << " capturing_player_id mismatch";
  1033. EXPECT_FLOAT_EQ(capture->capture_progress, buildings[i].capture_progress)
  1034. << "Building " << i << " capture_progress mismatch";
  1035. EXPECT_EQ(capture->is_being_captured, buildings[i].is_being_captured)
  1036. << "Building " << i << " is_being_captured mismatch";
  1037. }
  1038. }
  1039. TEST_F(SerializationTest, UnitMovementStatePreserved) {
  1040. // Create units with active movement paths
  1041. auto *entity = world->create_entity();
  1042. auto entity_id = entity->get_id();
  1043. auto *transform = entity->add_component<TransformComponent>();
  1044. transform->position.x = 10.0F;
  1045. transform->position.y = 0.0F;
  1046. transform->position.z = 20.0F;
  1047. auto *unit = entity->add_component<UnitComponent>();
  1048. unit->owner_id = 1;
  1049. unit->health = 85;
  1050. auto *movement = entity->add_component<MovementComponent>();
  1051. movement->has_target = true;
  1052. movement->target_x = 50.0F;
  1053. movement->target_y = 60.0F;
  1054. movement->goal_x = 55.0F;
  1055. movement->goal_y = 65.0F;
  1056. movement->vx = 2.5F;
  1057. movement->vz = 3.0F;
  1058. // Add path waypoints
  1059. movement->path.emplace_back(20.0F, 30.0F);
  1060. movement->path.emplace_back(35.0F, 45.0F);
  1061. movement->path.emplace_back(50.0F, 60.0F);
  1062. const size_t expected_path_size = movement->path.size();
  1063. // Serialize and restore
  1064. QJsonDocument doc = Serialization::serialize_world(world.get());
  1065. auto restored_world = std::make_unique<World>();
  1066. Serialization::deserialize_world(restored_world.get(), doc);
  1067. // Verify movement state is preserved
  1068. auto *restored_entity = restored_world->get_entity(entity_id);
  1069. ASSERT_NE(restored_entity, nullptr);
  1070. auto *restored_movement = restored_entity->get_component<MovementComponent>();
  1071. ASSERT_NE(restored_movement, nullptr);
  1072. EXPECT_TRUE(restored_movement->has_target);
  1073. EXPECT_FLOAT_EQ(restored_movement->target_x, 50.0F);
  1074. EXPECT_FLOAT_EQ(restored_movement->target_y, 60.0F);
  1075. EXPECT_FLOAT_EQ(restored_movement->goal_x, 55.0F);
  1076. EXPECT_FLOAT_EQ(restored_movement->goal_y, 65.0F);
  1077. EXPECT_FLOAT_EQ(restored_movement->vx, 2.5F);
  1078. EXPECT_FLOAT_EQ(restored_movement->vz, 3.0F);
  1079. // Verify path is preserved
  1080. ASSERT_EQ(restored_movement->path.size(), expected_path_size);
  1081. EXPECT_FLOAT_EQ(restored_movement->path[0].first, 20.0F);
  1082. EXPECT_FLOAT_EQ(restored_movement->path[0].second, 30.0F);
  1083. EXPECT_FLOAT_EQ(restored_movement->path[1].first, 35.0F);
  1084. EXPECT_FLOAT_EQ(restored_movement->path[1].second, 45.0F);
  1085. EXPECT_FLOAT_EQ(restored_movement->path[2].first, 50.0F);
  1086. EXPECT_FLOAT_EQ(restored_movement->path[2].second, 60.0F);
  1087. }
  1088. TEST_F(SerializationTest, CombatStatePreserved) {
  1089. // Create units engaged in combat
  1090. auto *attacker = world->create_entity();
  1091. auto *defender = world->create_entity();
  1092. auto attacker_id = attacker->get_id();
  1093. auto defender_id = defender->get_id();
  1094. // Setup attacker
  1095. auto *attacker_transform = attacker->add_component<TransformComponent>();
  1096. attacker_transform->position.x = 10.0F;
  1097. attacker_transform->position.z = 10.0F;
  1098. auto *attacker_unit = attacker->add_component<UnitComponent>();
  1099. attacker_unit->owner_id = 1;
  1100. attacker_unit->health = 90;
  1101. auto *attacker_attack = attacker->add_component<AttackComponent>();
  1102. attacker_attack->damage = 25;
  1103. attacker_attack->range = 15.0F;
  1104. attacker_attack->current_mode = AttackComponent::CombatMode::Melee;
  1105. attacker_attack->in_melee_lock = true;
  1106. attacker_attack->melee_lock_target_id = defender_id;
  1107. auto *attacker_target = attacker->add_component<AttackTargetComponent>();
  1108. attacker_target->target_id = defender_id;
  1109. attacker_target->should_chase = true;
  1110. // Setup defender
  1111. auto *defender_transform = defender->add_component<TransformComponent>();
  1112. defender_transform->position.x = 12.0F;
  1113. defender_transform->position.z = 12.0F;
  1114. auto *defender_unit = defender->add_component<UnitComponent>();
  1115. defender_unit->owner_id = 2;
  1116. defender_unit->health = 60;
  1117. auto *defender_attack = defender->add_component<AttackComponent>();
  1118. defender_attack->damage = 20;
  1119. defender_attack->in_melee_lock = true;
  1120. defender_attack->melee_lock_target_id = attacker_id;
  1121. // Serialize and restore
  1122. QJsonDocument doc = Serialization::serialize_world(world.get());
  1123. auto restored_world = std::make_unique<World>();
  1124. Serialization::deserialize_world(restored_world.get(), doc);
  1125. // Verify combat state is preserved
  1126. auto *restored_attacker = restored_world->get_entity(attacker_id);
  1127. auto *restored_defender = restored_world->get_entity(defender_id);
  1128. ASSERT_NE(restored_attacker, nullptr);
  1129. ASSERT_NE(restored_defender, nullptr);
  1130. auto *restored_attacker_attack =
  1131. restored_attacker->get_component<AttackComponent>();
  1132. ASSERT_NE(restored_attacker_attack, nullptr);
  1133. EXPECT_TRUE(restored_attacker_attack->in_melee_lock);
  1134. EXPECT_EQ(restored_attacker_attack->melee_lock_target_id, defender_id);
  1135. EXPECT_EQ(restored_attacker_attack->current_mode,
  1136. AttackComponent::CombatMode::Melee);
  1137. auto *restored_attacker_target =
  1138. restored_attacker->get_component<AttackTargetComponent>();
  1139. ASSERT_NE(restored_attacker_target, nullptr);
  1140. EXPECT_EQ(restored_attacker_target->target_id, defender_id);
  1141. EXPECT_TRUE(restored_attacker_target->should_chase);
  1142. auto *restored_defender_attack =
  1143. restored_defender->get_component<AttackComponent>();
  1144. ASSERT_NE(restored_defender_attack, nullptr);
  1145. EXPECT_TRUE(restored_defender_attack->in_melee_lock);
  1146. EXPECT_EQ(restored_defender_attack->melee_lock_target_id, attacker_id);
  1147. // Verify health is preserved
  1148. auto *restored_attacker_unit =
  1149. restored_attacker->get_component<UnitComponent>();
  1150. auto *restored_defender_unit =
  1151. restored_defender->get_component<UnitComponent>();
  1152. EXPECT_EQ(restored_attacker_unit->health, 90);
  1153. EXPECT_EQ(restored_defender_unit->health, 60);
  1154. }
  1155. TEST_F(SerializationTest, NationIdentityPreserved) {
  1156. // Create units from different nations
  1157. auto *roman_unit = world->create_entity();
  1158. auto *carthage_unit = world->create_entity();
  1159. auto roman_id = roman_unit->get_id();
  1160. auto carthage_id = carthage_unit->get_id();
  1161. auto *roman_unit_comp = roman_unit->add_component<UnitComponent>();
  1162. roman_unit_comp->nation_id = Game::Systems::NationID::RomanRepublic;
  1163. roman_unit_comp->spawn_type = Game::Units::SpawnType::Spearman;
  1164. auto *carthage_unit_comp = carthage_unit->add_component<UnitComponent>();
  1165. carthage_unit_comp->nation_id = Game::Systems::NationID::Carthage;
  1166. carthage_unit_comp->spawn_type = Game::Units::SpawnType::Archer;
  1167. // Serialize and restore
  1168. QJsonDocument doc = Serialization::serialize_world(world.get());
  1169. auto restored_world = std::make_unique<World>();
  1170. Serialization::deserialize_world(restored_world.get(), doc);
  1171. // Verify nation IDs are preserved
  1172. auto *restored_roman = restored_world->get_entity(roman_id);
  1173. auto *restored_carthage = restored_world->get_entity(carthage_id);
  1174. auto *restored_roman_comp = restored_roman->get_component<UnitComponent>();
  1175. EXPECT_EQ(restored_roman_comp->nation_id,
  1176. Game::Systems::NationID::RomanRepublic);
  1177. EXPECT_EQ(restored_roman_comp->spawn_type, Game::Units::SpawnType::Spearman);
  1178. auto *restored_carthage_comp =
  1179. restored_carthage->get_component<UnitComponent>();
  1180. EXPECT_EQ(restored_carthage_comp->nation_id,
  1181. Game::Systems::NationID::Carthage);
  1182. EXPECT_EQ(restored_carthage_comp->spawn_type, Game::Units::SpawnType::Archer);
  1183. }
  1184. TEST_F(SerializationTest, ElephantComponentSerialization) {
  1185. auto *entity = world->create_entity();
  1186. auto *elephant = entity->add_component<ElephantComponent>();
  1187. elephant->charge_state = ElephantComponent::ChargeState::Charging;
  1188. elephant->charge_speed_multiplier = 2.0F;
  1189. elephant->charge_duration = 3.5F;
  1190. elephant->charge_cooldown = 5.0F;
  1191. elephant->trample_radius = 3.0F;
  1192. elephant->trample_damage = 50;
  1193. elephant->trample_damage_accumulator = 1.5F;
  1194. elephant->is_panicked = true;
  1195. elephant->panic_duration = 2.0F;
  1196. QJsonObject json = Serialization::serialize_entity(entity);
  1197. ASSERT_TRUE(json.contains("elephant"));
  1198. QJsonObject elephant_obj = json["elephant"].toObject();
  1199. EXPECT_EQ(elephant_obj["charge_state"].toInt(),
  1200. static_cast<int>(ElephantComponent::ChargeState::Charging));
  1201. EXPECT_FLOAT_EQ(elephant_obj["charge_speed_multiplier"].toDouble(), 2.0);
  1202. EXPECT_FLOAT_EQ(elephant_obj["charge_duration"].toDouble(), 3.5);
  1203. EXPECT_FLOAT_EQ(elephant_obj["charge_cooldown"].toDouble(), 5.0);
  1204. EXPECT_FLOAT_EQ(elephant_obj["trample_radius"].toDouble(), 3.0);
  1205. EXPECT_EQ(elephant_obj["trample_damage"].toInt(), 50);
  1206. EXPECT_FLOAT_EQ(elephant_obj["trample_damage_accumulator"].toDouble(), 1.5);
  1207. EXPECT_TRUE(elephant_obj["is_panicked"].toBool());
  1208. EXPECT_FLOAT_EQ(elephant_obj["panic_duration"].toDouble(), 2.0);
  1209. }
  1210. TEST_F(SerializationTest, ElephantComponentRoundTrip) {
  1211. auto *original_entity = world->create_entity();
  1212. auto *elephant = original_entity->add_component<ElephantComponent>();
  1213. elephant->charge_state = ElephantComponent::ChargeState::Trampling;
  1214. elephant->charge_speed_multiplier = 1.9F;
  1215. elephant->charge_duration = 4.0F;
  1216. elephant->charge_cooldown = 6.0F;
  1217. elephant->trample_radius = 2.8F;
  1218. elephant->trample_damage = 45;
  1219. elephant->trample_damage_accumulator = 2.0F;
  1220. elephant->is_panicked = false;
  1221. elephant->panic_duration = 0.0F;
  1222. QJsonObject json = Serialization::serialize_entity(original_entity);
  1223. auto *new_entity = world->create_entity();
  1224. Serialization::deserialize_entity(new_entity, json);
  1225. auto *deserialized = new_entity->get_component<ElephantComponent>();
  1226. ASSERT_NE(deserialized, nullptr);
  1227. EXPECT_EQ(deserialized->charge_state,
  1228. ElephantComponent::ChargeState::Trampling);
  1229. EXPECT_FLOAT_EQ(deserialized->charge_speed_multiplier, 1.9F);
  1230. EXPECT_FLOAT_EQ(deserialized->charge_duration, 4.0F);
  1231. EXPECT_FLOAT_EQ(deserialized->charge_cooldown, 6.0F);
  1232. EXPECT_FLOAT_EQ(deserialized->trample_radius, 2.8F);
  1233. EXPECT_EQ(deserialized->trample_damage, 45);
  1234. EXPECT_FLOAT_EQ(deserialized->trample_damage_accumulator, 2.0F);
  1235. EXPECT_FALSE(deserialized->is_panicked);
  1236. EXPECT_FLOAT_EQ(deserialized->panic_duration, 0.0F);
  1237. }
  1238. TEST_F(SerializationTest, ElephantStompImpactComponentSerialization) {
  1239. auto *entity = world->create_entity();
  1240. auto *stomp_impact = entity->add_component<ElephantStompImpactComponent>();
  1241. stomp_impact->impacts.push_back({10.0F, 20.0F, 1.5F});
  1242. stomp_impact->impacts.push_back({30.0F, 40.0F, 2.5F});
  1243. stomp_impact->impacts.push_back({50.0F, 60.0F, 3.5F});
  1244. QJsonObject json = Serialization::serialize_entity(entity);
  1245. ASSERT_TRUE(json.contains("elephant_stomp_impacts"));
  1246. QJsonArray impacts_array = json["elephant_stomp_impacts"].toArray();
  1247. EXPECT_EQ(impacts_array.size(), 3);
  1248. QJsonObject impact0 = impacts_array[0].toObject();
  1249. EXPECT_FLOAT_EQ(impact0["x"].toDouble(), 10.0);
  1250. EXPECT_FLOAT_EQ(impact0["z"].toDouble(), 20.0);
  1251. EXPECT_FLOAT_EQ(impact0["time"].toDouble(), 1.5);
  1252. QJsonObject impact1 = impacts_array[1].toObject();
  1253. EXPECT_FLOAT_EQ(impact1["x"].toDouble(), 30.0);
  1254. EXPECT_FLOAT_EQ(impact1["z"].toDouble(), 40.0);
  1255. EXPECT_FLOAT_EQ(impact1["time"].toDouble(), 2.5);
  1256. }
  1257. TEST_F(SerializationTest, ElephantStompImpactComponentRoundTrip) {
  1258. auto *original_entity = world->create_entity();
  1259. auto *stomp_impact =
  1260. original_entity->add_component<ElephantStompImpactComponent>();
  1261. stomp_impact->impacts.push_back({15.0F, 25.0F, 1.0F});
  1262. stomp_impact->impacts.push_back({35.0F, 45.0F, 2.0F});
  1263. QJsonObject json = Serialization::serialize_entity(original_entity);
  1264. auto *new_entity = world->create_entity();
  1265. Serialization::deserialize_entity(new_entity, json);
  1266. auto *deserialized =
  1267. new_entity->get_component<ElephantStompImpactComponent>();
  1268. ASSERT_NE(deserialized, nullptr);
  1269. ASSERT_EQ(deserialized->impacts.size(), 2UL);
  1270. EXPECT_FLOAT_EQ(deserialized->impacts[0].x, 15.0F);
  1271. EXPECT_FLOAT_EQ(deserialized->impacts[0].z, 25.0F);
  1272. EXPECT_FLOAT_EQ(deserialized->impacts[0].time, 1.0F);
  1273. EXPECT_FLOAT_EQ(deserialized->impacts[1].x, 35.0F);
  1274. EXPECT_FLOAT_EQ(deserialized->impacts[1].z, 45.0F);
  1275. EXPECT_FLOAT_EQ(deserialized->impacts[1].time, 2.0F);
  1276. }