|
|
@@ -3,6 +3,7 @@
|
|
|
#include "core/serialization.h"
|
|
|
#include "core/world.h"
|
|
|
#include "systems/nation_id.h"
|
|
|
+#include "systems/owner_registry.h"
|
|
|
#include "units/spawn_type.h"
|
|
|
#include "units/troop_type.h"
|
|
|
#include <QJsonArray>
|
|
|
@@ -28,7 +29,7 @@ TEST_F(SerializationTest, EntitySerializationBasic) {
|
|
|
|
|
|
auto entity_id = entity->get_id();
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
EXPECT_TRUE(json.contains("id"));
|
|
|
EXPECT_EQ(json["id"].toVariant().toULongLong(),
|
|
|
@@ -51,7 +52,7 @@ TEST_F(SerializationTest, TransformComponentSerialization) {
|
|
|
transform->has_desired_yaw = true;
|
|
|
transform->desired_yaw = 45.0F;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("transform"));
|
|
|
QJsonObject transform_obj = json["transform"].toObject();
|
|
|
@@ -69,6 +70,41 @@ TEST_F(SerializationTest, TransformComponentSerialization) {
|
|
|
EXPECT_FLOAT_EQ(transform_obj["desired_yaw"].toDouble(), 45.0);
|
|
|
}
|
|
|
|
|
|
+TEST_F(SerializationTest, TransformComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *transform = original_entity->add_component<TransformComponent>();
|
|
|
+ transform->position.x = 15.0F;
|
|
|
+ transform->position.y = 25.0F;
|
|
|
+ transform->position.z = 35.0F;
|
|
|
+ transform->rotation.x = 1.0F;
|
|
|
+ transform->rotation.y = 2.0F;
|
|
|
+ transform->rotation.z = 3.0F;
|
|
|
+ transform->scale.x = 1.5F;
|
|
|
+ transform->scale.y = 2.5F;
|
|
|
+ transform->scale.z = 3.5F;
|
|
|
+ transform->has_desired_yaw = true;
|
|
|
+ transform->desired_yaw = 90.0F;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<TransformComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->position.x, 15.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->position.y, 25.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->position.z, 35.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->rotation.x, 1.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->rotation.y, 2.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->rotation.z, 3.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->scale.x, 1.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->scale.y, 2.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->scale.z, 3.5F);
|
|
|
+ EXPECT_TRUE(deserialized->has_desired_yaw);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->desired_yaw, 90.0F);
|
|
|
+}
|
|
|
+
|
|
|
TEST_F(SerializationTest, UnitComponentSerialization) {
|
|
|
auto *entity = world->create_entity();
|
|
|
auto *unit = entity->add_component<UnitComponent>();
|
|
|
@@ -81,7 +117,7 @@ TEST_F(SerializationTest, UnitComponentSerialization) {
|
|
|
unit->owner_id = 1;
|
|
|
unit->nation_id = Game::Systems::NationID::RomanRepublic;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("unit"));
|
|
|
QJsonObject unit_obj = json["unit"].toObject();
|
|
|
@@ -95,6 +131,33 @@ TEST_F(SerializationTest, UnitComponentSerialization) {
|
|
|
EXPECT_EQ(unit_obj["nation_id"].toString(), QString("roman_republic"));
|
|
|
}
|
|
|
|
|
|
+TEST_F(SerializationTest, UnitComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *unit = original_entity->add_component<UnitComponent>();
|
|
|
+ unit->health = 75;
|
|
|
+ unit->max_health = 150;
|
|
|
+ unit->speed = 7.5F;
|
|
|
+ unit->vision_range = 20.0F;
|
|
|
+ unit->spawn_type = Game::Units::SpawnType::Spearman;
|
|
|
+ unit->owner_id = 2;
|
|
|
+ unit->nation_id = Game::Systems::NationID::Carthage;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<UnitComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_EQ(deserialized->health, 75);
|
|
|
+ EXPECT_EQ(deserialized->max_health, 150);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->speed, 7.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->vision_range, 20.0F);
|
|
|
+ EXPECT_EQ(deserialized->spawn_type, Game::Units::SpawnType::Spearman);
|
|
|
+ EXPECT_EQ(deserialized->owner_id, 2);
|
|
|
+ EXPECT_EQ(deserialized->nation_id, Game::Systems::NationID::Carthage);
|
|
|
+}
|
|
|
+
|
|
|
TEST_F(SerializationTest, MovementComponentSerialization) {
|
|
|
auto *entity = world->create_entity();
|
|
|
auto *movement = entity->add_component<MovementComponent>();
|
|
|
@@ -116,7 +179,7 @@ TEST_F(SerializationTest, MovementComponentSerialization) {
|
|
|
movement->path.emplace_back(10.0F, 20.0F);
|
|
|
movement->path.emplace_back(30.0F, 40.0F);
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("movement"));
|
|
|
QJsonObject movement_obj = json["movement"].toObject();
|
|
|
@@ -164,7 +227,7 @@ TEST_F(SerializationTest, AttackComponentSerialization) {
|
|
|
attack->in_melee_lock = false;
|
|
|
attack->melee_lock_target_id = 0;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("attack"));
|
|
|
QJsonObject attack_obj = json["attack"].toObject();
|
|
|
@@ -196,10 +259,10 @@ TEST_F(SerializationTest, EntityDeserializationRoundTrip) {
|
|
|
unit->max_health = 100;
|
|
|
unit->speed = 6.0F;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized_transform =
|
|
|
new_entity->get_component<TransformComponent>();
|
|
|
@@ -224,7 +287,7 @@ TEST_F(SerializationTest, DeserializationWithMissingFields) {
|
|
|
json["unit"] = unit_obj;
|
|
|
|
|
|
auto *entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(entity, json);
|
|
|
+ Serialization::deserialize_entity(entity, json);
|
|
|
|
|
|
auto *unit = entity->get_component<UnitComponent>();
|
|
|
ASSERT_NE(unit, nullptr);
|
|
|
@@ -242,7 +305,7 @@ TEST_F(SerializationTest, DeserializationWithMalformedJSON) {
|
|
|
|
|
|
auto *entity = world->create_entity();
|
|
|
|
|
|
- EXPECT_NO_THROW({ Serialization::deserializeEntity(entity, json); });
|
|
|
+ EXPECT_NO_THROW({ Serialization::deserialize_entity(entity, json); });
|
|
|
|
|
|
auto *transform = entity->get_component<TransformComponent>();
|
|
|
ASSERT_NE(transform, nullptr);
|
|
|
@@ -258,7 +321,7 @@ TEST_F(SerializationTest, WorldSerializationRoundTrip) {
|
|
|
auto *transform2 = entity2->add_component<TransformComponent>();
|
|
|
transform2->position.x = 20.0F;
|
|
|
|
|
|
- QJsonDocument doc = Serialization::serializeWorld(world.get());
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
|
|
|
ASSERT_TRUE(doc.isObject());
|
|
|
QJsonObject world_obj = doc.object();
|
|
|
@@ -267,7 +330,7 @@ TEST_F(SerializationTest, WorldSerializationRoundTrip) {
|
|
|
EXPECT_TRUE(world_obj.contains("schemaVersion"));
|
|
|
|
|
|
auto new_world = std::make_unique<World>();
|
|
|
- Serialization::deserializeWorld(new_world.get(), doc);
|
|
|
+ Serialization::deserialize_world(new_world.get(), doc);
|
|
|
|
|
|
const auto &entities = new_world->get_entities();
|
|
|
EXPECT_EQ(entities.size(), 2UL);
|
|
|
@@ -280,20 +343,20 @@ TEST_F(SerializationTest, SaveAndLoadFromFile) {
|
|
|
transform->position.y = 43.0F;
|
|
|
transform->position.z = 44.0F;
|
|
|
|
|
|
- QJsonDocument doc = Serialization::serializeWorld(world.get());
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
|
|
|
QTemporaryFile temp_file;
|
|
|
ASSERT_TRUE(temp_file.open());
|
|
|
QString filename = temp_file.fileName();
|
|
|
temp_file.close();
|
|
|
|
|
|
- EXPECT_TRUE(Serialization::saveToFile(filename, doc));
|
|
|
+ EXPECT_TRUE(Serialization::save_to_file(filename, doc));
|
|
|
|
|
|
QJsonDocument loaded_doc = Serialization::load_from_file(filename);
|
|
|
EXPECT_FALSE(loaded_doc.isNull());
|
|
|
|
|
|
auto new_world = std::make_unique<World>();
|
|
|
- Serialization::deserializeWorld(new_world.get(), loaded_doc);
|
|
|
+ Serialization::deserialize_world(new_world.get(), loaded_doc);
|
|
|
|
|
|
const auto &entities = new_world->get_entities();
|
|
|
EXPECT_EQ(entities.size(), 1UL);
|
|
|
@@ -325,7 +388,7 @@ TEST_F(SerializationTest, ProductionComponentSerialization) {
|
|
|
production->production_queue.push_back(Game::Units::TroopType::Spearman);
|
|
|
production->production_queue.push_back(Game::Units::TroopType::Archer);
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("production"));
|
|
|
QJsonObject prod_obj = json["production"].toObject();
|
|
|
@@ -358,7 +421,7 @@ TEST_F(SerializationTest, PatrolComponentSerialization) {
|
|
|
patrol->waypoints.emplace_back(30.0F, 40.0F);
|
|
|
patrol->waypoints.emplace_back(50.0F, 60.0F);
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("patrol"));
|
|
|
QJsonObject patrol_obj = json["patrol"].toObject();
|
|
|
@@ -375,6 +438,129 @@ TEST_F(SerializationTest, PatrolComponentSerialization) {
|
|
|
EXPECT_FLOAT_EQ(wp0["y"].toDouble(), 20.0);
|
|
|
}
|
|
|
|
|
|
+TEST_F(SerializationTest, PatrolComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *patrol = original_entity->add_component<PatrolComponent>();
|
|
|
+ patrol->current_waypoint = 2;
|
|
|
+ patrol->patrolling = true;
|
|
|
+ patrol->waypoints.emplace_back(15.0F, 25.0F);
|
|
|
+ patrol->waypoints.emplace_back(35.0F, 45.0F);
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<PatrolComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_EQ(deserialized->current_waypoint, 2UL);
|
|
|
+ EXPECT_TRUE(deserialized->patrolling);
|
|
|
+ EXPECT_EQ(deserialized->waypoints.size(), 2UL);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->waypoints[0].first, 15.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->waypoints[0].second, 25.0F);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, MovementComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *movement = original_entity->add_component<MovementComponent>();
|
|
|
+ movement->has_target = true;
|
|
|
+ movement->target_x = 100.0F;
|
|
|
+ movement->target_y = 200.0F;
|
|
|
+ movement->goal_x = 150.0F;
|
|
|
+ movement->goal_y = 250.0F;
|
|
|
+ movement->vx = 1.5F;
|
|
|
+ movement->vz = 2.5F;
|
|
|
+ movement->path.emplace_back(10.0F, 20.0F);
|
|
|
+ movement->path.emplace_back(30.0F, 40.0F);
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<MovementComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_TRUE(deserialized->has_target);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->target_x, 100.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->target_y, 200.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->goal_x, 150.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->goal_y, 250.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->vx, 1.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->vz, 2.5F);
|
|
|
+ EXPECT_EQ(deserialized->path.size(), 2UL);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, AttackComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *attack = original_entity->add_component<AttackComponent>();
|
|
|
+ attack->range = 15.0F;
|
|
|
+ attack->damage = 30;
|
|
|
+ attack->cooldown = 2.5F;
|
|
|
+ attack->melee_range = 3.0F;
|
|
|
+ attack->melee_damage = 20;
|
|
|
+ attack->preferred_mode = AttackComponent::CombatMode::Ranged;
|
|
|
+ attack->current_mode = AttackComponent::CombatMode::Melee;
|
|
|
+ attack->can_melee = true;
|
|
|
+ attack->can_ranged = true;
|
|
|
+ attack->in_melee_lock = true;
|
|
|
+ attack->melee_lock_target_id = 42;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<AttackComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->range, 15.0F);
|
|
|
+ EXPECT_EQ(deserialized->damage, 30);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->cooldown, 2.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->melee_range, 3.0F);
|
|
|
+ EXPECT_EQ(deserialized->melee_damage, 20);
|
|
|
+ EXPECT_EQ(deserialized->preferred_mode, AttackComponent::CombatMode::Ranged);
|
|
|
+ EXPECT_EQ(deserialized->current_mode, AttackComponent::CombatMode::Melee);
|
|
|
+ EXPECT_TRUE(deserialized->can_melee);
|
|
|
+ EXPECT_TRUE(deserialized->can_ranged);
|
|
|
+ EXPECT_TRUE(deserialized->in_melee_lock);
|
|
|
+ EXPECT_EQ(deserialized->melee_lock_target_id, 42U);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, ProductionComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *production = original_entity->add_component<ProductionComponent>();
|
|
|
+ production->in_progress = true;
|
|
|
+ production->build_time = 15.0F;
|
|
|
+ production->time_remaining = 7.5F;
|
|
|
+ production->produced_count = 5;
|
|
|
+ production->max_units = 20;
|
|
|
+ production->product_type = Game::Units::TroopType::Spearman;
|
|
|
+ production->rally_x = 150.0F;
|
|
|
+ production->rally_z = 250.0F;
|
|
|
+ production->rally_set = true;
|
|
|
+ production->villager_cost = 3;
|
|
|
+ production->production_queue.push_back(Game::Units::TroopType::Archer);
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<ProductionComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_TRUE(deserialized->in_progress);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->build_time, 15.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->time_remaining, 7.5F);
|
|
|
+ EXPECT_EQ(deserialized->produced_count, 5);
|
|
|
+ EXPECT_EQ(deserialized->max_units, 20);
|
|
|
+ EXPECT_EQ(deserialized->product_type, Game::Units::TroopType::Spearman);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->rally_x, 150.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->rally_z, 250.0F);
|
|
|
+ EXPECT_TRUE(deserialized->rally_set);
|
|
|
+ EXPECT_EQ(deserialized->villager_cost, 3);
|
|
|
+ EXPECT_EQ(deserialized->production_queue.size(), 1UL);
|
|
|
+ EXPECT_EQ(deserialized->production_queue[0], Game::Units::TroopType::Archer);
|
|
|
+}
|
|
|
+
|
|
|
TEST_F(SerializationTest, RenderableComponentSerialization) {
|
|
|
auto *entity = world->create_entity();
|
|
|
auto *renderable =
|
|
|
@@ -387,7 +573,7 @@ TEST_F(SerializationTest, RenderableComponentSerialization) {
|
|
|
renderable->mesh = RenderableComponent::MeshKind::Capsule;
|
|
|
renderable->color = {0.8F, 0.2F, 0.5F};
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("renderable"));
|
|
|
QJsonObject renderable_obj = json["renderable"].toObject();
|
|
|
@@ -420,10 +606,10 @@ TEST_F(SerializationTest, RenderableComponentRoundTrip) {
|
|
|
renderable->mesh = RenderableComponent::MeshKind::Quad;
|
|
|
renderable->color = {1.0F, 0.5F, 0.25F};
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized = new_entity->get_component<RenderableComponent>();
|
|
|
ASSERT_NE(deserialized, nullptr);
|
|
|
@@ -443,7 +629,7 @@ TEST_F(SerializationTest, AttackTargetComponentSerialization) {
|
|
|
attack_target->target_id = 42;
|
|
|
attack_target->should_chase = true;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("attack_target"));
|
|
|
QJsonObject attack_target_obj = json["attack_target"].toObject();
|
|
|
@@ -458,10 +644,10 @@ TEST_F(SerializationTest, AttackTargetComponentRoundTrip) {
|
|
|
attack_target->target_id = 123;
|
|
|
attack_target->should_chase = false;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized = new_entity->get_component<AttackTargetComponent>();
|
|
|
ASSERT_NE(deserialized, nullptr);
|
|
|
@@ -473,7 +659,7 @@ TEST_F(SerializationTest, BuildingComponentSerialization) {
|
|
|
auto *entity = world->create_entity();
|
|
|
entity->add_component<BuildingComponent>();
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("building"));
|
|
|
EXPECT_TRUE(json["building"].toBool());
|
|
|
@@ -483,10 +669,10 @@ TEST_F(SerializationTest, BuildingComponentRoundTrip) {
|
|
|
auto *original_entity = world->create_entity();
|
|
|
original_entity->add_component<BuildingComponent>();
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized = new_entity->get_component<BuildingComponent>();
|
|
|
ASSERT_NE(deserialized, nullptr);
|
|
|
@@ -496,7 +682,7 @@ TEST_F(SerializationTest, AIControlledComponentSerialization) {
|
|
|
auto *entity = world->create_entity();
|
|
|
entity->add_component<AIControlledComponent>();
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("aiControlled"));
|
|
|
EXPECT_TRUE(json["aiControlled"].toBool());
|
|
|
@@ -506,10 +692,10 @@ TEST_F(SerializationTest, AIControlledComponentRoundTrip) {
|
|
|
auto *original_entity = world->create_entity();
|
|
|
original_entity->add_component<AIControlledComponent>();
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized = new_entity->get_component<AIControlledComponent>();
|
|
|
ASSERT_NE(deserialized, nullptr);
|
|
|
@@ -524,7 +710,7 @@ TEST_F(SerializationTest, CaptureComponentSerialization) {
|
|
|
capture->required_time = 15.0F;
|
|
|
capture->is_being_captured = true;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
ASSERT_TRUE(json.contains("capture"));
|
|
|
QJsonObject capture_obj = json["capture"].toObject();
|
|
|
@@ -543,10 +729,10 @@ TEST_F(SerializationTest, CaptureComponentRoundTrip) {
|
|
|
capture->required_time = 20.0F;
|
|
|
capture->is_being_captured = false;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(original_entity);
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
auto *deserialized = new_entity->get_component<CaptureComponent>();
|
|
|
ASSERT_NE(deserialized, nullptr);
|
|
|
@@ -592,7 +778,16 @@ TEST_F(SerializationTest, CompleteEntityWithAllComponents) {
|
|
|
auto *capture = entity->add_component<CaptureComponent>();
|
|
|
capture->is_being_captured = true;
|
|
|
|
|
|
- QJsonObject json = Serialization::serializeEntity(entity);
|
|
|
+ auto *hold_mode = entity->add_component<HoldModeComponent>();
|
|
|
+ hold_mode->active = true;
|
|
|
+
|
|
|
+ auto *healer = entity->add_component<HealerComponent>();
|
|
|
+ healer->healing_amount = 10;
|
|
|
+
|
|
|
+ auto *catapult = entity->add_component<CatapultLoadingComponent>();
|
|
|
+ catapult->state = CatapultLoadingComponent::LoadingState::Idle;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
|
|
|
EXPECT_TRUE(json.contains("transform"));
|
|
|
EXPECT_TRUE(json.contains("renderable"));
|
|
|
@@ -604,9 +799,12 @@ TEST_F(SerializationTest, CompleteEntityWithAllComponents) {
|
|
|
EXPECT_TRUE(json.contains("production"));
|
|
|
EXPECT_TRUE(json.contains("aiControlled"));
|
|
|
EXPECT_TRUE(json.contains("capture"));
|
|
|
+ EXPECT_TRUE(json.contains("hold_mode"));
|
|
|
+ EXPECT_TRUE(json.contains("healer"));
|
|
|
+ EXPECT_TRUE(json.contains("catapult_loading"));
|
|
|
|
|
|
auto *new_entity = world->create_entity();
|
|
|
- Serialization::deserializeEntity(new_entity, json);
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
|
|
|
EXPECT_NE(new_entity->get_component<TransformComponent>(), nullptr);
|
|
|
EXPECT_NE(new_entity->get_component<RenderableComponent>(), nullptr);
|
|
|
@@ -618,10 +816,13 @@ TEST_F(SerializationTest, CompleteEntityWithAllComponents) {
|
|
|
EXPECT_NE(new_entity->get_component<ProductionComponent>(), nullptr);
|
|
|
EXPECT_NE(new_entity->get_component<AIControlledComponent>(), nullptr);
|
|
|
EXPECT_NE(new_entity->get_component<CaptureComponent>(), nullptr);
|
|
|
+ EXPECT_NE(new_entity->get_component<HoldModeComponent>(), nullptr);
|
|
|
+ EXPECT_NE(new_entity->get_component<HealerComponent>(), nullptr);
|
|
|
+ EXPECT_NE(new_entity->get_component<CatapultLoadingComponent>(), nullptr);
|
|
|
}
|
|
|
|
|
|
TEST_F(SerializationTest, EmptyWorldSerialization) {
|
|
|
- QJsonDocument doc = Serialization::serializeWorld(world.get());
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
|
|
|
ASSERT_TRUE(doc.isObject());
|
|
|
QJsonObject world_obj = doc.object();
|
|
|
@@ -630,3 +831,553 @@ TEST_F(SerializationTest, EmptyWorldSerialization) {
|
|
|
QJsonArray entities = world_obj["entities"].toArray();
|
|
|
EXPECT_EQ(entities.size(), 0);
|
|
|
}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, HoldModeComponentSerialization) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto *hold_mode = entity->add_component<HoldModeComponent>();
|
|
|
+
|
|
|
+ hold_mode->active = false;
|
|
|
+ hold_mode->exit_cooldown = 1.5F;
|
|
|
+ hold_mode->stand_up_duration = 3.0F;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
+
|
|
|
+ ASSERT_TRUE(json.contains("hold_mode"));
|
|
|
+ QJsonObject hold_mode_obj = json["hold_mode"].toObject();
|
|
|
+
|
|
|
+ EXPECT_FALSE(hold_mode_obj["active"].toBool());
|
|
|
+ EXPECT_FLOAT_EQ(hold_mode_obj["exit_cooldown"].toDouble(), 1.5);
|
|
|
+ EXPECT_FLOAT_EQ(hold_mode_obj["stand_up_duration"].toDouble(), 3.0);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, HoldModeComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *hold_mode = original_entity->add_component<HoldModeComponent>();
|
|
|
+ hold_mode->active = true;
|
|
|
+ hold_mode->exit_cooldown = 2.5F;
|
|
|
+ hold_mode->stand_up_duration = 4.0F;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<HoldModeComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_TRUE(deserialized->active);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->exit_cooldown, 2.5F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->stand_up_duration, 4.0F);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, HealerComponentSerialization) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto *healer = entity->add_component<HealerComponent>();
|
|
|
+
|
|
|
+ healer->healing_range = 12.0F;
|
|
|
+ healer->healing_amount = 10;
|
|
|
+ healer->healing_cooldown = 3.0F;
|
|
|
+ healer->time_since_last_heal = 1.0F;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
+
|
|
|
+ ASSERT_TRUE(json.contains("healer"));
|
|
|
+ QJsonObject healer_obj = json["healer"].toObject();
|
|
|
+
|
|
|
+ EXPECT_FLOAT_EQ(healer_obj["healing_range"].toDouble(), 12.0);
|
|
|
+ EXPECT_EQ(healer_obj["healing_amount"].toInt(), 10);
|
|
|
+ EXPECT_FLOAT_EQ(healer_obj["healing_cooldown"].toDouble(), 3.0);
|
|
|
+ EXPECT_FLOAT_EQ(healer_obj["time_since_last_heal"].toDouble(), 1.0);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, HealerComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *healer = original_entity->add_component<HealerComponent>();
|
|
|
+ healer->healing_range = 15.0F;
|
|
|
+ healer->healing_amount = 8;
|
|
|
+ healer->healing_cooldown = 4.0F;
|
|
|
+ healer->time_since_last_heal = 2.0F;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<HealerComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->healing_range, 15.0F);
|
|
|
+ EXPECT_EQ(deserialized->healing_amount, 8);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->healing_cooldown, 4.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->time_since_last_heal, 2.0F);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, CatapultLoadingComponentSerialization) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto *catapult = entity->add_component<CatapultLoadingComponent>();
|
|
|
+
|
|
|
+ catapult->state = CatapultLoadingComponent::LoadingState::Loading;
|
|
|
+ catapult->loading_time = 1.0F;
|
|
|
+ catapult->loading_duration = 3.0F;
|
|
|
+ catapult->firing_time = 0.0F;
|
|
|
+ catapult->firing_duration = 1.0F;
|
|
|
+ catapult->target_id = 42;
|
|
|
+ catapult->target_locked_x = 100.0F;
|
|
|
+ catapult->target_locked_y = 50.0F;
|
|
|
+ catapult->target_locked_z = 200.0F;
|
|
|
+ catapult->target_position_locked = true;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(entity);
|
|
|
+
|
|
|
+ ASSERT_TRUE(json.contains("catapult_loading"));
|
|
|
+ QJsonObject catapult_obj = json["catapult_loading"].toObject();
|
|
|
+
|
|
|
+ EXPECT_EQ(catapult_obj["state"].toInt(),
|
|
|
+ static_cast<int>(CatapultLoadingComponent::LoadingState::Loading));
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["loading_time"].toDouble(), 1.0);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["loading_duration"].toDouble(), 3.0);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["firing_time"].toDouble(), 0.0);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["firing_duration"].toDouble(), 1.0);
|
|
|
+ EXPECT_EQ(catapult_obj["target_id"].toVariant().toULongLong(), 42ULL);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["target_locked_x"].toDouble(), 100.0);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["target_locked_y"].toDouble(), 50.0);
|
|
|
+ EXPECT_FLOAT_EQ(catapult_obj["target_locked_z"].toDouble(), 200.0);
|
|
|
+ EXPECT_TRUE(catapult_obj["target_position_locked"].toBool());
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, CatapultLoadingComponentRoundTrip) {
|
|
|
+ auto *original_entity = world->create_entity();
|
|
|
+ auto *catapult = original_entity->add_component<CatapultLoadingComponent>();
|
|
|
+ catapult->state = CatapultLoadingComponent::LoadingState::ReadyToFire;
|
|
|
+ catapult->loading_time = 2.0F;
|
|
|
+ catapult->loading_duration = 4.0F;
|
|
|
+ catapult->firing_time = 0.25F;
|
|
|
+ catapult->firing_duration = 0.75F;
|
|
|
+ catapult->target_id = 99;
|
|
|
+ catapult->target_locked_x = 150.0F;
|
|
|
+ catapult->target_locked_y = 75.0F;
|
|
|
+ catapult->target_locked_z = 250.0F;
|
|
|
+ catapult->target_position_locked = false;
|
|
|
+
|
|
|
+ QJsonObject json = Serialization::serialize_entity(original_entity);
|
|
|
+
|
|
|
+ auto *new_entity = world->create_entity();
|
|
|
+ Serialization::deserialize_entity(new_entity, json);
|
|
|
+
|
|
|
+ auto *deserialized = new_entity->get_component<CatapultLoadingComponent>();
|
|
|
+ ASSERT_NE(deserialized, nullptr);
|
|
|
+ EXPECT_EQ(deserialized->state,
|
|
|
+ CatapultLoadingComponent::LoadingState::ReadyToFire);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->loading_time, 2.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->loading_duration, 4.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->firing_time, 0.25F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->firing_duration, 0.75F);
|
|
|
+ EXPECT_EQ(deserialized->target_id, 99U);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->target_locked_x, 150.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->target_locked_y, 75.0F);
|
|
|
+ EXPECT_FLOAT_EQ(deserialized->target_locked_z, 250.0F);
|
|
|
+ EXPECT_FALSE(deserialized->target_position_locked);
|
|
|
+}
|
|
|
+
|
|
|
+// ============================================================================
|
|
|
+// Integration Tests: Multi-Unit Battlefield State Preservation
|
|
|
+// ============================================================================
|
|
|
+
|
|
|
+TEST_F(SerializationTest, MultipleUnitsPositionsAndHealthPreserved) {
|
|
|
+ // Create a battlefield with multiple units at different positions
|
|
|
+ struct UnitData {
|
|
|
+ float x, y, z;
|
|
|
+ int health;
|
|
|
+ int max_health;
|
|
|
+ int owner_id;
|
|
|
+ Game::Units::SpawnType spawn_type;
|
|
|
+ };
|
|
|
+
|
|
|
+ std::vector<UnitData> original_units = {
|
|
|
+ {10.0F, 0.0F, 20.0F, 80, 100, 1, Game::Units::SpawnType::Archer},
|
|
|
+ {15.5F, 1.0F, 25.5F, 45, 100, 1, Game::Units::SpawnType::Spearman},
|
|
|
+ {30.0F, 0.0F, 40.0F, 100, 100, 2, Game::Units::SpawnType::Knight},
|
|
|
+ {35.5F, 2.0F, 45.5F, 60, 150, 2, Game::Units::SpawnType::HorseArcher},
|
|
|
+ {50.0F, 0.5F, 60.0F, 25, 80, 1, Game::Units::SpawnType::Catapult},
|
|
|
+ };
|
|
|
+
|
|
|
+ std::vector<EntityID> entity_ids;
|
|
|
+ for (const auto &unit_data : original_units) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ entity_ids.push_back(entity->get_id());
|
|
|
+
|
|
|
+ auto *transform = entity->add_component<TransformComponent>();
|
|
|
+ transform->position.x = unit_data.x;
|
|
|
+ transform->position.y = unit_data.y;
|
|
|
+ transform->position.z = unit_data.z;
|
|
|
+
|
|
|
+ auto *unit = entity->add_component<UnitComponent>();
|
|
|
+ unit->health = unit_data.health;
|
|
|
+ unit->max_health = unit_data.max_health;
|
|
|
+ unit->owner_id = unit_data.owner_id;
|
|
|
+ unit->spawn_type = unit_data.spawn_type;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Serialize and deserialize the world
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify all units are restored with exact positions and health
|
|
|
+ const auto &entities = restored_world->get_entities();
|
|
|
+ EXPECT_EQ(entities.size(), original_units.size());
|
|
|
+
|
|
|
+ for (size_t i = 0; i < entity_ids.size(); ++i) {
|
|
|
+ auto *entity = restored_world->get_entity(entity_ids[i]);
|
|
|
+ ASSERT_NE(entity, nullptr) << "Entity " << i << " not found";
|
|
|
+
|
|
|
+ auto *transform = entity->get_component<TransformComponent>();
|
|
|
+ ASSERT_NE(transform, nullptr);
|
|
|
+ EXPECT_FLOAT_EQ(transform->position.x, original_units[i].x)
|
|
|
+ << "Unit " << i << " X position mismatch";
|
|
|
+ EXPECT_FLOAT_EQ(transform->position.y, original_units[i].y)
|
|
|
+ << "Unit " << i << " Y position mismatch";
|
|
|
+ EXPECT_FLOAT_EQ(transform->position.z, original_units[i].z)
|
|
|
+ << "Unit " << i << " Z position mismatch";
|
|
|
+
|
|
|
+ auto *unit = entity->get_component<UnitComponent>();
|
|
|
+ ASSERT_NE(unit, nullptr);
|
|
|
+ EXPECT_EQ(unit->health, original_units[i].health)
|
|
|
+ << "Unit " << i << " health mismatch";
|
|
|
+ EXPECT_EQ(unit->max_health, original_units[i].max_health)
|
|
|
+ << "Unit " << i << " max_health mismatch";
|
|
|
+ EXPECT_EQ(unit->owner_id, original_units[i].owner_id)
|
|
|
+ << "Unit " << i << " owner_id mismatch";
|
|
|
+ EXPECT_EQ(unit->spawn_type, original_units[i].spawn_type)
|
|
|
+ << "Unit " << i << " spawn_type mismatch";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, OwnerRegistryTeamsAndColorsPreserved) {
|
|
|
+ // Setup owner registry with teams and custom colors
|
|
|
+ auto ®istry = Game::Systems::OwnerRegistry::instance();
|
|
|
+ registry.clear();
|
|
|
+
|
|
|
+ // Register players with specific teams and colors
|
|
|
+ int player1 = registry.register_owner(Game::Systems::OwnerType::Player, "Blue Kingdom");
|
|
|
+ int player2 = registry.register_owner(Game::Systems::OwnerType::AI, "Red Empire");
|
|
|
+ int player3 = registry.register_owner(Game::Systems::OwnerType::Player, "Green Alliance");
|
|
|
+
|
|
|
+ // Set teams (player1 and player3 are allies)
|
|
|
+ registry.set_owner_team(player1, 1);
|
|
|
+ registry.set_owner_team(player2, 2);
|
|
|
+ registry.set_owner_team(player3, 1);
|
|
|
+
|
|
|
+ // Set custom colors
|
|
|
+ registry.set_owner_color(player1, 0.1F, 0.2F, 0.9F);
|
|
|
+ registry.set_owner_color(player2, 0.9F, 0.1F, 0.1F);
|
|
|
+ registry.set_owner_color(player3, 0.1F, 0.9F, 0.2F);
|
|
|
+
|
|
|
+ registry.set_local_player_id(player1);
|
|
|
+
|
|
|
+ // Create some entities owned by these players
|
|
|
+ for (int i = 0; i < 3; ++i) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto *unit = entity->add_component<UnitComponent>();
|
|
|
+ unit->owner_id = player1;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < 2; ++i) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto *unit = entity->add_component<UnitComponent>();
|
|
|
+ unit->owner_id = player2;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Serialize world (includes owner_registry)
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+
|
|
|
+ // Clear registry and restore
|
|
|
+ registry.clear();
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify owner registry state is preserved
|
|
|
+ EXPECT_EQ(registry.get_local_player_id(), player1);
|
|
|
+
|
|
|
+ // Verify teams are preserved
|
|
|
+ EXPECT_EQ(registry.get_owner_team(player1), 1);
|
|
|
+ EXPECT_EQ(registry.get_owner_team(player2), 2);
|
|
|
+ EXPECT_EQ(registry.get_owner_team(player3), 1);
|
|
|
+
|
|
|
+ // Verify alliances are preserved
|
|
|
+ EXPECT_TRUE(registry.are_allies(player1, player3));
|
|
|
+ EXPECT_TRUE(registry.are_enemies(player1, player2));
|
|
|
+ EXPECT_TRUE(registry.are_enemies(player2, player3));
|
|
|
+
|
|
|
+ // Verify colors are preserved
|
|
|
+ auto color1 = registry.get_owner_color(player1);
|
|
|
+ EXPECT_FLOAT_EQ(color1[0], 0.1F);
|
|
|
+ EXPECT_FLOAT_EQ(color1[1], 0.2F);
|
|
|
+ EXPECT_FLOAT_EQ(color1[2], 0.9F);
|
|
|
+
|
|
|
+ auto color2 = registry.get_owner_color(player2);
|
|
|
+ EXPECT_FLOAT_EQ(color2[0], 0.9F);
|
|
|
+ EXPECT_FLOAT_EQ(color2[1], 0.1F);
|
|
|
+ EXPECT_FLOAT_EQ(color2[2], 0.1F);
|
|
|
+
|
|
|
+ auto color3 = registry.get_owner_color(player3);
|
|
|
+ EXPECT_FLOAT_EQ(color3[0], 0.1F);
|
|
|
+ EXPECT_FLOAT_EQ(color3[1], 0.9F);
|
|
|
+ EXPECT_FLOAT_EQ(color3[2], 0.2F);
|
|
|
+
|
|
|
+ // Verify owner names are preserved
|
|
|
+ EXPECT_EQ(registry.get_owner_name(player1), "Blue Kingdom");
|
|
|
+ EXPECT_EQ(registry.get_owner_name(player2), "Red Empire");
|
|
|
+ EXPECT_EQ(registry.get_owner_name(player3), "Green Alliance");
|
|
|
+
|
|
|
+ // Verify owner types are preserved
|
|
|
+ EXPECT_TRUE(registry.is_player(player1));
|
|
|
+ EXPECT_TRUE(registry.is_ai(player2));
|
|
|
+ EXPECT_TRUE(registry.is_player(player3));
|
|
|
+
|
|
|
+ // Clean up
|
|
|
+ registry.clear();
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, BuildingOwnershipAndCaptureStatePreserved) {
|
|
|
+ // Create buildings (barracks/villages) with different ownership and capture states
|
|
|
+ struct BuildingData {
|
|
|
+ float x, z;
|
|
|
+ int owner_id;
|
|
|
+ int capturing_player_id;
|
|
|
+ float capture_progress;
|
|
|
+ bool is_being_captured;
|
|
|
+ };
|
|
|
+
|
|
|
+ std::vector<BuildingData> buildings = {
|
|
|
+ {100.0F, 100.0F, 1, -1, 0.0F, false}, // Owned by player 1, not being captured
|
|
|
+ {200.0F, 200.0F, 2, 1, 7.5F, true}, // Owned by player 2, being captured by player 1
|
|
|
+ {300.0F, 300.0F, 1, 2, 15.0F, true}, // Owned by player 1, being captured by player 2
|
|
|
+ {400.0F, 400.0F, -1, -1, 0.0F, false}, // Neutral building
|
|
|
+ };
|
|
|
+
|
|
|
+ std::vector<EntityID> building_ids;
|
|
|
+ for (const auto &bldg : buildings) {
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ building_ids.push_back(entity->get_id());
|
|
|
+
|
|
|
+ auto *transform = entity->add_component<TransformComponent>();
|
|
|
+ transform->position.x = bldg.x;
|
|
|
+ transform->position.z = bldg.z;
|
|
|
+
|
|
|
+ entity->add_component<BuildingComponent>();
|
|
|
+
|
|
|
+ auto *unit = entity->add_component<UnitComponent>();
|
|
|
+ unit->owner_id = bldg.owner_id;
|
|
|
+
|
|
|
+ auto *capture = entity->add_component<CaptureComponent>();
|
|
|
+ capture->capturing_player_id = bldg.capturing_player_id;
|
|
|
+ capture->capture_progress = bldg.capture_progress;
|
|
|
+ capture->is_being_captured = bldg.is_being_captured;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Serialize and restore
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify all buildings are restored with correct ownership and capture state
|
|
|
+ for (size_t i = 0; i < building_ids.size(); ++i) {
|
|
|
+ auto *entity = restored_world->get_entity(building_ids[i]);
|
|
|
+ ASSERT_NE(entity, nullptr) << "Building " << i << " not found";
|
|
|
+
|
|
|
+ auto *transform = entity->get_component<TransformComponent>();
|
|
|
+ ASSERT_NE(transform, nullptr);
|
|
|
+ EXPECT_FLOAT_EQ(transform->position.x, buildings[i].x)
|
|
|
+ << "Building " << i << " X position mismatch";
|
|
|
+ EXPECT_FLOAT_EQ(transform->position.z, buildings[i].z)
|
|
|
+ << "Building " << i << " Z position mismatch";
|
|
|
+
|
|
|
+ EXPECT_NE(entity->get_component<BuildingComponent>(), nullptr);
|
|
|
+
|
|
|
+ auto *unit = entity->get_component<UnitComponent>();
|
|
|
+ ASSERT_NE(unit, nullptr);
|
|
|
+ EXPECT_EQ(unit->owner_id, buildings[i].owner_id)
|
|
|
+ << "Building " << i << " owner mismatch";
|
|
|
+
|
|
|
+ auto *capture = entity->get_component<CaptureComponent>();
|
|
|
+ ASSERT_NE(capture, nullptr);
|
|
|
+ EXPECT_EQ(capture->capturing_player_id, buildings[i].capturing_player_id)
|
|
|
+ << "Building " << i << " capturing_player_id mismatch";
|
|
|
+ EXPECT_FLOAT_EQ(capture->capture_progress, buildings[i].capture_progress)
|
|
|
+ << "Building " << i << " capture_progress mismatch";
|
|
|
+ EXPECT_EQ(capture->is_being_captured, buildings[i].is_being_captured)
|
|
|
+ << "Building " << i << " is_being_captured mismatch";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, UnitMovementStatePreserved) {
|
|
|
+ // Create units with active movement paths
|
|
|
+ auto *entity = world->create_entity();
|
|
|
+ auto entity_id = entity->get_id();
|
|
|
+
|
|
|
+ auto *transform = entity->add_component<TransformComponent>();
|
|
|
+ transform->position.x = 10.0F;
|
|
|
+ transform->position.y = 0.0F;
|
|
|
+ transform->position.z = 20.0F;
|
|
|
+
|
|
|
+ auto *unit = entity->add_component<UnitComponent>();
|
|
|
+ unit->owner_id = 1;
|
|
|
+ unit->health = 85;
|
|
|
+
|
|
|
+ auto *movement = entity->add_component<MovementComponent>();
|
|
|
+ movement->has_target = true;
|
|
|
+ movement->target_x = 50.0F;
|
|
|
+ movement->target_y = 60.0F;
|
|
|
+ movement->goal_x = 55.0F;
|
|
|
+ movement->goal_y = 65.0F;
|
|
|
+ movement->vx = 2.5F;
|
|
|
+ movement->vz = 3.0F;
|
|
|
+ // Add path waypoints
|
|
|
+ movement->path.emplace_back(20.0F, 30.0F);
|
|
|
+ movement->path.emplace_back(35.0F, 45.0F);
|
|
|
+ movement->path.emplace_back(50.0F, 60.0F);
|
|
|
+ const size_t expected_path_size = movement->path.size();
|
|
|
+
|
|
|
+ // Serialize and restore
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify movement state is preserved
|
|
|
+ auto *restored_entity = restored_world->get_entity(entity_id);
|
|
|
+ ASSERT_NE(restored_entity, nullptr);
|
|
|
+
|
|
|
+ auto *restored_movement = restored_entity->get_component<MovementComponent>();
|
|
|
+ ASSERT_NE(restored_movement, nullptr);
|
|
|
+
|
|
|
+ EXPECT_TRUE(restored_movement->has_target);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->target_x, 50.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->target_y, 60.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->goal_x, 55.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->goal_y, 65.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->vx, 2.5F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->vz, 3.0F);
|
|
|
+
|
|
|
+ // Verify path is preserved
|
|
|
+ ASSERT_EQ(restored_movement->path.size(), expected_path_size);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[0].first, 20.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[0].second, 30.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[1].first, 35.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[1].second, 45.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[2].first, 50.0F);
|
|
|
+ EXPECT_FLOAT_EQ(restored_movement->path[2].second, 60.0F);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, CombatStatePreserved) {
|
|
|
+ // Create units engaged in combat
|
|
|
+ auto *attacker = world->create_entity();
|
|
|
+ auto *defender = world->create_entity();
|
|
|
+ auto attacker_id = attacker->get_id();
|
|
|
+ auto defender_id = defender->get_id();
|
|
|
+
|
|
|
+ // Setup attacker
|
|
|
+ auto *attacker_transform = attacker->add_component<TransformComponent>();
|
|
|
+ attacker_transform->position.x = 10.0F;
|
|
|
+ attacker_transform->position.z = 10.0F;
|
|
|
+
|
|
|
+ auto *attacker_unit = attacker->add_component<UnitComponent>();
|
|
|
+ attacker_unit->owner_id = 1;
|
|
|
+ attacker_unit->health = 90;
|
|
|
+
|
|
|
+ auto *attacker_attack = attacker->add_component<AttackComponent>();
|
|
|
+ attacker_attack->damage = 25;
|
|
|
+ attacker_attack->range = 15.0F;
|
|
|
+ attacker_attack->current_mode = AttackComponent::CombatMode::Melee;
|
|
|
+ attacker_attack->in_melee_lock = true;
|
|
|
+ attacker_attack->melee_lock_target_id = defender_id;
|
|
|
+
|
|
|
+ auto *attacker_target = attacker->add_component<AttackTargetComponent>();
|
|
|
+ attacker_target->target_id = defender_id;
|
|
|
+ attacker_target->should_chase = true;
|
|
|
+
|
|
|
+ // Setup defender
|
|
|
+ auto *defender_transform = defender->add_component<TransformComponent>();
|
|
|
+ defender_transform->position.x = 12.0F;
|
|
|
+ defender_transform->position.z = 12.0F;
|
|
|
+
|
|
|
+ auto *defender_unit = defender->add_component<UnitComponent>();
|
|
|
+ defender_unit->owner_id = 2;
|
|
|
+ defender_unit->health = 60;
|
|
|
+
|
|
|
+ auto *defender_attack = defender->add_component<AttackComponent>();
|
|
|
+ defender_attack->damage = 20;
|
|
|
+ defender_attack->in_melee_lock = true;
|
|
|
+ defender_attack->melee_lock_target_id = attacker_id;
|
|
|
+
|
|
|
+ // Serialize and restore
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify combat state is preserved
|
|
|
+ auto *restored_attacker = restored_world->get_entity(attacker_id);
|
|
|
+ auto *restored_defender = restored_world->get_entity(defender_id);
|
|
|
+ ASSERT_NE(restored_attacker, nullptr);
|
|
|
+ ASSERT_NE(restored_defender, nullptr);
|
|
|
+
|
|
|
+ auto *restored_attacker_attack =
|
|
|
+ restored_attacker->get_component<AttackComponent>();
|
|
|
+ ASSERT_NE(restored_attacker_attack, nullptr);
|
|
|
+ EXPECT_TRUE(restored_attacker_attack->in_melee_lock);
|
|
|
+ EXPECT_EQ(restored_attacker_attack->melee_lock_target_id, defender_id);
|
|
|
+ EXPECT_EQ(restored_attacker_attack->current_mode,
|
|
|
+ AttackComponent::CombatMode::Melee);
|
|
|
+
|
|
|
+ auto *restored_attacker_target =
|
|
|
+ restored_attacker->get_component<AttackTargetComponent>();
|
|
|
+ ASSERT_NE(restored_attacker_target, nullptr);
|
|
|
+ EXPECT_EQ(restored_attacker_target->target_id, defender_id);
|
|
|
+ EXPECT_TRUE(restored_attacker_target->should_chase);
|
|
|
+
|
|
|
+ auto *restored_defender_attack =
|
|
|
+ restored_defender->get_component<AttackComponent>();
|
|
|
+ ASSERT_NE(restored_defender_attack, nullptr);
|
|
|
+ EXPECT_TRUE(restored_defender_attack->in_melee_lock);
|
|
|
+ EXPECT_EQ(restored_defender_attack->melee_lock_target_id, attacker_id);
|
|
|
+
|
|
|
+ // Verify health is preserved
|
|
|
+ auto *restored_attacker_unit =
|
|
|
+ restored_attacker->get_component<UnitComponent>();
|
|
|
+ auto *restored_defender_unit =
|
|
|
+ restored_defender->get_component<UnitComponent>();
|
|
|
+ EXPECT_EQ(restored_attacker_unit->health, 90);
|
|
|
+ EXPECT_EQ(restored_defender_unit->health, 60);
|
|
|
+}
|
|
|
+
|
|
|
+TEST_F(SerializationTest, NationIdentityPreserved) {
|
|
|
+ // Create units from different nations
|
|
|
+ auto *roman_unit = world->create_entity();
|
|
|
+ auto *carthage_unit = world->create_entity();
|
|
|
+ auto roman_id = roman_unit->get_id();
|
|
|
+ auto carthage_id = carthage_unit->get_id();
|
|
|
+
|
|
|
+ auto *roman_unit_comp = roman_unit->add_component<UnitComponent>();
|
|
|
+ roman_unit_comp->nation_id = Game::Systems::NationID::RomanRepublic;
|
|
|
+ roman_unit_comp->spawn_type = Game::Units::SpawnType::Spearman;
|
|
|
+
|
|
|
+ auto *carthage_unit_comp = carthage_unit->add_component<UnitComponent>();
|
|
|
+ carthage_unit_comp->nation_id = Game::Systems::NationID::Carthage;
|
|
|
+ carthage_unit_comp->spawn_type = Game::Units::SpawnType::Archer;
|
|
|
+
|
|
|
+ // Serialize and restore
|
|
|
+ QJsonDocument doc = Serialization::serialize_world(world.get());
|
|
|
+ auto restored_world = std::make_unique<World>();
|
|
|
+ Serialization::deserialize_world(restored_world.get(), doc);
|
|
|
+
|
|
|
+ // Verify nation IDs are preserved
|
|
|
+ auto *restored_roman = restored_world->get_entity(roman_id);
|
|
|
+ auto *restored_carthage = restored_world->get_entity(carthage_id);
|
|
|
+
|
|
|
+ auto *restored_roman_comp = restored_roman->get_component<UnitComponent>();
|
|
|
+ EXPECT_EQ(restored_roman_comp->nation_id,
|
|
|
+ Game::Systems::NationID::RomanRepublic);
|
|
|
+ EXPECT_EQ(restored_roman_comp->spawn_type, Game::Units::SpawnType::Spearman);
|
|
|
+
|
|
|
+ auto *restored_carthage_comp =
|
|
|
+ restored_carthage->get_component<UnitComponent>();
|
|
|
+ EXPECT_EQ(restored_carthage_comp->nation_id, Game::Systems::NationID::Carthage);
|
|
|
+ EXPECT_EQ(restored_carthage_comp->spawn_type, Game::Units::SpawnType::Archer);
|
|
|
+}
|