28 #include <tbb/blocked_range.h>
29 #include <tbb/concurrent_vector.h>
30 #include <tbb/parallel_for.h>
31 #include <tbb/combinable.h>
32 #include <glm/gtx/compatibility.hpp>
34 #include "eng3d/log.hpp"
35 #include "eng3d/serializer.hpp"
36 #include "eng3d/rand.hpp"
50 typedef signed int ssize_t;
74 constexpr
auto friction = 0.1f;
75 return (c * (1.f - friction)) + friction * target;
79 static void update_industry_production(
World& world,
Building& building,
const BuildingType& building_type,
Province& province,
float& artisans_amount,
float& artisan_payment)
81 constexpr
auto artisan_production_rate = 0.01f;
82 auto& output = world.commodities[building_type.
output_id];
83 auto& output_product = province.
products[output];
85 if(output_product.supply < output_product.demand) {
86 const auto output_amount = artisans_amount * artisan_production_rate;
87 artisan_payment += output_product.produce(output_amount);
94 for(
const auto& [product_id, amount] : building_type.
req_goods) {
95 auto& product = province.
products[product_id];
96 const auto max_amount = glm::min(amount * building.
production_scale, product.supply);
102 static void update_industry_accounting(
World& world,
Building& building,
const BuildingType& building_type,
Province& province,
float& pop_payment,
float& state_payment,
float& private_payment)
109 const auto private_investment = private_payment * 0.8f * nation.current_policy.private_ownership;
111 building.
budget += private_investment;
113 auto& output = world.commodities[building_type.
output_id];
114 auto& output_product = province.
products[output];
122 float min_wage = glm::max(nation.current_policy.min_wage, glm::epsilon<float>());
138 auto surplus = profit - building.
expenses.get_total();
144 state_payment += state_dividends;
146 surplus -= state_dividends;
150 private_payment += private_dividends;
152 surplus -= private_dividends;
156 pop_payment += collective_dividends;
158 surplus -= collective_dividends;
160 profit -= building.
expenses.get_dividends();
163 building.
budget += profit;
166 if(building.
budget >= upgrade_cost) {
167 building.
budget -= upgrade_cost;
168 building.
level += 1.f;
169 }
else if(building.
budget <= -(upgrade_cost * 0.5f)) {
170 building.
budget += upgrade_cost;
171 building.
level -= 1.f;
187 if(max_revenue == 0.f) {
189 }
else if(building.
expenses.get_total() == 0.f) {
197 static void update_factories_employment(
const World& world,
Province& province, std::vector<float>& new_workers) {
201 std::vector<std::pair<size_t, float>> factories_by_profitability;
202 for(
size_t i = 0; i < province.
buildings.size(); i++)
203 factories_by_profitability.emplace_back(i, province.
buildings[i].production_scale);
204 std::sort(factories_by_profitability.begin(), factories_by_profitability.end(), [](
const auto& a,
const auto& b) { return a.second > b.second; });
207 for(
const auto& [industry_index, _] : factories_by_profitability) {
208 auto& building = province.
buildings[industry_index];
209 const auto& type = world.building_types[industry_index];
211 auto allocated_workers = glm::max(glm::min(industry_workers, unallocated_workers), 0.f);
214 new_workers[industry_index] = (allocated_workers / 16.0f + (building.
workers * 15.0f) / 16.0f) * is_operating;
215 unallocated_workers -= building.
workers * is_operating;
223 std::vector<float> goods_payment(world.commodities.size(), 0.f);
224 for(
size_t i = 0; i < province.
pops.size(); i++) {
225 auto& pop_need = pop_needs[i];
226 auto& pop = province.
pops[i];
227 const auto& needs_amounts = world.pop_types[pop.type_id].basic_needs_amount;
228 if(pop.size == 0.f || pop_need.budget == 0.f)
return;
230 const auto percentage_to_spend = 0.8f;
231 const auto budget_alloc = pop_need.budget * percentage_to_spend;
232 pop_need.budget -= budget_alloc;
235 state_payment += budget_alloc * nation.current_policy.pop_tax;
236 const auto budget_after_VAT = budget_alloc * (1.f - nation.current_policy.pop_tax);
237 const auto budget_per_pop = budget_after_VAT / pop.size;
239 auto total_factor = std::reduce(needs_amounts.begin(), needs_amounts.end());
240 for(
const auto& commodity : world.commodities) {
241 if(needs_amounts[commodity] <= 0.f)
continue;
243 auto& product = province.
products[commodity];
244 const auto need_factor = needs_amounts[commodity] / total_factor;
245 auto amount = glm::min(budget_per_pop * need_factor / product.price, product.supply);
247 pop_need.life_needs_met += amount * need_factor;
248 const auto payment = product.buy(amount);
249 goods_payment[commodity] += payment;
252 for(
const auto& building_type : world.building_types)
253 province.
buildings[building_type].revenue.outputs += goods_payment[building_type.
output_id];
257 std::vector<Economy::Market> markets(world.commodities.size());
258 for(
const auto& commodity : world.commodities)
259 markets[commodity].commodity = commodity.get_id();
261 for(auto& market : markets_range) {
262 market.price.resize(world.provinces.size());
263 market.supply.resize(world.provinces.size());
264 market.demand.resize(world.provinces.size());
265 market.global_demand = std::vector(world.provinces.size(), 0.f);
266 for(const auto& province : world.provinces) {
267 const auto& product = province.products[market.commodity];
268 market.demand[province] = product.demand;
269 market.price[province] = product.price;
270 market.supply[province] = product.supply;
279 for(auto& market : markets_range) {
280 for(const auto& province : world.provinces) {
281 const auto& product = province.products[market.commodity];
282 market.demand[province] = product.demand;
283 market.price[province] = product.price;
284 market.supply[province] = product.supply;
302 auto& trade = economy_state.
trade;
305 std::vector<float> values(world.provinces.size(), 0.f);
306 for(auto& market : markets_range) {
307 for(const auto province_id : trade.cost_eval) {
308 auto& province = world.provinces[province_id];
309 if(Nation::is_invalid(province.owner_id)) continue;
310 auto& nation = world.nations[province.owner_id];
311 auto sum_weightings = 0.f;
312 for(const auto other_province_id : nation.owned_provinces) {
313 auto price = market.price[other_province_id];
314 auto apparent_price = price + 0.01f * trade.trade_costs[province_id][other_province_id];
315 apparent_price += glm::epsilon<float>();
316 values[other_province_id] = market.supply[other_province_id] / (apparent_price * apparent_price);
317 sum_weightings += values[other_province_id];
319 if(sum_weightings == 0.f) continue;
320 for(const auto other_province_id : nation.owned_provinces) {
321 values[other_province_id] /= sum_weightings;
322 auto demand = market.demand[other_province_id];
323 market.global_demand[province_id] += demand * values[other_province_id];
326 for(const auto province_id : trade.cost_eval) {
327 const auto price_change_speed = 0.9f;
328 const auto price_factor = market.global_demand[province_id] / (market.supply[province_id] + 0.01f);
329 const auto new_price = market.price[province_id] * (1.f - price_change_speed) + (market.price[province_id] * price_factor) * price_change_speed;
331 auto& province = world.provinces[province_id];
332 if(Nation::is_invalid(province.owner_id)) continue;
333 auto& product = province.products[market.commodity];
335 product.global_demand = market.global_demand[province_id];
344 std::vector<std::vector<float>> buildings_new_worker(world.provinces.size());
345 std::vector<std::vector<PopNeed>> pops_new_needs(world.provinces.size());
347 tbb::parallel_for(
static_cast<size_t>(0), world.provinces.size(), [&world, &buildings_new_worker, &province_new_units, &pops_new_needs, &paid_taxes](
const auto province_id) {
348 auto& province = world.provinces[province_id];
349 if(Nation::is_invalid(province.controller_id)) return;
350 for(auto& product : province.products)
351 product.close_market();
353 auto& new_needs = pops_new_needs[province_id];
354 new_needs.assign(province.pops.size(), PopNeed{});
356 auto laborers_payment = 0.f, artisans_payment = 0.f, state_payment = 0.f, private_payment = 0.f, bureaucrats_payment = 0.f;
362 auto bureaucracy_pts = bureaucrats_amount
366 for(
auto& building_type : world.building_types) {
367 auto& building = province.
buildings[building_type];
369 update_industry_production(world, building, building_type, province, artisans_amount, artisans_payment);
372 for(
size_t i = 0; i < province.
pops.size(); i++) {
373 const auto& pop = province.
pops[i];
374 new_needs[i].budget = pop.budget;
375 new_needs[i].life_needs_met = glm::clamp(pop.life_needs_met - 0.1f, -1.f, 1.f);
381 auto& new_workers = buildings_new_worker[province_id];
382 new_workers.assign(world.building_types.size(), 0.f);
383 update_factories_employment(world, province, new_workers);
384 for(
auto& building_type : world.building_types) {
385 auto& building = province.
buildings[building_type];
386 update_industry_accounting(world, building, building_type, province, laborers_payment, state_payment, private_payment);
396 paid_taxes.
local().resize(world.nations.size());
398 for(
auto& building : province.
buildings) {
402 const auto final_size = glm::min(pop.size, 100.f);
412 std::vector<Nation*> eval_nations;
413 for(
auto& nation : world.nations)
415 eval_nations.push_back(&nation);
420 tbb::parallel_for(
static_cast<size_t>(0), world.provinces.size(), [&world, &pops_new_needs, &buildings_new_worker](
const auto province_id) {
421 auto& province = world.provinces[province_id];
422 if(Nation::is_invalid(province.controller_id)) return;
423 const auto& new_needs = pops_new_needs[province_id];
424 for(size_t i = 0; i < province.pops.size(); i++) {
425 auto& pop = province.pops[i];
426 pop.budget = new_needs[i].budget;
427 pop.life_needs_met = new_needs[i].life_needs_met;
428 const auto growth = glm::clamp(pop.size * pop.life_needs_met * 0.1f, -100.f, 100.f);
429 pop.size = glm::max(pop.size + growth, 1.f);
430 pop.militancy = glm::clamp(pop.militancy + 0.01f * -pop.life_needs_met, 0.f, 1.f);
432 const auto& new_workers = buildings_new_worker[province_id];
433 for(
size_t i = 0; i < province.
buildings.size(); i++)
434 province.
buildings[i].workers = new_workers[i];
437 province_new_units.
combine_each([&world](
auto& new_unit_list) {
438 for(
auto& new_unit : new_unit_list) {
439 const auto& province = world.provinces[new_unit.province_id];
443 unit.type_id = new_unit.type_id;
444 unit.size = new_unit.size;
445 unit.base = world.unit_types[unit.type_id].max_health;
446 unit.set_owner(nation);
453 paid_taxes.
combine_each([&world](
auto& paid_taxes_list) {
454 for(
auto& nation : world.nations)
455 nation.budget += paid_taxes_list[nation];
A single province, which is used to simulate economy in a "bulk-tiles" way instead of doing economica...
float average_militancy() const
std::array< Pop, 7 > pops
std::vector< Product > products
std::vector< Building > buildings
Roughly a batallion, consisting of approximately 500 soldiers each.
void add_unit(Unit unit, ProvinceId unit_current_province)
void update_pop_needs(World &world, Province &province, std::vector< PopNeed > &pop_needs, float &state_payment)
Calculate the budget that we spend on each needs.
void update_markets(const World &world, std::vector< Economy::Market > markets)
std::vector< Economy::Market > init_markets(const World &world)
constexpr auto scale_speed(auto c, auto target)
void do_tick(World &world, EconomyState &economy_state)
void debug(const std::string_view category, const std::string_view msg)
std::string string_format(const std::string_view format, Args &&... args)
String formatter.
void parallel_for(T range, F &&func)
void invest(float amount)
float get_ownership(float total_investments) const
float get_dividends(float profit, float ownership) const
A military outpost, on land serves as a "spawn" place for units When adjacent to a water tile this se...
bool is_working_on_unit() const
struct Building::@1 expenses
Investment estate_private
float get_upgrade_cost() const
float get_output_amount() const
bool can_build_unit() const
struct Building::@0 revenue
float get_max_output_amount(float max_workers) const
void stop_working_on_unit()
UnitTypeId working_unit_type_id
Investment estate_collective
bool can_do_output(const Province &province, const std::vector< CommodityId > &inputs) const
Checks if the building can produce output (if it has enough input)
float get_total_investment() const
Type for military outposts.
std::vector< std::pair< CommodityId, float > > req_goods
std::vector< CommodityId > input_ids
std::vector< Market > commodity_market
void recalculate(const World &world)
void stop(const std::string &name)
void start(const std::string &name)
const char * c_str() const
constexpr bool is_invalid() const
Checks if the current id is invalid.
NewUnit(UnitTypeId _type_id, float _size, ProvinceId _province_id, PopTypeId _pop_id)
Eng3D::StringRef ref_name
void combine_each(F &&func)