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; }