diff --git a/Vic3ToHoI4lib.vcxproj b/Vic3ToHoI4lib.vcxproj
index 693da461..b8fb8d0a 100644
--- a/Vic3ToHoI4lib.vcxproj
+++ b/Vic3ToHoI4lib.vcxproj
@@ -67,6 +67,7 @@
+
@@ -186,6 +187,7 @@
+
diff --git a/Vic3ToHoI4lib.vcxproj.filters b/Vic3ToHoI4lib.vcxproj.filters
index 3552a29d..eb50504b 100644
--- a/Vic3ToHoI4lib.vcxproj.filters
+++ b/Vic3ToHoI4lib.vcxproj.filters
@@ -166,6 +166,9 @@
{6a73785e-ecc8-45e2-b5b4-a89ffff0628c}
+
+ {d5fd8e32-66d1-4432-9d35-e933e67b6029}
+
@@ -591,6 +594,9 @@
src\mappers\infrastructure
+
+ src\mappers\industry
+
@@ -902,6 +908,9 @@
src\out_hoi4\national_focus
+
+ src\mappers\industry
+
diff --git a/external/Fronter b/external/Fronter
index 2f20d275..f7c1bdf1 160000
--- a/external/Fronter
+++ b/external/Fronter
@@ -1 +1 @@
-Subproject commit 2f20d27518360dd58f705a2aa3b679865cca0926
+Subproject commit f7c1bdf1408a936dd9e3e873bd43d4835f7b4f24
diff --git a/external/commonItems b/external/commonItems
index 72ab9c4d..bdebad6c 160000
--- a/external/commonItems
+++ b/external/commonItems
@@ -1 +1 @@
-Subproject commit 72ab9c4da4bdf521615bcacf940b903618bdb406
+Subproject commit bdebad6c947d5e528b43dcc4f8ff7d76d5bfd251
diff --git a/external/fmt b/external/fmt
index c48353cb..0bffed89 160000
--- a/external/fmt
+++ b/external/fmt
@@ -1 +1 @@
-Subproject commit c48353cb753889c38a94809b5bc72cedc1dedd26
+Subproject commit 0bffed89579ac8a210379fd640569e4b75fe28c8
diff --git a/src/hoi4_world/states/hoi4_states_converter.cpp b/src/hoi4_world/states/hoi4_states_converter.cpp
index 4ffec7b7..e093519d 100644
--- a/src/hoi4_world/states/hoi4_states_converter.cpp
+++ b/src/hoi4_world/states/hoi4_states_converter.cpp
@@ -1,5 +1,7 @@
#include "src/hoi4_world/states/hoi4_states_converter.h"
+#include
+
#include
#include
#include
@@ -10,8 +12,9 @@
#include "external/commonItems/Log.h"
#include "external/fmt/include/fmt/format.h"
+#include "src/mappers/industry/industry_mapper.h"
#include "src/maps/map_data.h"
-
+#include "src/out_hoi4/world/out_world.h"
namespace
@@ -739,16 +742,13 @@ void LogIndustryStats(const std::vector& hoi4_states,
}
}
- Log(LogLevel::Info) << fmt::format("\t\tTotal factories: {} (vanilla hoi4 had {})",
+ out::OutputStats("Total factories",
civilian_factories + military_factories + dockyards,
default_civilian_factories + default_military_factories + default_dockyards);
- Log(LogLevel::Info) << fmt::format("\t\t\tCivilian factories: {} (vanilla hoi4 had {})",
- civilian_factories,
- default_civilian_factories);
- Log(LogLevel::Info) << fmt::format("\t\t\tMilitary factories: {} (vanilla hoi4 had {})",
- military_factories,
- default_military_factories);
- Log(LogLevel::Info) << fmt::format("\t\t\tDockyards: {} (vanilla hoi4 had {})", dockyards, default_dockyards);
+ out::OutputStats("Civilian factories", civilian_factories, default_civilian_factories);
+ out::OutputStats("Military factories", military_factories, default_military_factories);
+ out::OutputStats("Dockyards", dockyards, default_dockyards);
+
for (const auto& [factories, num_states]: state_factory_numbers)
{
Log(LogLevel::Info) << fmt::format("\t\t\t{} states had {} factories", num_states, factories);
@@ -790,19 +790,14 @@ void LogManpowerStats(const std::vector& hoi4_states,
return total + state.second.GetManpower();
});
- Log(LogLevel::Info) << fmt::format("\t\tManpower conversion: total={}, target={}, match={}%",
- manpower,
- default_manpower,
- static_cast(manpower) / static_cast(default_manpower) * 100.0F);
+ out::OutputStats("Manpower", manpower, default_manpower);
}
void LogInfrastructure(mappers::InfrastructureMapper infrastructure_mapper)
{
- Log(LogLevel::Info) << fmt::format("Infrastructure conversion: total={}, target={}, match={}%",
+ out::OutputStats("Infrastructure",
infrastructure_mapper.GetConvertedInfrastructure(),
- infrastructure_mapper.GetTargetInfrastructure(),
- static_cast(infrastructure_mapper.GetConvertedInfrastructure()) /
- static_cast(infrastructure_mapper.GetTargetInfrastructure()) * 100.0F);
+ infrastructure_mapper.GetTargetInfrastructure());
Log(LogLevel::Info) << fmt::format("\tfudge factor is {}", infrastructure_mapper.GetFudgeFactor());
}
@@ -833,7 +828,7 @@ hoi4::States CreateStates(const vic3::World& source_world,
MapVic3ProvincesToStateNames(source_world.GetStateRegions());
std::map hoi4_state_names_to_vic3_state_names;
mappers::InfrastructureMapper infrastructure_mapper(source_world.GetStates());
-
+ mappers::IndustryMapper industry_mapper(source_world);
for (const auto& vic3_state_id: vic3_state_ids_by_vic3_industry)
{
const auto& hoi4_provinces = vic3_state_id_to_hoi4_provinces.at(vic3_state_id);
@@ -876,10 +871,9 @@ hoi4::States CreateStates(const vic3::World& source_world,
return total + static_cast(province_set.size());
});
int total_non_wasteland_provinces = static_cast(hoi4_provinces.size()) - total_wasteland_provinces;
-
const int64_t total_manpower = vic3_state_itr->second.GetPopulation();
- const float total_factories =
- static_cast(source_world.GetBuildings().GetTotalGoodSalesValueInState(vic3_state_id)) / 175'000.0F;
+ const float total_factories = industry_mapper.Map(source_world, vic3_state_id);
+ // static_cast(source_world.GetBuildings().GetTotalGoodSalesValueInState(vic3_state_id)) / 175'000.0F;
for (const auto& province_set: final_connected_province_sets)
{
RecordStateNamesMapping(province_set,
diff --git a/src/hoi4_world/world/hoi4_world.h b/src/hoi4_world/world/hoi4_world.h
index 0f3183fb..24782155 100644
--- a/src/hoi4_world/world/hoi4_world.h
+++ b/src/hoi4_world/world/hoi4_world.h
@@ -8,6 +8,7 @@
#include
#include "external/commonItems/Localization/LocalizationDatabase.h"
+#include "external/fmt/include/fmt/format.h"
#include "src/hoi4_world/characters/hoi4_character.h"
#include "src/hoi4_world/countries/hoi4_country.h"
#include "src/hoi4_world/localizations/localizations.h"
diff --git a/src/mappers/industry/industry_mapper.cpp b/src/mappers/industry/industry_mapper.cpp
new file mode 100644
index 00000000..68bc5889
--- /dev/null
+++ b/src/mappers/industry/industry_mapper.cpp
@@ -0,0 +1,121 @@
+#include
+#include "industry_mapper.h"
+
+namespace mappers
+{
+
+IndustryMapper::IndustryMapper(const vic3::World& source_world)
+{
+ // TODO: make a config value
+ target_global_factories_ = 1226.0F;
+ factory_lower_balancing_limit_ = 100.0F;
+
+ // there's probably a faster way to do this
+
+ for (const auto& state : source_world.GetStates())
+ {
+ if(state.second.GetOwnerNumber().has_value())
+ {
+ int owner = state.second.GetOwnerNumber().value();
+ auto countryInfo =
+ std::find_if(this->countryBalances.begin(), this->countryBalances.end(), [owner](const CountryBalanceInformation& balanceInfo) {
+ return balanceInfo.country_id == owner;
+ });
+ if (countryInfo == this->countryBalances.end())
+ {
+ countryBalances.emplace_back(CountryBalanceInformation{owner,
+ source_world.GetBuildings().GetTotalGoodSalesValueInState(state.first)});
+ }
+ else
+ {
+ countryInfo->raw_vic3_factories += source_world.GetBuildings().GetTotalGoodSalesValueInState(state.first);
+ }
+ }
+ }
+
+ std::sort(this->countryBalances.begin(), this->countryBalances.end(), [](const auto& a, const auto& b) {
+ return a.raw_vic3_factories > b.raw_vic3_factories;
+ });
+ BalanceFactories();
+
+
+
+ float base_hoi4_factories = std::accumulate(this->countryBalances.begin(),
+ this->countryBalances.end(),
+ 0.0F,
+ [](float sum, CountryBalanceInformation country) {
+ return sum + country.converted_vic3_factories;
+ });
+
+ target_global_factory_ratio_ = target_global_factories_ / base_hoi4_factories;
+}
+
+float IndustryMapper::Map(const vic3::World& source_world, int state_number)
+{
+ const int owner = source_world.GetStates().at(state_number).GetOwnerNumber().value();
+ auto countryInfo = std::find_if(this->countryBalances.begin(),
+ this->countryBalances.end(),
+ [owner](const CountryBalanceInformation& balanceInfo) {
+ return balanceInfo.country_id == owner;
+ });
+ return source_world.GetBuildings().GetTotalGoodSalesValueInState(state_number) * target_global_factory_ratio_ *
+ countryInfo->converted_factory_ratio;
+}
+
+
+float max_power_ratio_by_overkill(float country_power, float prev_country_power)
+{
+ float raw_power_ratio = country_power / prev_country_power;
+ if (raw_power_ratio <= 1.2)
+ {
+ return raw_power_ratio;
+ }
+ else
+ {
+ // played with in desmos, this power curve looks good
+ return 4 / 3 * log10(2*raw_power_ratio - 1) + 1;
+ }
+}
+
+void IndustryMapper::BalanceFactories()
+{
+ {
+ auto iter = this->countryBalances.rbegin();
+ while (iter->raw_vic3_factories == 0)
+ {
+ ++iter;
+ }
+ float prev_raw_vic3_factories = iter->raw_vic3_factories;
+ for (; iter != this->countryBalances.rend(); ++iter)
+ {
+ iter->max_ratio_by_competition = iter->raw_vic3_factories / prev_raw_vic3_factories;
+ iter->max_ratio_by_overkill = max_power_ratio_by_overkill(iter->raw_vic3_factories, prev_raw_vic3_factories);
+ prev_raw_vic3_factories = iter->raw_vic3_factories;
+ }
+ }
+
+ //and now, we adjust factory count
+ {
+ auto iter = this->countryBalances.rbegin();
+ while (iter->raw_vic3_factories == 0)
+ {
+ ++iter;
+ }
+ float prev_converted_vic3_factories = iter->raw_vic3_factories;
+ for (; iter != this->countryBalances.rend(); ++iter)
+ {
+ if (iter->raw_vic3_factories < factory_lower_balancing_limit_)
+ {
+ iter->converted_vic3_factories = iter->raw_vic3_factories;
+ }
+ else
+ {
+ iter->converted_vic3_factories =
+ std::min(iter->max_ratio_by_competition, iter->max_ratio_by_overkill) * prev_converted_vic3_factories;
+ }
+ prev_converted_vic3_factories = iter->converted_vic3_factories;
+ iter->converted_factory_ratio = iter->converted_vic3_factories / iter->raw_vic3_factories;
+ }
+ }
+}
+}
\ No newline at end of file
diff --git a/src/mappers/industry/industry_mapper.h b/src/mappers/industry/industry_mapper.h
new file mode 100644
index 00000000..95515aaf
--- /dev/null
+++ b/src/mappers/industry/industry_mapper.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "src/vic3_world/world/vic3_world.h"
+
+namespace mappers
+{
+struct CountryBalanceInformation
+{
+ int country_id;
+ float raw_vic3_factories;
+ // factories(n)/factories(n-1): can't gain an advantage over the next strongest player via conversion
+ float max_ratio_by_competition;
+ // log2 formula: above 120% of next strongest player, adjust max power down to give a more balanced game
+ float max_ratio_by_overkill;
+
+ // output "balanced" factory count
+ float converted_vic3_factories;
+ // ratio of "balanced" factory count to initial count, so we can multiply by this for each state
+ float converted_factory_ratio;
+ auto operator<=>(const CountryBalanceInformation& other) const = default;
+};
+
+/// maps factories, and potentially balances.
+class IndustryMapper
+{
+ public:
+ IndustryMapper(const vic3::World& source_world);
+ float Map(const vic3::World& source_world, int state_number);
+
+ private:
+ std::vector countryBalances = {};
+ float target_global_factories_ = 0.0F;
+ float target_global_factory_ratio_ = 0.0F;
+ // below this limit, no balancing will take place.
+ // this allows small vic3 nations, which may have dramatic gdp differences, to retain
+ // that, while smoothing out the power of larger nations
+ float factory_lower_balancing_limit_ = 0.0F;
+
+ void BalanceFactories();
+};
+} // namespace mappers
\ No newline at end of file
diff --git a/src/maps/map_data_importer.cpp b/src/maps/map_data_importer.cpp
index 1045ebcf..d730e4f0 100644
--- a/src/maps/map_data_importer.cpp
+++ b/src/maps/map_data_importer.cpp
@@ -159,6 +159,7 @@ void maps::MapDataImporter::ImportProvinces(const commonItems::ModFilesystem& mo
}
}
}
+ Log(LogLevel::Progress) << 50 + (10 * y / height);
}
}
diff --git a/src/out_hoi4/world/out_world.cpp b/src/out_hoi4/world/out_world.cpp
index 977d506b..7804395e 100644
--- a/src/out_hoi4/world/out_world.cpp
+++ b/src/out_hoi4/world/out_world.cpp
@@ -77,8 +77,6 @@ void OutputBookmark(std::string_view output_name,
} // namespace
-
-
void out::OutputWorld(std::string_view output_name, const hoi4::World& world)
{
OutputCountries(output_name, world.GetCountries(), world.GetCharacters());
@@ -97,4 +95,19 @@ void out::OutputWorld(std::string_view output_name, const hoi4::World& world)
true,
world.GetGreatPowers(),
world.GetMajorPowers());
+}
+
+void out::OutputStats(const std::string& stat_name, double converted_value, double base_value)
+{
+ double percent = static_cast(converted_value - base_value) / static_cast(base_value) * 100.0;
+ if (!std::isfinite(percent))
+ {
+ percent = 0.0;
+ }
+
+ Log(LogLevel::Info) << fmt::format("\t\t{} conversion: total={:.3f}, target={:.3f}, diff={:.3f}%",
+ stat_name,
+ converted_value,
+ base_value,
+ percent);
}
\ No newline at end of file
diff --git a/src/out_hoi4/world/out_world.h b/src/out_hoi4/world/out_world.h
index affbaf99..15895d18 100644
--- a/src/out_hoi4/world/out_world.h
+++ b/src/out_hoi4/world/out_world.h
@@ -12,6 +12,7 @@
namespace out
{
+ void OutputStats(const std::string& stat_name, double converted_value, double base_value);
void OutputWorld(std::string_view output_name, const hoi4::World& world);
} // namespace out
diff --git a/src/vic3_world/states/vic3_state.h b/src/vic3_world/states/vic3_state.h
index 3e2ca54e..ece88fde 100644
--- a/src/vic3_world/states/vic3_state.h
+++ b/src/vic3_world/states/vic3_state.h
@@ -12,6 +12,7 @@ namespace vic3
struct StateOptions
{
+ int id = 0;
std::optional owner_number;
std::optional owner_tag;
bool incorporated = false;
@@ -27,6 +28,7 @@ class State
public:
State() = default;
explicit State(StateOptions state_options):
+ id_(state_options.id),
owner_number_(state_options.owner_number),
owner_tag_(std::move(state_options.owner_tag)),
incorporated_(state_options.incorporated),
@@ -37,6 +39,7 @@ class State
{
}
+ [[nodiscard]] const int GetId() const { return id_; }
[[nodiscard]] const std::optional& GetOwnerNumber() const { return owner_number_; }
[[nodiscard]] const std::optional& GetOwnerTag() const { return owner_tag_; }
[[nodiscard]] bool IsIncorporated() const { return incorporated_; }
@@ -50,6 +53,7 @@ class State
bool operator==(const State&) const = default;
private:
+ int id_;
std::optional owner_number_;
std::optional owner_tag_;
bool incorporated_ = false;
diff --git a/src/vic3_world/states/vic3_state_importer.cpp b/src/vic3_world/states/vic3_state_importer.cpp
index 29fd2ac2..dcd66851 100644
--- a/src/vic3_world/states/vic3_state_importer.cpp
+++ b/src/vic3_world/states/vic3_state_importer.cpp
@@ -91,7 +91,7 @@ vic3::StateImporter::StateImporter()
}
-vic3::State vic3::StateImporter::ImportState(std::istream& input_stream)
+vic3::State vic3::StateImporter::ImportState(const int state_number, std::istream& input_stream)
{
owner_number_.reset();
incorporated_ = false;
@@ -102,7 +102,8 @@ vic3::State vic3::StateImporter::ImportState(std::istream& input_stream)
state_parser_.parseStream(input_stream);
- return State({.owner_number = owner_number_,
+ return State({.id = state_number,
+ .owner_number = owner_number_,
.incorporated = incorporated_,
.infrastructure = infrastructure_,
.provinces = provinces_,
diff --git a/src/vic3_world/states/vic3_state_importer.h b/src/vic3_world/states/vic3_state_importer.h
index 22b16255..2f6f9893 100644
--- a/src/vic3_world/states/vic3_state_importer.h
+++ b/src/vic3_world/states/vic3_state_importer.h
@@ -19,7 +19,7 @@ class StateImporter
public:
StateImporter();
- [[nodiscard]] State ImportState(std::istream& input_stream);
+ [[nodiscard]] State ImportState(const int state_number, std::istream& input_stream);
private:
commonItems::parser state_parser_;
diff --git a/src/vic3_world/states/vic3_states_importer.cpp b/src/vic3_world/states/vic3_states_importer.cpp
index 170e5ffa..f6e70e48 100644
--- a/src/vic3_world/states/vic3_states_importer.cpp
+++ b/src/vic3_world/states/vic3_states_importer.cpp
@@ -23,7 +23,7 @@ std::map vic3::ImportStates(std::istream& input_stream)
return;
}
std::istringstream state_stream(state_string);
- states.emplace(state_number, state_importer.ImportState(state_stream));
+ states.emplace(state_number, state_importer.ImportState(state_number, state_stream));
});
commonItems::parser states_parser;
diff --git a/src/vic3_world/world/vic3_world_builder.cpp b/src/vic3_world/world/vic3_world_builder.cpp
index 792b795c..e9231fce 100644
--- a/src/vic3_world/world/vic3_world_builder.cpp
+++ b/src/vic3_world/world/vic3_world_builder.cpp
@@ -40,7 +40,7 @@ WorldBuilder& WorldBuilder::AddTestStates(const std::vector>& prov
{
const int stateNum = state_number_++;
this->world_options_.states.emplace(stateNum,
- vic3::State(vic3::StateOptions{.owner_number = stateNum, .provinces = provinceList}));
+ vic3::State(vic3::StateOptions{.id = stateNum, .owner_number = stateNum, .provinces = provinceList}));
}
return *this;
}