Browse Source

Convert nation and building string identifiers to enums

Co-authored-by: djeada <[email protected]>
copilot-swe-agent[bot] 1 month ago
parent
commit
dba018645f

+ 5 - 1
game/core/component.h

@@ -10,6 +10,10 @@
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
+namespace Game::Systems {
+enum class NationID : std::uint8_t;
+}
+
 namespace Engine::Core {
 namespace Engine::Core {
 
 
 namespace Defaults {
 namespace Defaults {
@@ -81,7 +85,7 @@ public:
   Game::Units::SpawnType spawn_type{Game::Units::SpawnType::Archer};
   Game::Units::SpawnType spawn_type{Game::Units::SpawnType::Archer};
   int owner_id{0};
   int owner_id{0};
   float vision_range;
   float vision_range;
-  std::string nation_id;
+  Game::Systems::NationID nation_id{Game::Systems::NationID::KingdomOfIron};
 };
 };
 
 
 class MovementComponent : public Component {
 class MovementComponent : public Component {

+ 7 - 4
game/map/skirmish_loader.cpp

@@ -9,6 +9,7 @@
 #include "game/systems/building_collision_registry.h"
 #include "game/systems/building_collision_registry.h"
 #include "game/systems/command_service.h"
 #include "game/systems/command_service.h"
 #include "game/systems/global_stats_registry.h"
 #include "game/systems/global_stats_registry.h"
+#include "game/systems/nation_id.h"
 #include "game/systems/nation_registry.h"
 #include "game/systems/nation_registry.h"
 #include "game/systems/owner_registry.h"
 #include "game/systems/owner_registry.h"
 #include "game/systems/selection_system.h"
 #include "game/systems/selection_system.h"
@@ -158,7 +159,7 @@ auto SkirmishLoader::start(const QString &map_path,
   owner_registry.setLocalPlayerId(player_owner_id);
   owner_registry.setLocalPlayerId(player_owner_id);
 
 
   std::unordered_map<int, int> team_overrides;
   std::unordered_map<int, int> team_overrides;
-  std::unordered_map<int, std::string> nation_overrides;
+  std::unordered_map<int, Game::Systems::NationID> nation_overrides;
   QVariantList saved_player_configs;
   QVariantList saved_player_configs;
   std::set<int> processed_player_ids;
   std::set<int> processed_player_ids;
 
 
@@ -184,14 +185,16 @@ auto SkirmishLoader::start(const QString &map_path,
         processed_player_ids.insert(player_id);
         processed_player_ids.insert(player_id);
         team_overrides[player_id] = team_id;
         team_overrides[player_id] = team_id;
 
 
-        std::string chosen_nation;
+        Game::Systems::NationID chosen_nation;
         if (!nation_id_str.isEmpty()) {
         if (!nation_id_str.isEmpty()) {
-          chosen_nation = nation_id_str.toStdString();
+          auto parsed = Game::Systems::nationIDFromString(nation_id_str.toStdString());
+          chosen_nation = parsed.value_or(
+              Game::Systems::NationRegistry::instance().default_nation_id());
         } else {
         } else {
           chosen_nation =
           chosen_nation =
               Game::Systems::NationRegistry::instance().default_nation_id();
               Game::Systems::NationRegistry::instance().default_nation_id();
         }
         }
-        nation_overrides[player_id] = std::move(chosen_nation);
+        nation_overrides[player_id] = chosen_nation;
 
 
         QVariantMap updated_config = config;
         QVariantMap updated_config = config;
         updated_config["player_id"] = player_id;
         updated_config["player_id"] = player_id;

+ 6 - 1
game/systems/ai_system/ai_tactical.cpp

@@ -225,7 +225,12 @@ auto TacticalUtils::getUnitTypePriority(const std::string &unit_type,
     return 1.0F;
     return 1.0F;
   }
   }
 
 
-  if (unit_type == "barracks" || unit_type == "base") {
+  auto spawn_type = Game::Units::spawn_typeFromString(unit_type);
+  if (spawn_type && Game::Units::isBuildingSpawn(*spawn_type)) {
+    return 0.5F;
+  }
+
+  if (unit_type == "base") {
     return 0.5F;
     return 0.5F;
   }
   }
 
 

+ 62 - 0
game/systems/nation_id.h

@@ -0,0 +1,62 @@
+#pragma once
+
+#include <QString>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <string>
+
+namespace Game::Systems {
+
+enum class NationID : std::uint8_t { KingdomOfIron, RomanRepublic, Carthage };
+
+inline auto nationIDToQString(NationID id) -> QString {
+  switch (id) {
+  case NationID::KingdomOfIron:
+    return QStringLiteral("kingdom_of_iron");
+  case NationID::RomanRepublic:
+    return QStringLiteral("roman_republic");
+  case NationID::Carthage:
+    return QStringLiteral("carthage");
+  }
+  return QStringLiteral("kingdom_of_iron");
+}
+
+inline auto nationIDToString(NationID id) -> std::string {
+  return nationIDToQString(id).toStdString();
+}
+
+inline auto tryParseNationID(const QString &value, NationID &out) -> bool {
+  const QString lowered = value.trimmed().toLower();
+  if (lowered == QStringLiteral("kingdom_of_iron")) {
+    out = NationID::KingdomOfIron;
+    return true;
+  }
+  if (lowered == QStringLiteral("roman_republic")) {
+    out = NationID::RomanRepublic;
+    return true;
+  }
+  if (lowered == QStringLiteral("carthage")) {
+    out = NationID::Carthage;
+    return true;
+  }
+  return false;
+}
+
+inline auto nationIDFromString(const std::string &str) -> std::optional<NationID> {
+  NationID result;
+  if (tryParseNationID(QString::fromStdString(str), result)) {
+    return result;
+  }
+  return std::nullopt;
+}
+
+} // namespace Game::Systems
+
+namespace std {
+template <> struct hash<Game::Systems::NationID> {
+  auto operator()(Game::Systems::NationID id) const noexcept -> size_t {
+    return hash<std::uint8_t>()(static_cast<std::uint8_t>(id));
+  }
+};
+} // namespace std

+ 20 - 8
game/systems/nation_loader.cpp

@@ -1,7 +1,9 @@
 #include "nation_loader.h"
 #include "nation_loader.h"
 
 
+#include "../units/building_type.h"
 #include "../units/troop_catalog.h"
 #include "../units/troop_catalog.h"
 #include "../units/troop_type.h"
 #include "../units/troop_type.h"
+#include "nation_id.h"
 #include "nation_registry.h"
 #include "nation_registry.h"
 #include <QCoreApplication>
 #include <QCoreApplication>
 #include <QDir>
 #include <QDir>
@@ -327,18 +329,28 @@ auto NationLoader::load_from_file(const QString &path)
 
 
   const QJsonObject root = doc.object();
   const QJsonObject root = doc.object();
   Nation nation{};
   Nation nation{};
-  nation.id = root.value("id").toString().toStdString();
-  if (nation.id.empty()) {
+  const QString id_str = root.value("id").toString();
+  if (id_str.isEmpty()) {
     qCWarning(nation_loader_logger())
     qCWarning(nation_loader_logger())
         << "Nation file" << path << "is missing 'id'";
         << "Nation file" << path << "is missing 'id'";
     return std::nullopt;
     return std::nullopt;
   }
   }
-  nation.displayName = root.value("display_name")
-                           .toString(QString::fromStdString(nation.id))
-                           .toStdString();
-  nation.primaryBuilding = root.value("primary_building")
-                               .toString(QStringLiteral("barracks"))
-                               .toStdString();
+
+  auto parsed_id = Game::Systems::nationIDFromString(id_str.toStdString());
+  if (!parsed_id) {
+    qCWarning(nation_loader_logger())
+        << "Nation file" << path << "has unknown nation id:" << id_str;
+    return std::nullopt;
+  }
+  nation.id = *parsed_id;
+
+  nation.displayName = root.value("display_name").toString(id_str).toStdString();
+
+  const QString building_str =
+      root.value("primary_building").toString(QStringLiteral("barracks"));
+  auto parsed_building =
+      Game::Units::buildingTypeFromString(building_str.toStdString());
+  nation.primaryBuilding = parsed_building.value_or(Game::Units::BuildingType::Barracks);
   if (auto formation =
   if (auto formation =
           parse_formation_type(root.value("formation_type").toString())) {
           parse_formation_type(root.value("formation_type").toString())) {
     nation.formation_type = *formation;
     nation.formation_type = *formation;

+ 7 - 10
game/systems/nation_registry.cpp

@@ -101,8 +101,7 @@ void NationRegistry::registerNation(Nation nation) {
   m_nationIndex[m_nations.back().id] = index;
   m_nationIndex[m_nations.back().id] = index;
 }
 }
 
 
-auto NationRegistry::getNation(const std::string &nationId) const
-    -> const Nation * {
+auto NationRegistry::getNation(NationID nationId) const -> const Nation * {
   auto it = m_nationIndex.find(nationId);
   auto it = m_nationIndex.find(nationId);
   if (it == m_nationIndex.end()) {
   if (it == m_nationIndex.end()) {
     return nullptr;
     return nullptr;
@@ -124,8 +123,7 @@ auto NationRegistry::getNationForPlayer(int player_id) const -> const Nation * {
   return nation;
   return nation;
 }
 }
 
 
-void NationRegistry::setPlayerNation(int player_id,
-                                     const std::string &nationId) {
+void NationRegistry::setPlayerNation(int player_id, NationID nationId) {
   m_playerNations[player_id] = nationId;
   m_playerNations[player_id] = nationId;
 }
 }
 
 
@@ -140,9 +138,9 @@ void NationRegistry::initializeDefaults() {
   auto nations = NationLoader::load_default_nations();
   auto nations = NationLoader::load_default_nations();
   if (nations.empty()) {
   if (nations.empty()) {
     Nation kingdom_of_iron;
     Nation kingdom_of_iron;
-    kingdom_of_iron.id = "kingdom_of_iron";
+    kingdom_of_iron.id = NationID::KingdomOfIron;
     kingdom_of_iron.displayName = "Kingdom of Iron";
     kingdom_of_iron.displayName = "Kingdom of Iron";
-    kingdom_of_iron.primaryBuilding = "barracks";
+    kingdom_of_iron.primaryBuilding = Game::Units::BuildingType::Barracks;
     kingdom_of_iron.formation_type = FormationType::Roman;
     kingdom_of_iron.formation_type = FormationType::Roman;
 
 
     auto appendTroop = [&kingdom_of_iron](Game::Units::TroopType type) {
     auto appendTroop = [&kingdom_of_iron](Game::Units::TroopType type) {
@@ -166,12 +164,11 @@ void NationRegistry::initializeDefaults() {
     appendTroop(Game::Units::TroopType::MountedKnight);
     appendTroop(Game::Units::TroopType::MountedKnight);
 
 
     registerNation(std::move(kingdom_of_iron));
     registerNation(std::move(kingdom_of_iron));
-    m_defaultNation = "kingdom_of_iron";
+    m_defaultNation = NationID::KingdomOfIron;
   } else {
   } else {
-    const std::string desired_default = "kingdom_of_iron";
-    std::string fallback_default = nations.front().id;
+    NationID fallback_default = nations.front().id;
     for (auto &nation : nations) {
     for (auto &nation : nations) {
-      if (nation.id == desired_default) {
+      if (nation.id == NationID::KingdomOfIron) {
         fallback_default = nation.id;
         fallback_default = nation.id;
       }
       }
       registerNation(std::move(nation));
       registerNation(std::move(nation));

+ 10 - 10
game/systems/nation_registry.h

@@ -1,7 +1,9 @@
 #pragma once
 #pragma once
 
 
+#include "../units/building_type.h"
 #include "../units/troop_type.h"
 #include "../units/troop_type.h"
 #include "formation_system.h"
 #include "formation_system.h"
+#include "nation_id.h"
 #include <memory>
 #include <memory>
 #include <optional>
 #include <optional>
 #include <string>
 #include <string>
@@ -44,10 +46,10 @@ struct TroopType {
 };
 };
 
 
 struct Nation {
 struct Nation {
-  std::string id;
+  NationID id;
   std::string displayName;
   std::string displayName;
   std::vector<TroopType> availableTroops;
   std::vector<TroopType> availableTroops;
-  std::string primaryBuilding = "barracks";
+  Game::Units::BuildingType primaryBuilding = Game::Units::BuildingType::Barracks;
   FormationType formation_type = FormationType::Roman;
   FormationType formation_type = FormationType::Roman;
   std::unordered_map<Game::Units::TroopType, NationTroopVariant> troopVariants;
   std::unordered_map<Game::Units::TroopType, NationTroopVariant> troopVariants;
 
 
@@ -73,11 +75,11 @@ public:
 
 
   void registerNation(Nation nation);
   void registerNation(Nation nation);
 
 
-  auto getNation(const std::string &nationId) const -> const Nation *;
+  auto getNation(NationID nationId) const -> const Nation *;
 
 
   auto getNationForPlayer(int player_id) const -> const Nation *;
   auto getNationForPlayer(int player_id) const -> const Nation *;
 
 
-  void setPlayerNation(int player_id, const std::string &nationId);
+  void setPlayerNation(int player_id, NationID nationId);
 
 
   auto getAllNations() const -> const std::vector<Nation> & {
   auto getAllNations() const -> const std::vector<Nation> & {
     return m_nations;
     return m_nations;
@@ -89,17 +91,15 @@ public:
 
 
   void clearPlayerAssignments();
   void clearPlayerAssignments();
 
 
-  auto default_nation_id() const -> const std::string & {
-    return m_defaultNation;
-  }
+  auto default_nation_id() const -> NationID { return m_defaultNation; }
 
 
 private:
 private:
   NationRegistry() = default;
   NationRegistry() = default;
 
 
   std::vector<Nation> m_nations;
   std::vector<Nation> m_nations;
-  std::unordered_map<std::string, size_t> m_nationIndex;
-  std::unordered_map<int, std::string> m_playerNations;
-  std::string m_defaultNation = "kingdom_of_iron";
+  std::unordered_map<NationID, size_t> m_nationIndex;
+  std::unordered_map<int, NationID> m_playerNations;
+  NationID m_defaultNation = NationID::KingdomOfIron;
   bool m_initialized = false;
   bool m_initialized = false;
 };
 };
 
 

+ 3 - 7
game/systems/production_service.cpp

@@ -33,11 +33,7 @@ findFirstSelectedBarracks(Engine::Core::World &world,
 namespace {
 namespace {
 
 
 auto resolve_nation_id(const Engine::Core::UnitComponent *unit,
 auto resolve_nation_id(const Engine::Core::UnitComponent *unit,
-                       int owner_id) -> std::string {
-  if ((unit != nullptr) && !unit->nation_id.empty()) {
-    return unit->nation_id;
-  }
-
+                       int owner_id) -> Game::Systems::NationID {
   auto &registry = NationRegistry::instance();
   auto &registry = NationRegistry::instance();
   if (const auto *nation = registry.getNationForPlayer(owner_id)) {
   if (const auto *nation = registry.getNationForPlayer(owner_id)) {
     return nation->id;
     return nation->id;
@@ -46,7 +42,7 @@ auto resolve_nation_id(const Engine::Core::UnitComponent *unit,
 }
 }
 
 
 void apply_production_profile(Engine::Core::ProductionComponent *prod,
 void apply_production_profile(Engine::Core::ProductionComponent *prod,
-                              const std::string &nation_id,
+                              Game::Systems::NationID nation_id,
                               Game::Units::TroopType unit_type) {
                               Game::Units::TroopType unit_type) {
   if (prod == nullptr) {
   if (prod == nullptr) {
     return;
     return;
@@ -68,7 +64,7 @@ auto ProductionService::startProductionForFirstSelectedBarracks(
     return ProductionResult::NoBarracks;
     return ProductionResult::NoBarracks;
   }
   }
   auto *unit = e->getComponent<Engine::Core::UnitComponent>();
   auto *unit = e->getComponent<Engine::Core::UnitComponent>();
-  const std::string nation_id = resolve_nation_id(unit, owner_id);
+  const auto nation_id = resolve_nation_id(unit, owner_id);
   const auto profile =
   const auto profile =
       TroopProfileService::instance().get_profile(nation_id, unit_type);
       TroopProfileService::instance().get_profile(nation_id, unit_type);
 
 

+ 3 - 6
game/systems/production_system.cpp

@@ -18,7 +18,7 @@ namespace Game::Systems {
 namespace {
 namespace {
 
 
 void apply_production_profile(Engine::Core::ProductionComponent *prod,
 void apply_production_profile(Engine::Core::ProductionComponent *prod,
-                              const std::string &nation_id,
+                              Game::Systems::NationID nation_id,
                               Game::Units::TroopType troop_type) {
                               Game::Units::TroopType troop_type) {
   if (prod == nullptr) {
   if (prod == nullptr) {
     return;
     return;
@@ -30,10 +30,7 @@ void apply_production_profile(Engine::Core::ProductionComponent *prod,
 }
 }
 
 
 auto resolve_nation_id(const Engine::Core::UnitComponent *unit,
 auto resolve_nation_id(const Engine::Core::UnitComponent *unit,
-                       int owner_id) -> std::string {
-  if ((unit != nullptr) && !unit->nation_id.empty()) {
-    return unit->nation_id;
-  }
+                       int owner_id) -> Game::Systems::NationID {
   auto &registry = NationRegistry::instance();
   auto &registry = NationRegistry::instance();
   if (const auto *nation = registry.getNationForPlayer(owner_id)) {
   if (const auto *nation = registry.getNationForPlayer(owner_id)) {
     return nation->id;
     return nation->id;
@@ -65,7 +62,7 @@ void ProductionSystem::update(Engine::Core::World *world, float deltaTime) {
     }
     }
 
 
     const int owner_id = (unit_comp != nullptr) ? unit_comp->owner_id : -1;
     const int owner_id = (unit_comp != nullptr) ? unit_comp->owner_id : -1;
-    const std::string nation_id = resolve_nation_id(unit_comp, owner_id);
+    const auto nation_id = resolve_nation_id(unit_comp, owner_id);
     const auto current_profile = TroopProfileService::instance().get_profile(
     const auto current_profile = TroopProfileService::instance().get_profile(
         nation_id, prod->product_type);
         nation_id, prod->product_type);
     int const individuals_per_unit = current_profile.individuals_per_unit;
     int const individuals_per_unit = current_profile.individuals_per_unit;

+ 4 - 3
game/systems/troop_profile_service.cpp

@@ -12,8 +12,9 @@ auto TroopProfileService::instance() -> TroopProfileService & {
 
 
 void TroopProfileService::clear() { m_cache.clear(); }
 void TroopProfileService::clear() { m_cache.clear(); }
 
 
-auto TroopProfileService::get_profile(
-    const std::string &nation_id, Game::Units::TroopType type) -> TroopProfile {
+auto TroopProfileService::get_profile(NationID nation_id,
+                                      Game::Units::TroopType type)
+    -> TroopProfile {
   auto &nationCache = m_cache[nation_id];
   auto &nationCache = m_cache[nation_id];
   auto cached = nationCache.find(type);
   auto cached = nationCache.find(type);
   if (cached != nationCache.end()) {
   if (cached != nationCache.end()) {
@@ -22,7 +23,7 @@ auto TroopProfileService::get_profile(
 
 
   const Nation *nation = NationRegistry::instance().getNation(nation_id);
   const Nation *nation = NationRegistry::instance().getNation(nation_id);
   if (nation == nullptr) {
   if (nation == nullptr) {
-    const auto &fallback_id = NationRegistry::instance().default_nation_id();
+    const auto fallback_id = NationRegistry::instance().default_nation_id();
     nation = NationRegistry::instance().getNation(fallback_id);
     nation = NationRegistry::instance().getNation(fallback_id);
     if (nation == nullptr) {
     if (nation == nullptr) {
       const auto &all = NationRegistry::instance().getAllNations();
       const auto &all = NationRegistry::instance().getAllNations();

+ 3 - 3
game/systems/troop_profile_service.h

@@ -22,8 +22,8 @@ class TroopProfileService {
 public:
 public:
   static auto instance() -> TroopProfileService &;
   static auto instance() -> TroopProfileService &;
 
 
-  auto get_profile(const std::string &nation_id,
-                   Game::Units::TroopType type) -> TroopProfile;
+  auto get_profile(NationID nation_id, Game::Units::TroopType type)
+      -> TroopProfile;
 
 
   void clear();
   void clear();
 
 
@@ -33,7 +33,7 @@ private:
   auto build_profile(const Nation &nation,
   auto build_profile(const Nation &nation,
                      Game::Units::TroopType type) -> TroopProfile;
                      Game::Units::TroopType type) -> TroopProfile;
 
 
-  std::unordered_map<std::string,
+  std::unordered_map<NationID,
                      std::unordered_map<Game::Units::TroopType, TroopProfile>>
                      std::unordered_map<Game::Units::TroopType, TroopProfile>>
       m_cache;
       m_cache;
 };
 };

+ 1 - 1
game/units/archer.cpp

@@ -39,7 +39,7 @@ void Archer::init(const SpawnParams &params) {
   auto *e = m_world->createEntity();
   auto *e = m_world->createEntity();
   m_id = e->getId();
   m_id = e->getId();
 
 
-  const std::string nation_id = resolve_nation_id(params);
+  const auto nation_id = resolve_nation_id(params);
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
       nation_id, TroopType::Archer);
       nation_id, TroopType::Archer);
 
 

+ 1 - 1
game/units/barracks.cpp

@@ -26,7 +26,7 @@ void Barracks::init(const SpawnParams &params) {
   auto *e = m_world->createEntity();
   auto *e = m_world->createEntity();
   m_id = e->getId();
   m_id = e->getId();
 
 
-  const std::string nation_id = resolve_nation_id(params);
+  const auto nation_id = resolve_nation_id(params);
 
 
   m_t = e->addComponent<Engine::Core::TransformComponent>();
   m_t = e->addComponent<Engine::Core::TransformComponent>();
   m_t->position = {params.position.x(), params.position.y(),
   m_t->position = {params.position.x(), params.position.y(),

+ 42 - 0
game/units/building_type.h

@@ -0,0 +1,42 @@
+#pragma once
+
+#include <QString>
+#include <optional>
+#include <string>
+
+namespace Game::Units {
+
+enum class BuildingType : std::uint8_t { Barracks };
+
+inline auto buildingTypeToQString(BuildingType type) -> QString {
+  switch (type) {
+  case BuildingType::Barracks:
+    return QStringLiteral("barracks");
+  }
+  return QStringLiteral("barracks");
+}
+
+inline auto buildingTypeToString(BuildingType type) -> std::string {
+  return buildingTypeToQString(type).toStdString();
+}
+
+inline auto tryParseBuildingType(const QString &value, BuildingType &out)
+    -> bool {
+  const QString lowered = value.trimmed().toLower();
+  if (lowered == QStringLiteral("barracks")) {
+    out = BuildingType::Barracks;
+    return true;
+  }
+  return false;
+}
+
+inline auto
+buildingTypeFromString(const std::string &str) -> std::optional<BuildingType> {
+  BuildingType result;
+  if (tryParseBuildingType(QString::fromStdString(str), result)) {
+    return result;
+  }
+  return std::nullopt;
+}
+
+} // namespace Game::Units

+ 1 - 1
game/units/mounted_knight.cpp

@@ -41,7 +41,7 @@ void MountedKnight::init(const SpawnParams &params) {
   auto *e = m_world->createEntity();
   auto *e = m_world->createEntity();
   m_id = e->getId();
   m_id = e->getId();
 
 
-  const std::string nation_id = resolve_nation_id(params);
+  const auto nation_id = resolve_nation_id(params);
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
       nation_id, TroopType::MountedKnight);
       nation_id, TroopType::MountedKnight);
 
 

+ 1 - 1
game/units/spearman.cpp

@@ -40,7 +40,7 @@ void Spearman::init(const SpawnParams &params) {
   auto *e = m_world->createEntity();
   auto *e = m_world->createEntity();
   m_id = e->getId();
   m_id = e->getId();
 
 
-  const std::string nation_id = resolve_nation_id(params);
+  const auto nation_id = resolve_nation_id(params);
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
       nation_id, TroopType::Spearman);
       nation_id, TroopType::Spearman);
 
 

+ 1 - 1
game/units/swordsman.cpp

@@ -40,7 +40,7 @@ void Swordsman::init(const SpawnParams &params) {
   auto *e = m_world->createEntity();
   auto *e = m_world->createEntity();
   m_id = e->getId();
   m_id = e->getId();
 
 
-  const std::string nation_id = resolve_nation_id(params);
+  const auto nation_id = resolve_nation_id(params);
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
   auto profile = Game::Systems::TroopProfileService::instance().get_profile(
       nation_id, TroopType::Swordsman);
       nation_id, TroopType::Swordsman);
 
 

+ 3 - 11
game/units/unit.cpp

@@ -2,6 +2,7 @@
 
 
 #include "../core/component.h"
 #include "../core/component.h"
 #include "../core/world.h"
 #include "../core/world.h"
+#include "../systems/nation_id.h"
 #include "../systems/nation_registry.h"
 #include "../systems/nation_registry.h"
 #include "units/troop_type.h"
 #include "units/troop_type.h"
 #include <qvectornd.h>
 #include <qvectornd.h>
@@ -20,22 +21,13 @@ auto Unit::entity() const -> Engine::Core::Entity * {
   return (m_world != nullptr) ? m_world->getEntity(m_id) : nullptr;
   return (m_world != nullptr) ? m_world->getEntity(m_id) : nullptr;
 }
 }
 
 
-auto Unit::resolve_nation_id(const SpawnParams &params) -> std::string {
-  if (!params.nation_id.empty()) {
-    return params.nation_id;
-  }
-
+auto Unit::resolve_nation_id(const SpawnParams &params) -> Game::Systems::NationID {
   auto &registry = Game::Systems::NationRegistry::instance();
   auto &registry = Game::Systems::NationRegistry::instance();
   if (const auto *nation = registry.getNationForPlayer(params.player_id)) {
   if (const auto *nation = registry.getNationForPlayer(params.player_id)) {
     return nation->id;
     return nation->id;
   }
   }
 
 
-  const auto &fallback_id = registry.default_nation_id();
-  if (const auto *nation = registry.getNation(fallback_id)) {
-    return nation->id;
-  }
-
-  return fallback_id;
+  return registry.default_nation_id();
 }
 }
 
 
 void Unit::ensureCoreComponents() {
 void Unit::ensureCoreComponents() {

+ 6 - 2
game/units/unit.h

@@ -7,6 +7,10 @@
 #include <string>
 #include <string>
 #include <utility>
 #include <utility>
 
 
+namespace Game::Systems {
+enum class NationID : std::uint8_t;
+}
+
 namespace Engine::Core {
 namespace Engine::Core {
 class World;
 class World;
 class Entity;
 class Entity;
@@ -27,7 +31,7 @@ struct SpawnParams {
   SpawnType spawn_type = SpawnType::Archer;
   SpawnType spawn_type = SpawnType::Archer;
   bool aiControlled = false;
   bool aiControlled = false;
   int maxPopulation = 100;
   int maxPopulation = 100;
-  std::string nation_id;
+  Game::Systems::NationID nation_id = Game::Systems::NationID::KingdomOfIron;
 };
 };
 
 
 class Unit {
 class Unit {
@@ -53,7 +57,7 @@ protected:
 
 
   void ensureCoreComponents();
   void ensureCoreComponents();
 
 
-  static auto resolve_nation_id(const SpawnParams &params) -> std::string;
+  static auto resolve_nation_id(const SpawnParams &params) -> Game::Systems::NationID;
 
 
   Engine::Core::World *m_world = nullptr;
   Engine::Core::World *m_world = nullptr;
   Engine::Core::EntityID m_id = 0;
   Engine::Core::EntityID m_id = 0;