Project Alice
Loading...
Searching...
No Matches
ai.cpp
Go to the documentation of this file.
1#include "ai.hpp"
2#include "system_state.hpp"
3#include "demographics.hpp"
4#include "effects.hpp"
6#include "math_fns.hpp"
7#include "military.hpp"
8#include "politics.hpp"
9#include "prng.hpp"
11#include "triggers.hpp"
12
13namespace ai {
14
17};
18
19float estimate_strength(sys::state& state, dcon::nation_id n) {
20 float value = state.world.nation_get_military_score(n) * state.defines.alice_ai_strength_estimation_military_industrial_balance;
21 value += state.world.nation_get_industrial_score(n) * (1.f - state.defines.alice_ai_strength_estimation_military_industrial_balance);
22 for(auto subj : state.world.nation_get_overlord_as_ruler(n)) {
23 value += subj.get_subject().get_military_score() * state.defines.alice_ai_strength_estimation_military_industrial_balance;
24 value += subj.get_subject().get_industrial_score() * (1.f - state.defines.alice_ai_strength_estimation_military_industrial_balance);
25 }
26 return value;
27}
28
29float estimate_defensive_strength(sys::state& state, dcon::nation_id n) {
30 float value = estimate_strength(state, n);
31 for(auto dr : state.world.nation_get_diplomatic_relation(n)) {
32 if(!dr.get_are_allied())
33 continue;
34
35 auto other = dr.get_related_nations(0) != n ? dr.get_related_nations(0) : dr.get_related_nations(1);
36 if(other.get_overlord_as_subject().get_ruler() != n)
37 value += estimate_strength(state, other);
38 }
39 if(auto sl = state.world.nation_get_in_sphere_of(n); sl)
40 value += estimate_strength(state, sl);
41 return value;
42}
43
44float estimate_additional_offensive_strength(sys::state& state, dcon::nation_id n, dcon::nation_id target) {
45 float value = 0.f;
46 for(auto dr : state.world.nation_get_diplomatic_relation(n)) {
47 if(!dr.get_are_allied())
48 continue;
49
50 auto other = dr.get_related_nations(0) != n ? dr.get_related_nations(0) : dr.get_related_nations(1);
51 if(other.get_overlord_as_subject().get_ruler() != n && military::can_use_cb_against(state, other, target) && !military::has_truce_with(state, other, target))
52 value += estimate_strength(state, other);
53 }
54 return value * state.defines.alice_ai_offensive_strength_overestimate;
55}
56
57/* Update AI threats and rivals */
59 for(auto n : state.world.in_nation) {
60 if(state.world.nation_get_owned_province_count(n) == 0) {
61 state.world.nation_set_ai_is_threatened(n, false);
62 state.world.nation_set_ai_rival(n, dcon::nation_id{});
63 continue;
64 }
65
66 auto ll = state.world.nation_get_last_war_loss(n);
67 float safety_factor = 1.2f;
68 if(ll && state.current_date < ll + 365 * 4) {
69 safety_factor = 1.8f;
70 }
71 auto in_sphere_of = state.world.nation_get_in_sphere_of(n);
72
73 float greatest_neighbor = 0.0f;
74 for(auto b : state.world.nation_get_nation_adjacency_as_connected_nations(n)) {
75 auto other = b.get_connected_nations(0) != n ? b.get_connected_nations(0) : b.get_connected_nations(1);
76 if(!nations::are_allied(state, n, other) && (!in_sphere_of || in_sphere_of != other.get_in_sphere_of())) {
77 greatest_neighbor = std::max(greatest_neighbor, estimate_strength(state, other));
78 }
79 }
80
81 float self_str = float(state.world.nation_get_military_score(n));
82 for(auto subj : n.get_overlord_as_ruler())
83 self_str += 0.75f * float(subj.get_subject().get_military_score());
84 float defensive_str = estimate_defensive_strength(state, n);
85
86 bool threatened = defensive_str < safety_factor * greatest_neighbor;
87 state.world.nation_set_ai_is_threatened(n, threatened);
88
89 if(!n.get_ai_rival()) {
90 float min_str = 0.0f;
91 dcon::nation_id potential;
92 for(auto adj : n.get_nation_adjacency()) {
93 auto other = adj.get_connected_nations(0) != n ? adj.get_connected_nations(0) : adj.get_connected_nations(1);
94 auto ol = other.get_overlord_as_subject().get_ruler();
95 if(!ol && other.get_in_sphere_of() != n && (!threatened || !nations::are_allied(state, n, other))) {
96 auto other_str = estimate_strength(state, other);
97 if(self_str * 0.5f < other_str && other_str <= self_str * 1.5f && min_str > self_str) {
98 min_str = other_str;
99 potential = other;
100 }
101 }
102 }
103
104 if(potential) {
105 if(!n.get_is_player_controlled() && nations::are_allied(state, n, potential)) {
106 assert(command::can_cancel_alliance(state, n, potential));
107 command::execute_cancel_alliance(state, n, potential);
108 }
109 n.set_ai_rival(potential);
110 }
111 } else {
112 auto rival_str = estimate_strength(state, n.get_ai_rival());
113 auto ol = n.get_ai_rival().get_overlord_as_subject().get_ruler();
114 if(ol || n.get_ai_rival().get_in_sphere_of() == n || rival_str * 2 < self_str || self_str * 2 < rival_str) {
115 n.set_ai_rival(dcon::nation_id{});
116 }
117 }
118 }
119}
120
121static void internal_get_alliance_targets_by_adjacency(sys::state& state, dcon::nation_id n, dcon::nation_id adj, std::vector<dcon::nation_id>& alliance_targets) {
122 for(auto nb : state.world.nation_get_nation_adjacency(adj)) {
123 auto other = nb.get_connected_nations(0) != adj ? nb.get_connected_nations(0) : nb.get_connected_nations(1);
124
125 bool b = other.get_is_player_controlled()
126 ? state.world.unilateral_relationship_get_interested_in_alliance(state.world.get_unilateral_relationship_by_unilateral_pair(n, other))
127 : ai_will_accept_alliance(state, other, n);
128 if(other != n && !(other.get_overlord_as_subject().get_ruler()) && !nations::are_allied(state, n, other) && !military::are_at_war(state, other, n) && b) {
129 alliance_targets.push_back(other.id);
130 }
131 }
132}
133static void internal_get_alliance_targets(sys::state& state, dcon::nation_id n, std::vector<dcon::nation_id>& alliance_targets) {
134 // Adjacency with us
135 internal_get_alliance_targets_by_adjacency(state, n, n, alliance_targets);
136 if(!alliance_targets.empty())
137 return;
138
139 // Adjacency with rival (useful for e.x, Chile allying Paraguay to fight bolivia)
140 if(auto rival = state.world.nation_get_ai_rival(n); bool(rival)) {
141 internal_get_alliance_targets_by_adjacency(state, n, rival, alliance_targets);
142 if(!alliance_targets.empty())
143 return;
144 }
145
146 // Adjacency with people who are at war with us
147 for(auto wp : state.world.nation_get_war_participant(n)) {
148 for(auto p : state.world.war_get_war_participant(wp.get_war())) {
149 if(p.get_is_attacker() == !wp.get_is_attacker()) {
150 internal_get_alliance_targets_by_adjacency(state, n, p.get_nation(), alliance_targets);
151 if(!alliance_targets.empty())
152 return;
153 }
154 }
155 }
156}
157
159 static std::vector<dcon::nation_id> alliance_targets;
160 for(auto n : state.world.in_nation) {
161 if(!n.get_is_player_controlled() && n.get_ai_is_threatened() && !(n.get_overlord_as_subject().get_ruler())) {
162 alliance_targets.clear();
163 internal_get_alliance_targets(state, n, alliance_targets);
164 if(!alliance_targets.empty()) {
165 std::sort(alliance_targets.begin(), alliance_targets.end(), [&](dcon::nation_id a, dcon::nation_id b) {
166 if(estimate_strength(state, a) != estimate_strength(state, b))
167 return estimate_strength(state, a) > estimate_strength(state, b);
168 else
169 return a.index() > b.index();
170 });
171 if(state.world.nation_get_is_player_controlled(alliance_targets[0])) {
173 [source = n](sys::state& state, text::layout_base& contents) {
174 text::add_line(state, contents, "msg_entered_automatic_alliance_1", text::variable_type::x, source);
175 },
176 "msg_entered_automatic_alliance_title",
177 n, dcon::nation_id{}, dcon::nation_id{},
179 });
180 }
181 nations::make_alliance(state, n, alliance_targets[0]);
182 }
183 }
184 }
185}
186
188 static std::vector<dcon::nation_id> prune_targets;
189 for(auto n : state.world.in_nation) {
190 if(!n.get_is_player_controlled()
191 && !n.get_ai_is_threatened()
192 && !(n.get_overlord_as_subject().get_ruler())) {
193 prune_targets.clear();
194 for(auto dr : n.get_diplomatic_relation()) {
195 if(dr.get_are_allied()) {
196 auto other = dr.get_related_nations(0) != n ? dr.get_related_nations(0) : dr.get_related_nations(1);
197 if(other.get_in_sphere_of() != n
198 && !military::are_allied_in_war(state, n, other)) {
199 prune_targets.push_back(other);
200 }
201 }
202 }
203
204 if(prune_targets.empty())
205 continue;
206
207 std::sort(prune_targets.begin(), prune_targets.end(), [&](dcon::nation_id a, dcon::nation_id b) {
208 if(estimate_strength(state, a) != estimate_strength(state, b))
209 return estimate_strength(state, a) < estimate_strength(state, b);
210 else
211 return a.index() > b.index();
212 });
213
214 float greatest_neighbor = 0.0f;
215 auto in_sphere_of = state.world.nation_get_in_sphere_of(n);
216
217 for(auto b : state.world.nation_get_nation_adjacency_as_connected_nations(n)) {
218 auto other = b.get_connected_nations(0) != n ? b.get_connected_nations(0) : b.get_connected_nations(1);
219 if(!nations::are_allied(state, n, other) && (!in_sphere_of || in_sphere_of != other.get_in_sphere_of())) {
220 greatest_neighbor = std::max(greatest_neighbor, estimate_strength(state, other));
221 }
222 }
223
224 float defensive_str = estimate_defensive_strength(state, n);
225 auto ll = state.world.nation_get_last_war_loss(n);
226 float safety_factor = 1.2f;
227 if(ll && state.current_date < ll + 365 * 4) {
228 safety_factor = 1.8f;
229 }
230
231 auto safety_margin = defensive_str - safety_factor * greatest_neighbor;
232
233 for(auto pt : prune_targets) {
234 auto weakest_str = estimate_strength(state, pt);
235 if(weakest_str * 1.25 < safety_margin) {
236 safety_margin -= weakest_str;
237 assert(command::can_cancel_alliance(state, n, pt, true));
239 } else if(state.world.nation_get_infamy(pt) >= state.defines.badboy_limit) {
240 safety_margin -= weakest_str;
241 assert(command::can_cancel_alliance(state, n, pt, true));
243 } else {
244 break;
245 }
246 }
247 }
248 }
249}
250
251bool ai_is_close_enough(sys::state& state, dcon::nation_id target, dcon::nation_id from) {
252 auto target_continent = state.world.province_get_continent(state.world.nation_get_capital(target));
253 auto source_continent = state.world.province_get_continent(state.world.nation_get_capital(from));
254 return (target_continent == source_continent) || bool(state.world.get_nation_adjacency_by_nation_adjacency_pair(target, from));
255}
256
257static bool ai_has_mutual_enemy(sys::state& state, dcon::nation_id from, dcon::nation_id target) {
258 auto rival_a = state.world.nation_get_ai_rival(target);
259 auto rival_b = state.world.nation_get_ai_rival(from);
260 // Same rival equates to instantaneous alliance (we benefit from more allies against a common enemy)
261 if(rival_a && rival_a == rival_b)
262 return true;
263 // // Our rivals are allied?
264 // if(rival_a && rival_b && rival_a != rival_b && nations::are_allied(state, rival_a, rival_b))
265 // return true;
266
267 // // One of the allies of our rivals can be declared on?
268 // for(auto n : state.world.in_nation) {
269 // if(n.id != target && n.id != from && n.id != rival_a && n.id != rival_b) {
270 // if(nations::are_allied(state, rival_a, n.id) || nations::are_allied(state, rival_b, n.id)) {
271 // bool enemy_a = military::can_use_cb_against(state, from, n.id);
272 // bool enemy_b = military::can_use_cb_against(state, target, n.id);
273 // if(enemy_a || enemy_b)
274 // return true;
275 // }
276 // }
277 // }
278 return false;
279}
280
281constexpr inline float ally_overestimate = 2.f;
282
283bool ai_will_accept_alliance(sys::state& state, dcon::nation_id target, dcon::nation_id from) {
284 if(!state.world.nation_get_ai_is_threatened(target))
285 return false;
286
287 // Has not surpassed infamy limit
288 if(state.world.nation_get_infamy(target) >= state.defines.badboy_limit * 0.75f)
289 return false;
290
291 // Won't ally our rivals
292 if(state.world.nation_get_ai_rival(target) == from || state.world.nation_get_ai_rival(from) == target)
293 return false;
294
295 // No more than 2 GP allies
296 // No more than 4 alliances
297 if(bool(state.defines.alice_artificial_gp_limitant) && state.world.nation_get_is_great_power(target)) {
298 int32_t gp_count = 0;
299 for(const auto rel : state.world.nation_get_diplomatic_relation(from)) {
300 auto n = rel.get_related_nations(rel.get_related_nations(0) == from ? 1 : 0);
301 if(rel.get_are_allied() && n.get_is_great_power()) {
302 if(gp_count >= 2) {
303 return false;
304 }
305 ++gp_count;
306 }
307 }
308 }
309 if(bool(state.defines.alice_spherelings_only_ally_sphere)) {
310 auto spherelord = state.world.nation_get_in_sphere_of(from);
311 //If no spherelord -> Then must not ally spherelings
312 //If spherelord -> Then must not ally non-spherelings
313 if(state.world.nation_get_in_sphere_of(target) != spherelord && target != spherelord)
314 return false;
315 if(target == spherelord)
316 return true; //always ally spherelord
317 }
318
319 if(ai_has_mutual_enemy(state, from, target))
320 return true;
321
322 // Otherwise we may consider alliances only iff they are close to our continent or we are adjacent
323 if(!ai_is_close_enough(state, target, from))
324 return false;
325
326 // And also if they're powerful enough to be considered for an alliance
327 auto target_score = estimate_strength(state, target);
328 auto source_score = estimate_strength(state, from);
329 return std::max<float>(source_score, 1.f) * ally_overestimate >= target_score;
330}
331
332void explain_ai_alliance_reasons(sys::state& state, dcon::nation_id target, text::layout_base& contents, int32_t indent) {
333
334 text::add_line_with_condition(state, contents, "ai_alliance_1", state.world.nation_get_ai_is_threatened(target), indent);
335
336 text::add_line(state, contents, "kierkegaard_1", indent);
337
338 text::add_line_with_condition(state, contents, "ai_alliance_5", ai_has_mutual_enemy(state, state.local_player_nation, target), indent + 15);
339
340 text::add_line(state, contents, "kierkegaard_2", indent);
341
342 text::add_line_with_condition(state, contents, "ai_alliance_2", ai_is_close_enough(state, target, state.local_player_nation), indent + 15);
343
344 text::add_line_with_condition(state, contents, "ai_alliance_3", state.world.nation_get_ai_rival(target) != state.local_player_nation && state.world.nation_get_ai_rival(state.local_player_nation) != target, indent + 15);
345
346 auto target_score = estimate_strength(state, target);
347 auto source_score = estimate_strength(state, state.local_player_nation);
348 text::add_line_with_condition(state, contents, "ai_alliance_4", std::max<float>(source_score, 1.f) * ally_overestimate >= target_score, indent + 15);
349}
350
351bool ai_will_grant_access(sys::state& state, dcon::nation_id target, dcon::nation_id from) {
352 if(!state.world.nation_get_is_at_war(from))
353 return false;
354 if(state.world.nation_get_ai_rival(target) == from)
355 return false;
356 if(military::are_at_war(state, from, state.world.nation_get_ai_rival(target)))
357 return true;
358
359 for(auto wa : state.world.nation_get_war_participant(target)) {
360 auto is_attacker = wa.get_is_attacker();
361 for(auto o : wa.get_war().get_war_participant()) {
362 if(o.get_is_attacker() != is_attacker) {
363 if(military::are_at_war(state, o.get_nation(), from))
364 return true;
365 }
366 }
367 }
368 return false;
369
370}
371void explain_ai_access_reasons(sys::state& state, dcon::nation_id target, text::layout_base& contents, int32_t indent) {
372 text::add_line_with_condition(state, contents, "ai_access_1", ai_will_grant_access(state, target, state.local_player_nation), indent);
373}
374
376 auto ymd_date = state.current_date.to_ymd(state.start_date);
377 auto year = uint32_t(ymd_date.year);
378 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t id) {
379 dcon::nation_id n{ dcon::nation_id::value_base_t(id) };
380
381 if(state.world.nation_get_is_player_controlled(n)
382 || state.world.nation_get_current_research(n)
383 || !state.world.nation_get_is_civilized(n)
384 || state.world.nation_get_owned_province_count(n) == 0) {
385
386 //skip -- does not need new research
387 return;
388 }
389
390 struct potential_techs {
391 dcon::technology_id id;
392 float weight = 0.0f;
393 };
394
395 std::vector<potential_techs> potential;
396
397 for(auto tid : state.world.in_technology) {
398 if(state.world.nation_get_active_technologies(n, tid))
399 continue; // Already researched
400
401 if(state.current_date.to_ymd(state.start_date).year >= state.world.technology_get_year(tid)) {
402 // Find previous technology before this one
403 dcon::technology_id prev_tech = dcon::technology_id(dcon::technology_id::value_base_t(tid.id.index() - 1));
404 // Previous technology is from the same folder so we have to check that we have researched it beforehand
405 if(tid.id.index() != 0 && state.world.technology_get_folder_index(prev_tech) == state.world.technology_get_folder_index(tid)) {
406 // Only allow if all previously researched techs are researched
407 if(state.world.nation_get_active_technologies(n, prev_tech))
408 potential.push_back(potential_techs{ tid, 0.0f });
409 } else { // first tech in folder
410 potential.push_back(potential_techs{ tid, 0.0f });
411 }
412 }
413 }
414
415 for(auto& pt : potential) { // weight techs
416 auto base = state.world.technology_get_ai_weight(pt.id);
417 if(state.world.nation_get_ai_is_threatened(n) && state.culture_definitions.tech_folders[state.world.technology_get_folder_index(pt.id)].category == culture::tech_category::army) {
418 base *= 2.0f;
419 }
420
421 if(state.world.nation_get_ai_strategy(n) == ai_strategies::militant && state.culture_definitions.tech_folders[state.world.technology_get_folder_index(pt.id)].category == culture::tech_category::army) {
422 base *= 2.0f;
423 }
424 if(state.world.nation_get_ai_strategy(n) == ai_strategies::industrious && state.culture_definitions.tech_folders[state.world.technology_get_folder_index(pt.id)].category == culture::tech_category::industry) {
425 base *= 2.0f;
426 }
427 if(state.world.nation_get_ai_strategy(n) == ai_strategies::industrious && state.culture_definitions.tech_folders[state.world.technology_get_folder_index(pt.id)].category == culture::tech_category::commerce) {
428 base *= 2.0f;
429 }
430 if(state.world.nation_get_ai_strategy(n) == ai_strategies::technological && state.culture_definitions.tech_folders[state.world.technology_get_folder_index(pt.id)].category == culture::tech_category::culture) {
431 base *= 2.0f;
432 }
433
434 auto cost = std::max(1.0f, culture::effective_technology_cost(state, year, n, pt.id));
435 pt.weight = base / cost;
436 }
437 auto rval = rng::get_random(state, id);
438 std::sort(potential.begin(), potential.end(), [&](potential_techs& a, potential_techs& b) {
439 if(a.weight != b.weight)
440 return a.weight > b.weight;
441 else // sort semi randomly
442 return (a.id.index() ^ rval) > (b.id.index() ^ rval);
443 });
444
445 if(!potential.empty()) {
446 state.world.nation_set_current_research(n, potential[0].id);
447 }
448 });
449}
450
452 for(auto t : state.world.in_technology) {
453 float base = 1000.0f;
454 if(state.culture_definitions.tech_folders[t.get_folder_index()].category == culture::tech_category::army)
455 base *= 3.0f;
456
457 if(t.get_increase_building(economy::province_building_type::naval_base))
458 base *= 4.5f;
459 else if(state.culture_definitions.tech_folders[t.get_folder_index()].category == culture::tech_category::navy)
460 base *= 0.9f;
461
462 auto mod = t.get_modifier();
463 auto& vals = mod.get_national_values();
465 if(vals.offsets[i] == sys::national_mod_offsets::research_points) {
466 base *= 6.0f;
467 } else if(vals.offsets[i] == sys::national_mod_offsets::research_points_modifier) {
468 base *= 6.0f;
469 } else if(vals.offsets[i] == sys::national_mod_offsets::education_efficiency) {
470 base *= 3.0f;
471 } else if(vals.offsets[i] == sys::national_mod_offsets::education_efficiency_modifier) {
472 base *= 3.0f;
473 } else if(vals.offsets[i] == sys::national_mod_offsets::pop_growth) {
474 base *= 5.0f;
475 } else if(vals.offsets[i] == sys::national_mod_offsets::max_national_focus) {
476 base *= 4.0f;
477 } else if(vals.offsets[i] == sys::national_mod_offsets::colonial_life_rating) {
478 base *= 4.0f;
479 } else if(vals.offsets[i] == sys::national_mod_offsets::rgo_output) {
480 base *= 1.2f;
481 } else if(vals.offsets[i] == sys::national_mod_offsets::factory_output) {
482 base *= 3.0f;
483 } else if(vals.offsets[i] == sys::national_mod_offsets::factory_throughput) {
484 base *= 5.0f;
485 } else if(vals.offsets[i] == sys::national_mod_offsets::factory_input) {
486 base *= 3.0f;
487 } else if(vals.offsets[i] == sys::national_mod_offsets::tax_efficiency) {
488 base *= 3.0f;
489 }
490 }
491
492 t.set_ai_weight(base);
493 }
494}
495
497 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t id) {
498 dcon::nation_id n{ dcon::nation_id::value_base_t(id) };
499
500 if(state.world.nation_get_owned_province_count(n) == 0) {
501 // skip -- do not need experience
502 return;
503 }
504 // private research
505
506 std::vector<float> employment;
507 employment.resize(state.world.factory_type_size());
508
509 state.world.nation_for_each_province_ownership_as_nation(n, [&](dcon::province_ownership_id province_ownership) {
510 auto province = state.world.province_ownership_get_province(province_ownership);
511 auto sid = state.world.province_get_state_membership(province);
512 auto market = state.world.state_instance_get_market_from_local_market(sid);
513 state.world.province_for_each_factory_location_as_province(province, [&](auto location) {
514 auto factory = state.world.factory_location_get_factory(location);
515 auto type = state.world.factory_get_building_type(factory);
516 auto level = state.world.factory_get_level(factory);
517 employment[type.id.index()] +=
518 state.world.factory_get_primary_employment(factory)
519 * level
520 * economy::factory_type_input_cost(
521 state, n, market, type
522 );
523 });
524 });
525
526 state.world.for_each_factory_type([&](dcon::factory_type_id factory_type_id) {
527
528 if(
529 !state.world.nation_get_active_building(n, factory_type_id)
530 && !state.world.factory_type_get_is_available_from_start(factory_type_id)
531 ) {
532 state.world.nation_set_factory_type_experience_priority_private(n, factory_type_id, 0.f);
533 return;
534 }
535
536 // capitalists want to minimise costs related to current factories
537 // so they choose factory types
538 // according to:
539 // 1) current amount of workers in factories of a given type
540 // 2) costs of inputs
541
542 auto costs = employment[factory_type_id.index()];
543 state.world.nation_set_factory_type_experience_priority_private(n, factory_type_id, costs);
544 });
545
546 if(state.world.nation_get_is_player_controlled(n)) {
547 // skip -- do not need AI
548 return;
549 }
550 // national research
551
552 std::vector<float> supply;
553 supply.resize(state.world.commodity_size());
554
555 state.world.for_each_commodity([&](dcon::commodity_id cid) {
556 supply[cid.index()] = economy::supply(state, n, cid);
557 });
558
559 state.world.for_each_factory_type([&](dcon::factory_type_id factory_type_id) {
560 if(
561 !state.world.nation_get_active_building(n, factory_type_id)
562 && !state.world.factory_type_get_is_available_from_start(factory_type_id)
563 ) {
564 state.world.nation_set_factory_type_experience_priority_private(n, factory_type_id, 0.f);
565 return;
566 }
567
568 // auto priority = 0.5f + float(rng::get_random(state, uint32_t(id) ^ uint32_t(factory_type_id)) & 0xFFFF) / float(0xFFFF);
569
570 auto& inputs = state.world.factory_type_get_inputs(factory_type_id);
571
572 auto min_effective_supply = std::numeric_limits<float>::infinity();
573 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
574 auto input = inputs.commodity_type[i];
575 if(input) {
576 min_effective_supply = std::min(supply[input.index()] / inputs.commodity_amounts[i], min_effective_supply);
577 } else {
578 break;
579 }
580 }
581
582 //check if there are "rivals" which would push you away from the industry
583 auto local_price = economy::price(state, n, state.world.factory_type_get_output(factory_type_id));
584 auto rival_price = local_price * 2.f;
585 for(auto adj : state.world.nation_get_nation_adjacency(n)) {
586 auto other = adj.get_connected_nations(0) != n ? adj.get_connected_nations(0) : adj.get_connected_nations(1);
587 rival_price = std::min(rival_price, economy::price(state, other, state.world.factory_type_get_output(factory_type_id)));
588 }
589
590 auto rival_modifier = (rival_price + 0.01f) / (local_price + 0.01f);
591
592 // we want this modifier to be really strong
593 rival_modifier = (rival_modifier * rival_modifier) * (rival_modifier * rival_modifier) * rival_modifier;
594
595 state.world.nation_set_factory_type_experience_priority_national(n, factory_type_id, min_effective_supply * rival_modifier);
596 });
597 });
598}
599
601 struct weighted_nation {
602 dcon::nation_id id;
603 float weight = 0.0f;
604 };
605 static std::vector<weighted_nation> targets;
606
607 for(auto gprl : state.world.in_gp_relationship) {
608 if(gprl.get_great_power().get_is_player_controlled()) {
609 // nothing -- player GP
610 } else {
611 auto& status = gprl.get_status();
612 status &= ~nations::influence::priority_mask;
615 }
616 }
617 }
618
619 for(auto& n : state.great_nations) {
620 if(state.world.nation_get_is_player_controlled(n.nation))
621 continue;
622
623 targets.clear();
624 for(auto t : state.world.in_nation) {
625 if(t.get_is_great_power())
626 continue;
627 if(t.get_owned_province_count() == 0)
628 continue;
629 if(t.get_in_sphere_of() == n.nation)
630 continue;
631 if(t.get_demographics(demographics::total) > state.defines.large_population_limit)
632 continue;
633
634 float weight = 0.0f;
635
636 for(auto c : state.world.in_commodity) {
637 if(auto d = economy::demand(state, n.nation, c); d > 0.001f) {
638 auto cweight = std::min(
639 1.0f,
640 economy::supply(state, n.nation, c) / d)
641 * (1.0f - economy::demand_satisfaction(state, n.nation, c));
642 weight += cweight;
643 }
644 }
645
646 //We probably don't want to fight a forever lasting sphere war, let's find some other uncontested nations
647 if(t.get_in_sphere_of()) {
648 weight /= 4.0f;
649 }
650
651 //Prioritize primary culture before culture groups; should ensure Prussia spheres all of the NGF first before trying to contest Austria
652 if(t.get_primary_culture() == state.world.nation_get_primary_culture(n.nation)) {
653 weight += 1.0f;
654 weight *= 4000.0f;
655 }
656
657 else if(t.get_primary_culture().get_group_from_culture_group_membership() == state.world.nation_get_primary_culture(n.nation).get_group_from_culture_group_membership()) {
658 weight *= 4.0f;
659 }
660 //Focus on gaining influence against nations we have active wargoals against so we can remove their protector, even if it's us
661 if(military::can_use_cb_against(state, n.nation, t) && t.get_in_sphere_of()) {
662 weight += 1.0f;
663 weight *= 1000.0f;
664 }
665 //If it doesn't neighbor us or a friendly sphere and isn't coastal, please don't sphere it, we don't want sphere gore
666 bool is_reachable = false;
667 for(auto adj : state.world.nation_get_nation_adjacency(t)) {
668 auto casted_adj = adj.get_connected_nations(0) != t ? adj.get_connected_nations(0) : adj.get_connected_nations(1);
669 if(casted_adj == n.nation) {
670 is_reachable = true;
671 break;
672 }
673 if(casted_adj.get_in_sphere_of() == n.nation) {
674 is_reachable = true;
675 break;
676 }
677 };
678
679 //Is coastal? Technically reachable
680 if(state.world.nation_get_central_ports(t) > 0) {
681 is_reachable = true;
682 }
683
684 //Prefer neighbors
685 if(state.world.get_nation_adjacency_by_nation_adjacency_pair(n.nation, t.id)) {
686 weight *= 10.0f;
687 is_reachable = true;
688 }
689
690 if(!is_reachable) {
691 weight *= 0.0f;
692 }
693
694 targets.push_back(weighted_nation{ t.id, weight });
695 }
696
697 std::sort(targets.begin(), targets.end(), [](weighted_nation const& a, weighted_nation const& b) {
698 if(a.weight != b.weight)
699 return a.weight > b.weight;
700 else
701 return a.id.index() < b.id.index();
702 });
703
704 uint32_t i = 0;
705 for(; i < 2 && i < targets.size(); ++i) {
706 auto rel = state.world.get_gp_relationship_by_gp_influence_pair(targets[i].id, n.nation);
707 if(!rel)
708 rel = state.world.force_create_gp_relationship(targets[i].id, n.nation);
709 state.world.gp_relationship_get_status(rel) |= nations::influence::priority_three;
710 }
711 for(; i < 4 && i < targets.size(); ++i) {
712 auto rel = state.world.get_gp_relationship_by_gp_influence_pair(targets[i].id, n.nation);
713 if(!rel)
714 rel = state.world.force_create_gp_relationship(targets[i].id, n.nation);
715 state.world.gp_relationship_get_status(rel) |= nations::influence::priority_two;
716 }
717 for(; i < 6 && i < targets.size(); ++i) {
718 auto rel = state.world.get_gp_relationship_by_gp_influence_pair(targets[i].id, n.nation);
719 if(!rel)
720 rel = state.world.force_create_gp_relationship(targets[i].id, n.nation);
721 state.world.gp_relationship_get_status(rel) |= nations::influence::priority_one;
722 }
723 }
724}
725
727 for(auto gprl : state.world.in_gp_relationship) {
728 if(gprl.get_great_power().get_is_player_controlled()) {
729 // nothing -- player GP
730 } else {
731 if((gprl.get_status() & nations::influence::is_banned) != 0)
732 continue; // can't do anything with a banned nation
733
734 if(military::are_at_war(state, gprl.get_great_power(), gprl.get_influence_target()))
735 continue; // can't do anything while at war
736
737 auto clevel = (nations::influence::level_mask & gprl.get_status());
739 continue; // already in sphere
740
741 auto current_sphere = gprl.get_influence_target().get_in_sphere_of();
742
743 if(state.defines.increaseopinion_influence_cost <= gprl.get_influence() && clevel != nations::influence::level_friendly) {
744 assert(command::can_increase_opinion(state, gprl.get_great_power(), gprl.get_influence_target()));
745 command::execute_increase_opinion(state, gprl.get_great_power(), gprl.get_influence_target());
746 } else if(state.defines.removefromsphere_influence_cost <= gprl.get_influence() && current_sphere /* && current_sphere != gprl.get_great_power()*/ && clevel == nations::influence::level_friendly) { // condition taken care of by check above
747 assert(command::can_remove_from_sphere(state, gprl.get_great_power(), gprl.get_influence_target(), gprl.get_influence_target().get_in_sphere_of()));
748 command::execute_remove_from_sphere(state, gprl.get_great_power(), gprl.get_influence_target(), gprl.get_influence_target().get_in_sphere_of());
749 } else if(state.defines.addtosphere_influence_cost <= gprl.get_influence() && !current_sphere && clevel == nations::influence::level_friendly) {
750 assert(command::can_add_to_sphere(state, gprl.get_great_power(), gprl.get_influence_target()));
751 command::execute_add_to_sphere(state, gprl.get_great_power(), gprl.get_influence_target());
752 //De-sphere countries we have wargoals against, desphering countries need to check for going over infamy
753 } else if(military::can_use_cb_against(state, gprl.get_great_power(), gprl.get_influence_target())
754 && state.defines.removefromsphere_influence_cost <= gprl.get_influence()
755 && current_sphere
757 && (state.world.nation_get_infamy(gprl.get_great_power()) + state.defines.removefromsphere_infamy_cost) < state.defines.badboy_limit
758 ) {
759 assert(command::can_remove_from_sphere(state, gprl.get_great_power(), gprl.get_influence_target(), gprl.get_influence_target().get_in_sphere_of()));
760 command::execute_remove_from_sphere(state, gprl.get_great_power(), gprl.get_influence_target(), gprl.get_influence_target().get_in_sphere_of());
761 }
762 }
763 }
764}
765
767 for(auto f : state.world.in_national_focus) {
768 if(f.get_promotion_amount() > 0) {
769 if(f.get_promotion_type() == state.culture_definitions.clergy)
770 state.national_definitions.clergy_focus = f;
771 if(f.get_promotion_type() == state.culture_definitions.soldiers)
772 state.national_definitions.soldier_focus = f;
773 if(f.get_promotion_type() == state.culture_definitions.aristocrat)
774 state.national_definitions.aristocrat_focus = f;
775 if(f.get_promotion_type() == state.culture_definitions.capitalists)
776 state.national_definitions.capitalist_focus = f;
777 if(f.get_promotion_type() == state.culture_definitions.primary_factory_worker)
778 state.national_definitions.primary_factory_worker_focus = f;
779 if(f.get_promotion_type() == state.culture_definitions.secondary_factory_worker)
780 state.national_definitions.secondary_factory_worker_focus = f;
781 }
782 }
783}
784
786 for(auto si : state.world.in_state_instance) {
787 if(!si.get_nation_from_state_ownership().get_is_player_controlled())
788 si.set_owner_focus(dcon::national_focus_id{});
789 }
790
791 for(auto n : state.world.in_nation) {
792 if(n.get_is_player_controlled())
793 continue;
794 if(n.get_owned_province_count() == 0)
795 continue;
796
797 n.set_state_from_flashpoint_focus(dcon::state_instance_id{});
798
799 auto num_focuses_total = nations::max_national_focuses(state, n);
800 if(num_focuses_total <= 0)
801 return;
802
803 auto base_opt = state.world.pop_type_get_research_optimum(state.culture_definitions.clergy);
804 auto clergy_frac = n.get_demographics(demographics::to_key(state, state.culture_definitions.clergy)) / n.get_demographics(demographics::total);
805 bool max_clergy = clergy_frac >= base_opt;
806
807 static std::vector<dcon::state_instance_id> ordered_states;
808 ordered_states.clear();
809 for(auto si : n.get_state_ownership()) {
810 ordered_states.push_back(si.get_state().id);
811 }
812 std::sort(ordered_states.begin(), ordered_states.end(), [&](auto a, auto b) {
813 auto apop = state.world.state_instance_get_demographics(a, demographics::total);
814 auto bpop = state.world.state_instance_get_demographics(b, demographics::total);
815 if(apop != bpop)
816 return apop > bpop;
817 else
818 return a.index() < b.index();
819 });
820 bool threatened = n.get_ai_is_threatened() || n.get_is_at_war();
821 for(uint32_t i = 0; num_focuses_total > 0 && i < ordered_states.size(); ++i) {
822 auto prov = state.world.state_instance_get_capital(ordered_states[i]);
823 if(max_clergy) {
824 if(threatened) {
825 auto nf = state.national_definitions.soldier_focus;
826 auto k = state.world.national_focus_get_limit(nf);
827 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
828 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
829 state.world.state_instance_set_owner_focus(ordered_states[i], state.national_definitions.soldier_focus);
830 --num_focuses_total;
831 }
832 } else {
833 auto total = state.world.state_instance_get_demographics(ordered_states[i], demographics::total);
834 auto cfrac = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_key(state, state.culture_definitions.clergy)) / total;
835 if(cfrac < state.defines.max_clergy_for_literacy * 0.8f && !state.world.province_get_is_colonial(state.world.state_instance_get_capital(ordered_states[i]))) {
836 auto nf = state.national_definitions.clergy_focus;
837 auto k = state.world.national_focus_get_limit(nf);
838 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
839 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
840 state.world.state_instance_set_owner_focus(ordered_states[i], state.national_definitions.clergy_focus);
841 --num_focuses_total;
842 }
843 }
844 }
845 } else {
846 // If we haven't maxxed out clergy on this state, then our number 1 priority is to maximize clergy
847 auto cfrac = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_key(state, state.culture_definitions.clergy)) / state.world.state_instance_get_demographics(ordered_states[i], demographics::total);
848 if(cfrac < base_opt * 1.2f && !state.world.province_get_is_colonial(state.world.state_instance_get_capital(ordered_states[i]))) {
849 auto nf = state.national_definitions.clergy_focus;
850 auto k = state.world.national_focus_get_limit(nf);
851 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
852 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
853 state.world.state_instance_set_owner_focus(ordered_states[i], state.national_definitions.clergy_focus);
854 --num_focuses_total;
855 }
856 }
857 }
858 }
859
860 for(uint32_t i = 0; num_focuses_total > 0 && i < ordered_states.size(); ++i) {
861 auto prov = state.world.state_instance_get_capital(ordered_states[i]);
862
863 if(state.world.province_get_is_colonial(state.world.state_instance_get_capital(ordered_states[i]))) {
864 continue;
865 }
866
867 auto total = state.world.state_instance_get_demographics(ordered_states[i], demographics::total);
868 auto pw_num = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_key(state, state.culture_definitions.primary_factory_worker));
869 auto pw_employed = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_employment_key(state, state.culture_definitions.primary_factory_worker));
870 auto sw_num = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_key(state, state.culture_definitions.secondary_factory_worker));
871 auto sw_employed = state.world.state_instance_get_demographics(ordered_states[i], demographics::to_employment_key(state, state.culture_definitions.secondary_factory_worker));
872 auto sw_frac = sw_num / std::max(pw_num + sw_num, 1.0f);
873 auto ideal_swfrac = (1.f - state.economy_definitions.craftsmen_fraction);
874 // Due to floating point comparison where 2.9999 != 3, we will round the number
875 // so that the ratio is NOT exact, but rather an aproximate
876 if((pw_employed >= pw_num || pw_num < 1.0f)) {
877 auto nf = state.national_definitions.primary_factory_worker_focus;
878 auto k = state.world.national_focus_get_limit(nf);
879 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
880 // Keep balance between ratio of factory workers
881 // we will only promote secondary workers if none are unemployed
882 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
883 state.world.state_instance_set_owner_focus(ordered_states[i], nf);
884 --num_focuses_total;
885 }
886 } else if(pw_num > 1.0f && pw_employed > 1.0f && int8_t(sw_frac * 100.f) != int8_t(ideal_swfrac * 100.f)) {
887 auto nf = state.national_definitions.secondary_factory_worker_focus;
888 auto k = state.world.national_focus_get_limit(nf);
889 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
890 // Keep balance between ratio of factory workers
891 // we will only promote primary workers if none are unemployed
892 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
893 state.world.state_instance_set_owner_focus(ordered_states[i], nf);
894 --num_focuses_total;
895 }
896 } else {
897 /* If we are a civilized nation, and we allow pops to operate on the economy
898 i.e Laissez faire, we WILL promote capitalists, since they will help to
899 build new factories for us */
900 auto rules = n.get_combined_issue_rules();
902 auto nf = state.national_definitions.capitalist_focus;
903 auto k = state.world.national_focus_get_limit(nf);
904 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
905 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
906 state.world.state_instance_set_owner_focus(ordered_states[i], nf);
907 --num_focuses_total;
908 }
909 } else {
910 auto nf = state.national_definitions.aristocrat_focus;
911 auto k = state.world.national_focus_get_limit(nf);
912 if(!k || trigger::evaluate(state, k, trigger::to_generic(prov), trigger::to_generic(n), -1)) {
913 assert(command::can_set_national_focus(state, n, ordered_states[i], nf));
914 state.world.state_instance_set_owner_focus(ordered_states[i], nf);
915 --num_focuses_total;
916 }
917 }
918 }
919 }
920 }
921}
922
924 using decision_nation_pair = std::pair<dcon::decision_id, dcon::nation_id>;
925 concurrency::combinable<std::vector<decision_nation_pair, dcon::cache_aligned_allocator<decision_nation_pair>>> decisions_taken;
926
927 // execute in staggered blocks
928 uint32_t d_block_size = state.world.decision_size() / 32;
929 uint32_t block_index = 0;
930 auto d_block_end = state.world.decision_size();
931 //uint32_t block_index = (state.current_date.value & 31);
932 //auto d_block_end = block_index == 31 ? state.world.decision_size() : d_block_size * (block_index + 1);
933 concurrency::parallel_for(d_block_size * block_index, d_block_end, [&](uint32_t i) {
934 auto d = dcon::decision_id{ dcon::decision_id::value_base_t(i) };
935 auto e = state.world.decision_get_effect(d);
936 if(e) {
937 auto potential = state.world.decision_get_potential(d);
938 auto allow = state.world.decision_get_allow(d);
939 auto ai_will_do = state.world.decision_get_ai_will_do(d);
940 ve::execute_serial_fast<dcon::nation_id>(state.world.nation_size(), [&](auto ids) {
941 // AI-only, not dead nations
942 ve::mask_vector filter_a = !state.world.nation_get_is_player_controlled(ids)
943 && state.world.nation_get_owned_province_count(ids) != 0;
944 if(ve::compress_mask(filter_a).v != 0) {
945 // empty allow assumed to be an "always = yes"
946 ve::mask_vector filter_b = potential
947 ? filter_a && (trigger::evaluate(state, potential, trigger::to_generic(ids), trigger::to_generic(ids), 0))
948 : filter_a;
949 if(ve::compress_mask(filter_b).v != 0) {
950 ve::mask_vector filter_c = allow
951 ? filter_b && (trigger::evaluate(state, allow, trigger::to_generic(ids), trigger::to_generic(ids), 0))
952 : filter_b;
953 if(ve::compress_mask(filter_c).v != 0) {
954 ve::mask_vector filter_d = ai_will_do
955 ? filter_c && (trigger::evaluate_multiplicative_modifier(state, ai_will_do, trigger::to_generic(ids), trigger::to_generic(ids), 0) > 0.0f)
956 : filter_c;
957 ve::apply([&](dcon::nation_id n, bool passed_filter) {
958 if(passed_filter) {
959 decisions_taken.local().push_back(decision_nation_pair(d, n));
960 }
961 }, ids, filter_d);
962 }
963 }
964 }
965 });
966 }
967 });
968 // combination and final execution
969 auto total_vector = decisions_taken.combine([](auto& a, auto& b) {
970 std::vector<decision_nation_pair, dcon::cache_aligned_allocator<decision_nation_pair>> result(a.begin(), a.end());
971 result.insert(result.end(), b.begin(), b.end());
972 return result;
973 });
974 // ensure total deterministic ordering
975 std::sort(total_vector.begin(), total_vector.end(), [&](auto a, auto b) {
976 auto na = a.second;
977 auto nb = b.second;
978 if(na != nb)
979 return na.index() < nb.index();
980 return a.first.index() < b.first.index();
981 });
982 // assumption 1: no duplicate pair of <n, d>
983 for(const auto& v : total_vector) {
984 auto n = v.second;
985 auto d = v.first;
986 auto e = state.world.decision_get_effect(d);
987 if(trigger::evaluate(state, state.world.decision_get_potential(d), trigger::to_generic(n), trigger::to_generic(n), 0)
988 && trigger::evaluate(state, state.world.decision_get_allow(d), trigger::to_generic(n), trigger::to_generic(n), 0)) {
989 effect::execute(state, e, trigger::to_generic(n), trigger::to_generic(n), 0, uint32_t(state.current_date.value), uint32_t(n.index() << 4 ^ d.index()));
991 [e, n, d, when = state.current_date](sys::state& state, text::layout_base& contents) {
992 text::add_line(state, contents, "msg_decision_1", text::variable_type::x, n, text::variable_type::y, state.world.decision_get_name(d));
993 text::add_line(state, contents, "msg_decision_2");
994 ui::effect_description(state, contents, e, trigger::to_generic(n), trigger::to_generic(n), 0, uint32_t(when.value), uint32_t(n.index() << 4 ^ d.index()));
995 },
996 "msg_decision_title",
997 n, dcon::nation_id{}, dcon::nation_id{},
999 });
1000 }
1001 }
1002}
1003
1004float estimate_pop_party_support(sys::state& state, dcon::nation_id n, dcon::political_party_id pid) {
1005 auto iid = state.world.political_party_get_ideology(pid);
1006 /*float v = 0.f;
1007 for(const auto poid : state.world.nation_get_province_ownership_as_nation(n)) {
1008 for(auto plid : state.world.province_get_pop_location_as_province(poid.get_province())) {
1009 float weigth = plid.get_pop().get_size() * 0.001f;
1010 v += state.world.pop_get_demographics(plid.get_pop(), pop_demographics::to_key(state, iid)) * weigth;
1011 }
1012 }*/
1013 return state.world.nation_get_demographics(n, demographics::to_key(state, iid));
1014}
1015
1016bool ai_can_appoint_political_party(sys::state& state, dcon::nation_id n) {
1018 return false;
1019 auto last_change = state.world.nation_get_ruling_party_last_appointed(n);
1020 if(last_change && state.current_date < last_change + 365)
1021 return false;
1022 if(politics::is_election_ongoing(state, n))
1023 return false;
1024 // Do not appoint if we are a democracy!
1025 if(politics::has_elections(state, n))
1026 return false;
1027 return true;
1028}
1029
1031 for(auto n : state.world.in_nation) {
1032 // skip over: non ais, dead nations
1033 if(n.get_is_player_controlled() || n.get_owned_province_count() == 0)
1034 continue;
1035
1036 if(ai_can_appoint_political_party(state, n)) {
1037 auto gov = n.get_government_type();
1038 auto identity = n.get_identity_from_identity_holder();
1039 auto start = state.world.national_identity_get_political_party_first(identity).id.index();
1040 auto end = start + state.world.national_identity_get_political_party_count(identity);
1041
1042 dcon::political_party_id target;
1043 float max_support = estimate_pop_party_support(state, n, state.world.nation_get_ruling_party(n));
1044 for(int32_t i = start; i < end; i++) {
1045 auto pid = dcon::political_party_id(uint16_t(i));
1046 if(pid != state.world.nation_get_ruling_party(n) && politics::political_party_is_active(state, n, pid) && (gov.get_ideologies_allowed() & ::culture::to_bits(state.world.political_party_get_ideology(pid))) != 0) {
1047 auto support = estimate_pop_party_support(state, n, pid);
1048 if(support > max_support) {
1049 target = pid;
1050 max_support = support;
1051 }
1052 }
1053 }
1054
1055 assert(target != state.world.nation_get_ruling_party(n));
1056 if(target) {
1057 politics::appoint_ruling_party(state, n, target);
1058 }
1059 }
1060 }
1061}
1062
1063void get_craved_factory_types(sys::state& state, dcon::nation_id nid, dcon::market_id mid, std::vector<dcon::factory_type_id>& desired_types) {
1064 assert(desired_types.empty());
1065 auto n = dcon::fatten(state.world, nid);
1066 auto m = dcon::fatten(state.world, mid);
1067
1068 if(desired_types.empty()) {
1069 for(auto type : state.world.in_factory_type) {
1070 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1071 auto& inputs = type.get_inputs();
1072 bool lacking_input = false;
1073 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1074
1075 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1076 if(inputs.commodity_type[i]) {
1077 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1078 lacking_input = true;
1079 } else {
1080 break;
1081 }
1082 }
1083
1084 float cost = economy::factory_type_build_cost(state, n, m, type);
1085 float output = economy::factory_type_output_cost(state, n, m, type);
1086 float input = economy::factory_type_input_cost(state, n, m, type);
1087
1088 if((output - input) / input > 10.f)
1089 desired_types.push_back(type.id);
1090 } // END if building unlocked
1091 }
1092 }
1093}
1094
1095void get_desired_factory_types(sys::state& state, dcon::nation_id nid, dcon::market_id mid, std::vector<dcon::factory_type_id>& desired_types) {
1096 assert(desired_types.empty());
1097 auto n = dcon::fatten(state.world, nid);
1098 auto m = dcon::fatten(state.world, mid);
1099
1100 // pass zero:
1101 // factories with stupid income margins
1102 // which are impossible to ignore if you are sane
1103 if(desired_types.empty()) {
1104 for(auto type : state.world.in_factory_type) {
1105 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1106 auto& inputs = type.get_inputs();
1107 bool lacking_input = false;
1108 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1109
1110 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1111 if(inputs.commodity_type[i]) {
1112 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1113 lacking_input = true;
1114 } else {
1115 break;
1116 }
1117 }
1118
1119 float cost = economy::factory_type_build_cost(state, n, m, type);
1120 float output = economy::factory_type_output_cost(state, n, m, type);
1121 float input = economy::factory_type_input_cost(state, n, m, type);
1122
1123 if((output - input) / input > 2.f)
1124 desired_types.push_back(type.id);
1125 } // END if building unlocked
1126 }
1127 }
1128
1129 // first pass: try to create factories which will pay back investment fast - in a year at most:
1130 // or have very high margins
1131 if(desired_types.empty()) {
1132 for(auto type : state.world.in_factory_type) {
1133 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1134 auto& inputs = type.get_inputs();
1135 bool lacking_input = false;
1136 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1137
1138 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1139 if(inputs.commodity_type[i]) {
1140 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1141 lacking_input = true;
1142 } else {
1143 break;
1144 }
1145 }
1146
1147 float cost = economy::factory_type_build_cost(state, n, m, type);
1148 float output = economy::factory_type_output_cost(state, n, m, type);
1149 float input = economy::factory_type_input_cost(state, n, m, type);
1150
1151 if((!lacking_input && (lacking_output || ((output - input) / cost < 365.f))) || (output - input) / input > 1.00f)
1152 desired_types.push_back(type.id);
1153 } // END if building unlocked
1154 }
1155 }
1156
1157 // second pass: try to create factories which have a good profit margin
1158 if(desired_types.empty()) {
1159 for(auto type : state.world.in_factory_type) {
1160 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1161 auto& inputs = type.get_inputs();
1162 bool lacking_input = false;
1163 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1164
1165 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1166 if(inputs.commodity_type[i]) {
1167 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1168 lacking_input = true;
1169 } else {
1170 break;
1171 }
1172 }
1173
1174 float output = economy::factory_type_output_cost(state, n, m, type);
1175 float input = economy::factory_type_input_cost(state, n, m, type);
1176
1177 if((output - input) / input > 0.3f)
1178 desired_types.push_back(type.id);
1179 } // END if building unlocked
1180 }
1181 }
1182}
1183
1184void get_state_craved_factory_types(sys::state& state, dcon::nation_id nid, dcon::market_id mid, std::vector<dcon::factory_type_id>& desired_types) {
1185 assert(desired_types.empty());
1186 auto n = dcon::fatten(state.world, nid);
1187 auto m = dcon::fatten(state.world, mid);
1188 auto treasury = n.get_stockpiles(economy::money);
1189
1190 if(desired_types.empty()) {
1191 for(auto type : state.world.in_factory_type) {
1192 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1193 float cost = economy::factory_type_build_cost(state, n, m, type);
1194 float output = economy::factory_type_output_cost(state, n, m, type);
1195 float input = economy::factory_type_input_cost(state, n, m, type);
1196
1197 if((output - input) / input > 20.f)
1198 desired_types.push_back(type.id);
1199 } // END if building unlocked
1200 }
1201 }
1202}
1203
1204void get_state_desired_factory_types(sys::state& state, dcon::nation_id nid, dcon::market_id mid, std::vector<dcon::factory_type_id>& desired_types) {
1205 assert(desired_types.empty());
1206 auto n = dcon::fatten(state.world, nid);
1207 auto m = dcon::fatten(state.world, mid);
1208
1209 auto treasury = n.get_stockpiles(economy::money);
1210
1211 // first pass: try to create factories which will pay back investment fast - in a year at most:
1212 if(desired_types.empty()) {
1213 for(auto type : state.world.in_factory_type) {
1214 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1215 auto& inputs = type.get_inputs();
1216 bool lacking_input = false;
1217 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1218
1219 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1220 if(inputs.commodity_type[i]) {
1221 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1222 lacking_input = true;
1223 } else {
1224 break;
1225 }
1226 }
1227
1228 float cost = economy::factory_type_build_cost(state, n, m, type);
1229 float output = economy::factory_type_output_cost(state, n, m, type);
1230 float input = economy::factory_type_input_cost(state, n, m, type);
1231
1232 if((lacking_output || ((output - input) / cost < 365.f)))
1233 desired_types.push_back(type.id);
1234 } // END if building unlocked
1235 }
1236 }
1237
1238 // second pass: try to create factories which have a good profit margin
1239 if(desired_types.empty()) {
1240 for(auto type : state.world.in_factory_type) {
1241 if(n.get_active_building(type) || type.get_is_available_from_start()) {
1242 auto& inputs = type.get_inputs();
1243 bool lacking_input = false;
1244 bool lacking_output = m.get_demand_satisfaction(type.get_output()) < 0.98f;
1245
1246 for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) {
1247 if(inputs.commodity_type[i]) {
1248 if(m.get_demand_satisfaction(inputs.commodity_type[i]) < 0.5f)
1249 lacking_input = true;
1250 } else {
1251 break;
1252 }
1253 }
1254
1255 float cost = economy::factory_type_build_cost(state, n, m, type);
1256 float output = economy::factory_type_output_cost(state, n, m, type);
1257 float input = economy::factory_type_input_cost(state, n, m, type);
1258 auto profitabilitymark = std::max(0.01f, cost * 10.f / treasury);
1259
1260 if((lacking_output || ((output - input) / input > profitabilitymark)))
1261 desired_types.push_back(type.id);
1262 } // END if building unlocked
1263 }
1264 }
1265}
1266
1267int32_t future_rebels_in_nation(sys::state& state, dcon::nation_id n) {
1268 auto total = 0;
1269 for(auto fac : state.world.nation_get_rebellion_within(n)) {
1270 for(auto ar : state.world.rebel_faction_get_army_rebel_control(fac.get_rebels())) {
1271 auto regs = ar.get_army().get_army_membership();
1272 total += int32_t(regs.end() - regs.begin());
1273 }
1274 }
1275
1276 return total;
1277}
1278
1279int16_t calculate_desired_army_size(sys::state& state, dcon::nation_id nation) {
1280 auto fid = dcon::fatten(state.world, nation);
1281
1282 auto factor = 1.0f;
1283
1284 if(state.world.nation_get_ai_is_threatened(nation)) {
1285 factor *= 1.25f;
1286 }
1287
1288 if(fid.get_is_at_war()) {
1289 factor *= 1.5f;
1290 }
1291
1292 if(state.world.nation_get_ai_strategy(nation) == ai_strategies::militant) {
1293 factor *= 1.25f;
1294 } else {
1295 factor *= 0.75f;
1296 }
1297
1298 if(future_rebels_in_nation(state, nation) == 0) {
1299 factor *= 0.9f;
1300 }
1301 else {
1302 factor *= 1.1f;
1303 }
1304
1305 auto in_sphere_of = state.world.nation_get_in_sphere_of(nation);
1306
1307 // Most dangerous neighbor
1308 dcon::nation_id greatest_neighbor;
1309 auto greatest_neighbor_strength = 0.0f;
1310
1311 auto own_str = estimate_strength(state, nation);
1312 for(auto b : state.world.nation_get_nation_adjacency_as_connected_nations(nation)) {
1313 auto other = b.get_connected_nations(0) != nation ? b.get_connected_nations(0) : b.get_connected_nations(1);
1314 if(!nations::are_allied(state, nation, other) && (!in_sphere_of || in_sphere_of != other.get_in_sphere_of())) {
1315
1316 auto other_str = estimate_strength(state, other);
1317 if(other_str > greatest_neighbor_strength && other_str < own_str * 10.0f) {
1318 greatest_neighbor_strength = other_str;
1319 greatest_neighbor = other;
1320 }
1321 }
1322 }
1323
1324 // How many regiments it has
1325 int16_t total = 0;
1326 for(auto p : state.world.nation_get_army_control(greatest_neighbor)) {
1327 auto frange = p.get_army().get_army_membership();
1328 total += int16_t(frange.end() - frange.begin());
1329 }
1330
1331 // Debug lines
1332#ifndef NDEBUG
1333 auto fid2 = dcon::fatten(state.world, nation);
1334 auto identity = fid.get_identity_from_identity_holder();
1335 auto tagname = text::produce_simple_string(state, identity.get_name());
1336 fid2 = dcon::fatten(state.world, greatest_neighbor);
1337 identity = fid2.get_identity_from_identity_holder();
1338 auto greatest_neighbour_tagname = text::produce_simple_string(state, identity.get_name());
1339#endif
1340
1341 return int16_t(std::clamp(total * double(factor), 0.1 * fid.get_recruitable_regiments(), 1.0 * fid.get_recruitable_regiments()));
1342}
1343
1345 for(auto n : state.world.in_nation) {
1346 // skip over: non ais, dead nations, and nations that aren't making money
1347 if(n.get_owned_province_count() == 0 || !n.get_is_civilized())
1348 continue;
1349
1350 if(n.get_is_player_controlled()) {
1351 // to handle the logic of player building automation later
1352 continue;
1353 }
1354
1355 /*
1356 if(n.get_spending_level() < 1.0f || n.get_last_treasury() >= n.get_stockpiles(economy::money))
1357 continue;
1358 */
1359
1360 float treasury = n.get_stockpiles(economy::money);
1361 float base_income = economy::estimate_daily_income(state, n);
1362 float estimated_construction_costs = economy::estimate_construction_spending_from_budget(state, n, std::max(treasury, 1'000'000'000'000.f));
1363
1364 //if our army is too small, ignore buildings:
1365 if(calculate_desired_army_size(state, n) * 0.4f > n.get_active_regiments())
1366 continue;
1367
1368 float budget = treasury * 0.5f + base_income - estimated_construction_costs * 2.f;
1369 float additional_expenses = 0.f;
1370 float days_prepaid = 10.f;
1371 auto rules = n.get_combined_issue_rules();
1372
1373 if(budget < 0.f) {
1374 continue;
1375 }
1376
1377 if((rules & issue_rule::expand_factory) != 0 || (rules & issue_rule::build_factory) != 0) {
1378 // prepare a list of states
1379 static std::vector<dcon::state_instance_id> ordered_states;
1380 ordered_states.clear();
1381 for(auto si : n.get_state_ownership()) {
1382 if(si.get_state().get_capital().get_is_colonial() == false)
1383 ordered_states.push_back(si.get_state().id);
1384 }
1385 std::sort(ordered_states.begin(), ordered_states.end(), [&](auto a, auto b) {
1386 auto apop = state.world.state_instance_get_demographics(a, demographics::total);
1387 auto bpop = state.world.state_instance_get_demographics(b, demographics::total);
1388 if(apop != bpop)
1389 return apop > bpop;
1390 else
1391 return a.index() < b.index();
1392 });
1393
1394 // try to build
1395 static::std::vector<dcon::factory_type_id> craved_types;
1396
1397 // desired types filled: try to construct or upgrade
1398 if(!craved_types.empty()) {
1399 if((rules & issue_rule::build_factory) == 0 && (rules & issue_rule::expand_factory) != 0) { // can't build -- by elimination, can upgrade
1400
1401 } else if((rules & issue_rule::build_factory) != 0) { // -- i.e. if building is possible
1402 for(auto si : ordered_states) {
1403
1404 auto market = state.world.state_instance_get_market_from_local_market(si);
1405
1406 if(budget - additional_expenses <= 0.f)
1407 break;
1408
1409 auto m = state.world.state_instance_get_market_from_local_market(si);
1410 craved_types.clear();
1411 get_state_craved_factory_types(state, n, m, craved_types);
1412
1413 // check -- either unemployed factory workers or no factory workers
1414 auto pw_num = state.world.state_instance_get_demographics(si,
1415 demographics::to_key(state, state.culture_definitions.primary_factory_worker));
1416 pw_num += state.world.state_instance_get_demographics(si,
1417 demographics::to_key(state, state.culture_definitions.secondary_factory_worker));
1418 auto pw_employed = state.world.state_instance_get_demographics(si,
1419 demographics::to_employment_key(state, state.culture_definitions.primary_factory_worker));
1420 pw_employed += state.world.state_instance_get_demographics(si,
1421 demographics::to_employment_key(state, state.culture_definitions.secondary_factory_worker));
1422
1423 if(pw_employed >= float(pw_num) * 2.5f && pw_num > 0.0f)
1424 continue; // no spare workers
1425
1426 auto type_selection = craved_types[rng::get_random(state, uint32_t(n.id.index() + int32_t(budget))) % craved_types.size()];
1427 assert(type_selection);
1428
1429 if(state.world.factory_type_get_is_coastal(type_selection) && !province::state_is_coastal(state, si))
1430 continue;
1431
1432 bool already_in_progress = [&]() {
1433 for(auto p : state.world.state_instance_get_state_building_construction(si)) {
1434 if(p.get_type() == type_selection)
1435 return true;
1436 }
1437 return false;
1438 }();
1439
1440 if(already_in_progress)
1441 continue;
1442
1443 // check: if present, try to upgrade
1444 bool present_in_location = false;
1445 bool under_cap = false;
1446
1447 province::for_each_province_in_state_instance(state, si, [&](dcon::province_id p) {
1448 for(auto fac : state.world.province_get_factory_location(p)) {
1449 auto type = fac.get_factory().get_building_type();
1450 if(type_selection == type) {
1451 under_cap = fac.get_factory().get_primary_employment() * state.world.market_get_labor_unskilled_demand_satisfaction(market) < 0.9f;
1452 present_in_location = true;
1453 return;
1454 }
1455 }
1456 });
1457 if(under_cap) {
1458 continue; // factory doesn't need to get larger
1459 }
1460
1461 auto expected_item_cost = 0.f;
1462 auto costs = state.world.factory_type_get_construction_costs(type_selection);
1463 auto time = state.world.factory_type_get_construction_time(type_selection);
1464 for(uint32_t i = 0; i < costs.set_size; ++i) {
1465 if(costs.commodity_type[i]) {
1466 expected_item_cost +=
1467 costs.commodity_amounts[i]
1468 * economy::price(state, market, costs.commodity_type[i])
1469 / float(time)
1470 * days_prepaid;
1471 } else {
1472 break;
1473 }
1474 }
1475
1476 if(budget - additional_expenses - expected_item_cost <= 0.f)
1477 continue;
1478
1479 if(present_in_location) {
1480 if((rules & issue_rule::expand_factory) != 0) {
1481 auto new_up = fatten(state.world, state.world.force_create_state_building_construction(si, n));
1482 new_up.set_is_pop_project(false);
1483 new_up.set_is_upgrade(true);
1484 new_up.set_type(type_selection);
1485
1486 additional_expenses += expected_item_cost;
1487 }
1488 continue;
1489 }
1490
1491 // else -- try to build -- must have room
1492 int32_t num_factories = economy::state_factory_count(state, si, n);
1493 if(num_factories < int32_t(state.defines.factories_per_state)) {
1494 auto new_up = fatten(state.world, state.world.force_create_state_building_construction(si, n));
1495 new_up.set_is_pop_project(false);
1496 new_up.set_is_upgrade(false);
1497 new_up.set_type(type_selection);
1498 additional_expenses += expected_item_cost;
1499 continue;
1500 } else {
1501 // TODO: try to delete a factory here
1502 }
1503 } // END for(auto si : ordered_states) {
1504 } // END if((rules & issue_rule::build_factory) == 0)
1505 } // END if(!desired_types.empty()) {
1506
1507 // try to upgrade factories first:
1508 if((rules & issue_rule::expand_factory) != 0) { // can't build -- by elimination, can upgrade
1509 for(auto si : ordered_states) {
1510
1511 auto market = state.world.state_instance_get_market_from_local_market(si);
1512
1513 if(budget - additional_expenses <= 0.f)
1514 break;
1515
1516 province::for_each_province_in_state_instance(state, si, [&](dcon::province_id p) {
1517 for(auto fac : state.world.province_get_factory_location(p)) {
1518 auto type = fac.get_factory().get_building_type();
1519
1520
1521 auto unprofitable = fac.get_factory().get_unprofitable();
1522 auto factory_level = fac.get_factory().get_level();
1523 auto primary_employment = fac.get_factory().get_primary_employment() * state.world.market_get_labor_unskilled_demand_satisfaction(market);
1524
1525 if(!unprofitable && factory_level < uint8_t(255) && primary_employment >= 0.9f) {
1526 // test if factory is already upgrading
1527 auto ug_in_progress = false;
1528 for(auto c : state.world.state_instance_get_state_building_construction(si)) {
1529 if(c.get_type() == type) {
1530 ug_in_progress = true;
1531 break;
1532 }
1533 }
1534
1535 auto expected_item_cost = 0.f;
1536 auto& costs = state.world.factory_type_get_construction_costs(type);
1537 auto& time = state.world.factory_type_get_construction_time(type);
1538 for(uint32_t i = 0; i < costs.set_size; ++i) {
1539 if(costs.commodity_type[i]) {
1540 expected_item_cost +=
1541 costs.commodity_amounts[i]
1542 * economy::price(state, market, costs.commodity_type[i])
1543 / float(time)
1544 * days_prepaid;
1545 } else {
1546 break;
1547 }
1548 }
1549
1550 if(budget - additional_expenses - expected_item_cost <= 0.f)
1551 continue;
1552
1553 if(!ug_in_progress) {
1554 auto new_up = fatten(state.world, state.world.force_create_state_building_construction(si, n));
1555 new_up.set_is_pop_project(false);
1556 new_up.set_is_upgrade(true);
1557 new_up.set_type(type);
1558
1559 additional_expenses += expected_item_cost;
1560 }
1561 }
1562 }
1563 });
1564 }
1565 }
1566
1567 // try to build
1568 static::std::vector<dcon::factory_type_id> desired_types;
1569
1570 // desired types filled: try to construct or upgrade
1571 if(!desired_types.empty()) {
1572 if((rules & issue_rule::build_factory) == 0 && (rules & issue_rule::expand_factory) != 0) { // can't build -- by elimination, can upgrade
1573
1574 } else if((rules & issue_rule::build_factory) != 0) { // -- i.e. if building is possible
1575 for(auto si : ordered_states) {
1576
1577 auto market = state.world.state_instance_get_market_from_local_market(si);
1578
1579 if(budget - additional_expenses <= 0.f)
1580 break;
1581
1582 auto m = state.world.state_instance_get_market_from_local_market(si);
1583 desired_types.clear();
1584 get_state_desired_factory_types(state, n, m, desired_types);
1585
1586 // check -- either unemployed factory workers or no factory workers
1587 auto pw_num = state.world.state_instance_get_demographics(si,
1588 demographics::to_key(state, state.culture_definitions.primary_factory_worker));
1589 pw_num += state.world.state_instance_get_demographics(si,
1590 demographics::to_key(state, state.culture_definitions.secondary_factory_worker));
1591 auto pw_employed = state.world.state_instance_get_demographics(si,
1592 demographics::to_employment_key(state, state.culture_definitions.primary_factory_worker));
1593 pw_employed += state.world.state_instance_get_demographics(si,
1594 demographics::to_employment_key(state, state.culture_definitions.secondary_factory_worker));
1595
1596 if(pw_employed >= float(pw_num) * 2.5f && pw_num > 0.0f)
1597 continue; // no spare workers
1598
1599 auto type_selection = desired_types[rng::get_random(state, uint32_t(n.id.index() + int32_t(budget))) % desired_types.size()];
1600 assert(type_selection);
1601
1602 if(state.world.factory_type_get_is_coastal(type_selection) && !province::state_is_coastal(state, si))
1603 continue;
1604
1605 bool already_in_progress = [&]() {
1606 for(auto p : state.world.state_instance_get_state_building_construction(si)) {
1607 if(p.get_type() == type_selection)
1608 return true;
1609 }
1610 return false;
1611 }();
1612
1613 if(already_in_progress)
1614 continue;
1615
1616 // check: if present, try to upgrade
1617 bool present_in_location = false;
1618 bool under_cap = false;
1619
1620 province::for_each_province_in_state_instance(state, si, [&](dcon::province_id p) {
1621 for(auto fac : state.world.province_get_factory_location(p)) {
1622 auto type = fac.get_factory().get_building_type();
1623 if(type_selection == type) {
1624 under_cap = fac.get_factory().get_primary_employment() * state.world.market_get_labor_unskilled_demand_satisfaction(market) < 0.9f;
1625 present_in_location = true;
1626 return;
1627 }
1628 }
1629 });
1630 if(under_cap) {
1631 continue; // factory doesn't need to get larger
1632 }
1633
1634 auto expected_item_cost = 0.f;
1635 auto costs = state.world.factory_type_get_construction_costs(type_selection);
1636 auto time = state.world.factory_type_get_construction_time(type_selection);
1637 for(uint32_t i = 0; i < costs.set_size; ++i) {
1638 if(costs.commodity_type[i]) {
1639 expected_item_cost +=
1640 costs.commodity_amounts[i]
1641 * economy::price(state, market, costs.commodity_type[i])
1642 / float(time)
1643 * days_prepaid;
1644 } else {
1645 break;
1646 }
1647 }
1648
1649 if(budget - additional_expenses - expected_item_cost <= 0.f)
1650 continue;
1651
1652 if(present_in_location) {
1653 if((rules & issue_rule::expand_factory) != 0) {
1654 auto new_up = fatten(state.world, state.world.force_create_state_building_construction(si, n));
1655 new_up.set_is_pop_project(false);
1656 new_up.set_is_upgrade(true);
1657 new_up.set_type(type_selection);
1658
1659 additional_expenses += expected_item_cost;
1660 }
1661 continue;
1662 }
1663
1664 // else -- try to build -- must have room
1665 int32_t num_factories = economy::state_factory_count(state, si, n);
1666 if(num_factories < int32_t(state.defines.factories_per_state)) {
1667 auto new_up = fatten(state.world, state.world.force_create_state_building_construction(si, n));
1668 new_up.set_is_pop_project(false);
1669 new_up.set_is_upgrade(false);
1670 new_up.set_type(type_selection);
1671 additional_expenses += expected_item_cost;
1672 continue;
1673 } else {
1674 // TODO: try to delete a factory here
1675 }
1676 } // END for(auto si : ordered_states) {
1677 } // END if((rules & issue_rule::build_factory) == 0)
1678 } // END if(!desired_types.empty()) {
1679 } // END if((rules & issue_rule::expand_factory) != 0 || (rules & issue_rule::build_factory) != 0)
1680
1681 if(0.9f * n.get_recruitable_regiments() > n.get_active_regiments())
1682 continue;
1683
1684 static std::vector<dcon::province_id> project_provs;
1685 project_provs.clear();
1686
1687 // try naval bases
1688 if(budget - additional_expenses >= 0.f) {
1689 project_provs.clear();
1690 for(auto o : n.get_province_ownership()) {
1691 if(!o.get_province().get_is_coast())
1692 continue;
1693 if(n != o.get_province().get_nation_from_province_control())
1694 continue;
1695
1696 if(military::province_is_under_siege(state, o.get_province()))
1697 continue;
1698 if(o.get_province().get_building_level(uint8_t(economy::province_building_type::naval_base)) == 0 && o.get_province().get_state_membership().get_naval_base_is_taken())
1699 continue;
1700
1701 int32_t current_lvl = o.get_province().get_building_level(uint8_t(economy::province_building_type::naval_base));
1702 int32_t max_local_lvl = n.get_max_building_level(uint8_t(economy::province_building_type::naval_base));
1703 int32_t min_build = int32_t(o.get_province().get_modifier_values(sys::provincial_mod_offsets::min_build_naval_base));
1704
1705 if(max_local_lvl - current_lvl - min_build <= 0)
1706 continue;
1707
1708 if(!province::has_naval_base_being_built(state, o.get_province())) {
1709 project_provs.push_back(o.get_province().id);
1710 }
1711 }
1712
1713 auto cap = n.get_capital();
1714 std::sort(project_provs.begin(), project_provs.end(), [&](dcon::province_id a, dcon::province_id b) {
1715 auto a_dist = province::sorting_distance(state, a, cap);
1716 auto b_dist = province::sorting_distance(state, b, cap);
1717 if(a_dist != b_dist)
1718 return a_dist < b_dist;
1719 else
1720 return a.index() < b.index();
1721 });
1722 if(!project_provs.empty()) {
1723 auto si = state.world.province_get_state_membership(project_provs[0]);
1724 auto market = state.world.state_instance_get_market_from_local_market(si);
1725
1726 // avoid overbuilding!
1727
1728 auto expected_item_cost = 0.f;
1729 auto& costs = state.economy_definitions.building_definitions[int32_t(economy::province_building_type::naval_base)].cost;
1730 auto& time = state.economy_definitions.building_definitions[int32_t(economy::province_building_type::naval_base)].time;
1731 for(uint32_t i = 0; i < costs.set_size; ++i) {
1732 if(costs.commodity_type[i]) {
1733 expected_item_cost +=
1734 costs.commodity_amounts[i]
1735 * economy::price(state, market, costs.commodity_type[i])
1736 / float(time)
1737 * days_prepaid;
1738 } else {
1739 break;
1740 }
1741 }
1742
1743 if(budget - additional_expenses - expected_item_cost <= 0.f)
1744 continue;
1745
1746 if(si)
1747 si.set_naval_base_is_taken(true);
1748 auto new_rr = fatten(state.world, state.world.force_create_province_building_construction(project_provs[0], n));
1749 new_rr.set_is_pop_project(false);
1751 additional_expenses += expected_item_cost;
1752 }
1753 }
1754
1755 // try railroads
1756 const struct {
1757 bool buildable;
1759 dcon::provincial_modifier_value mod;
1760 } econ_buildable[3] = {
1761 { (rules & issue_rule::build_railway) != 0, economy::province_building_type::railroad, sys::provincial_mod_offsets::min_build_railroad },
1762 { (rules & issue_rule::build_bank) != 0 && state.economy_definitions.building_definitions[uint32_t(economy::province_building_type::bank)].defined, economy::province_building_type::bank, sys::provincial_mod_offsets::min_build_bank },
1763 { (rules & issue_rule::build_university) != 0 && state.economy_definitions.building_definitions[uint32_t(economy::province_building_type::university)].defined, economy::province_building_type::university, sys::provincial_mod_offsets::min_build_university }
1764 };
1765 for(auto i = 0; i < 3; i++) {
1766 if(econ_buildable[i].buildable && budget - additional_expenses > 0) {
1767 project_provs.clear();
1768 for(auto o : n.get_province_ownership()) {
1769 if(n != o.get_province().get_nation_from_province_control())
1770 continue;
1771 if(military::province_is_under_siege(state, o.get_province()))
1772 continue;
1773 int32_t current_lvl = state.world.province_get_building_level(o.get_province(), uint8_t(econ_buildable[i].type));
1774 int32_t max_local_lvl = state.world.nation_get_max_building_level(n, uint8_t(econ_buildable[i].type));
1775 int32_t min_build = int32_t(state.world.province_get_modifier_values(o.get_province(), econ_buildable[i].mod));
1776 if(max_local_lvl - current_lvl - min_build <= 0)
1777 continue;
1778 if(!province::has_province_building_being_built(state, o.get_province(), econ_buildable[i].type)) {
1779 project_provs.push_back(o.get_province().id);
1780 }
1781 }
1782 auto cap = n.get_capital();
1783 std::sort(project_provs.begin(), project_provs.end(), [&](dcon::province_id a, dcon::province_id b) {
1784 auto a_dist = province::sorting_distance(state, a, cap);
1785 auto b_dist = province::sorting_distance(state, b, cap);
1786 if(a_dist != b_dist)
1787 return a_dist < b_dist;
1788 else
1789 return a.index() < b.index();
1790 });
1791 for(uint32_t j = 0; j < project_provs.size() && budget - additional_expenses > 0; ++j) {
1792 auto sid = state.world.province_get_state_membership(project_provs[j]);
1793 auto market = state.world.state_instance_get_market_from_local_market(sid);
1794
1795 // avoid overbuilding!
1796
1797 auto expected_item_cost = 0.f;
1798 auto& costs = state.economy_definitions.building_definitions[uint8_t(econ_buildable[i].type)].cost;
1799 auto& time = state.economy_definitions.building_definitions[uint8_t(econ_buildable[i].type)].time;
1800 for(uint32_t k = 0; k < costs.set_size; ++k) {
1801 if(costs.commodity_type[k]) {
1802 expected_item_cost +=
1803 costs.commodity_amounts[k]
1804 * economy::price(state, market, costs.commodity_type[k])
1805 / float(time)
1806 * days_prepaid;
1807 } else {
1808 break;
1809 }
1810 }
1811
1812 if(budget - additional_expenses - expected_item_cost <= 0.f)
1813 continue;
1814
1815 auto new_proj = fatten(state.world, state.world.force_create_province_building_construction(project_provs[j], n));
1816 new_proj.set_is_pop_project(false);
1817 new_proj.set_type(uint8_t(econ_buildable[i].type));
1818 additional_expenses += expected_item_cost;
1819 }
1820 }
1821 }
1822
1823 if(0.95f * n.get_recruitable_regiments() > n.get_active_regiments())
1824 continue;
1825
1826 // try forts
1827 if(budget - additional_expenses > 0.f) {
1828 project_provs.clear();
1829
1830 for(auto o : n.get_province_ownership()) {
1831 if(n != o.get_province().get_nation_from_province_control())
1832 continue;
1833
1834 if(military::province_is_under_siege(state, o.get_province()))
1835 continue;
1836
1837 int32_t current_lvl = state.world.province_get_building_level(o.get_province(), uint8_t(economy::province_building_type::fort));
1838 int32_t max_local_lvl = state.world.nation_get_max_building_level(n, uint8_t(economy::province_building_type::fort));
1839 int32_t min_build = int32_t(state.world.province_get_modifier_values(o.get_province(), sys::provincial_mod_offsets::min_build_fort));
1840
1841 if(max_local_lvl - current_lvl - min_build <= 0)
1842 continue;
1843
1844 if(!province::has_fort_being_built(state, o.get_province())) {
1845 project_provs.push_back(o.get_province().id);
1846 }
1847 }
1848
1849 auto cap = n.get_capital();
1850 std::sort(project_provs.begin(), project_provs.end(), [&](dcon::province_id a, dcon::province_id b) {
1851 auto a_dist = province::sorting_distance(state, a, cap);
1852 auto b_dist = province::sorting_distance(state, b, cap);
1853 if(a_dist != b_dist)
1854 return a_dist < b_dist;
1855 else
1856 return a.index() < b.index();
1857 });
1858
1859 for(uint32_t i = 0; i < project_provs.size() && budget - additional_expenses > 0.f; ++i) {
1860
1861 auto sid = state.world.province_get_state_membership(project_provs[i]);
1862 auto market = state.world.state_instance_get_market_from_local_market(sid.id);
1863
1864 // avoid overbuilding!
1865
1866 auto expected_item_cost = 0.f;
1867 auto& costs = state.economy_definitions.building_definitions[uint8_t(economy::province_building_type::fort)].cost;
1868 auto& time = state.economy_definitions.building_definitions[uint8_t(economy::province_building_type::fort)].time;
1869 for(uint32_t k = 0; k < costs.set_size; ++k) {
1870 if(costs.commodity_type[k]) {
1871 expected_item_cost +=
1872 costs.commodity_amounts[k]
1873 * economy::price(state, market, costs.commodity_type[k])
1874 * days_prepaid
1875 * 100000.f;
1876 // forts are very bad investment and demand volatile goods,
1877 // so build them if AI is really rich and has no idea where how to spend money
1878 } else {
1879 break;
1880 }
1881 }
1882
1883 if(budget - additional_expenses - expected_item_cost <= 0.f)
1884 continue;
1885
1886 auto new_rr = fatten(state.world, state.world.force_create_province_building_construction(project_provs[i], n));
1887 new_rr.set_is_pop_project(false);
1889 additional_expenses += expected_item_cost;
1890 }
1891 }
1892 }
1893}
1894
1896 static std::vector<dcon::state_definition_id> investments;
1897 static std::vector<int32_t> free_points;
1898
1899 investments.clear();
1900 investments.resize(uint32_t(state.defines.colonial_rank));
1901
1902 free_points.clear();
1903 free_points.resize(uint32_t(state.defines.colonial_rank), -1);
1904
1905
1906
1907 for(auto col : state.world.in_colonization) {
1908 auto n = col.get_colonizer();
1909 if(n.get_is_player_controlled() == false
1910 && n.get_rank() <= uint16_t(state.defines.colonial_rank)
1911 && !investments[n.get_rank() - 1]
1912 && col.get_state().get_colonization_stage() <= uint8_t(2)
1913 // Perhaps wrong logic
1914 && col.get_state() != state.world.state_instance_get_definition(state.crisis_state_instance)
1915 && (!state.crisis_war || n.get_is_at_war() == false)
1916 ) {
1917
1918 if(state.crisis_attacker_wargoals.size() > 0) {
1919 auto first_wg = state.crisis_attacker_wargoals.at(0);
1920 if(first_wg.state == col.get_state()) {
1921 continue;
1922 }
1923 }
1924
1925 auto crange = col.get_state().get_colonization();
1926 if(crange.end() - crange.begin() > 1) {
1927 if(col.get_last_investment() + int32_t(state.defines.colonization_days_between_investment) <= state.current_date) {
1928
1929 if(free_points[n.get_rank() - 1] < 0) {
1930 free_points[n.get_rank() - 1] = nations::free_colonial_points(state, n);
1931 }
1932
1933 int32_t cost = 0;;
1934 if(col.get_state().get_colonization_stage() == 1) {
1935 cost = int32_t(state.defines.colonization_interest_cost);
1936 } else if(col.get_level() <= 4) {
1937 cost = int32_t(state.defines.colonization_influence_cost);
1938 } else {
1939 cost =
1940 int32_t(state.defines.colonization_extra_guard_cost * (col.get_level() - 4) + state.defines.colonization_influence_cost);
1941 }
1942 if(free_points[n.get_rank() - 1] >= cost) {
1943 investments[n.get_rank() - 1] = col.get_state().id;
1944 }
1945 }
1946 }
1947 }
1948 }
1949 for(uint32_t i = 0; i < investments.size(); ++i) {
1950 if(investments[i])
1951 province::increase_colonial_investment(state, state.nations_by_rank[i], investments[i]);
1952 }
1953}
1955 static std::vector<int32_t> free_points;
1956 free_points.clear();
1957 free_points.resize(uint32_t(state.defines.colonial_rank), -1);
1958 for(int32_t i = 0; i < int32_t(state.defines.colonial_rank); ++i) {
1959 if(state.world.nation_get_is_player_controlled(state.nations_by_rank[i])) {
1960 free_points[i] = 0;
1961 } else {
1962 if(military::get_role(state, state.crisis_war, state.nations_by_rank[i]) != military::war_role::none) {
1963 free_points[i] = 0;
1964 } else {
1965 free_points[i] = nations::free_colonial_points(state, state.nations_by_rank[i]);
1966 }
1967 }
1968 }
1969 for(auto sd : state.world.in_state_definition) {
1970 if(sd.get_colonization_stage() <= 1) {
1971 bool has_unowned_land = false;
1972
1973 dcon::province_id coastal_target;
1974 for(auto p : state.world.state_definition_get_abstract_state_membership(sd)) {
1975 if(!p.get_province().get_nation_from_province_ownership()) {
1976 if(p.get_province().get_is_coast() && !coastal_target) {
1977 coastal_target = p.get_province();
1978 }
1979 if(p.get_province().id.index() < state.province_definitions.first_sea_province.index())
1980 has_unowned_land = true;
1981 }
1982 }
1983 if(has_unowned_land) {
1984 for(int32_t i = 0; i < int32_t(state.defines.colonial_rank); ++i) {
1985 if(free_points[i] > 0) {
1986 bool adjacent = false;
1987 if(province::fast_can_start_colony(state, state.nations_by_rank[i], sd, free_points[i], coastal_target, adjacent)) {
1988 free_points[i] -= int32_t(state.defines.colonization_interest_cost_initial + (adjacent ? state.defines.colonization_interest_cost_neighbor_modifier : 0.0f));
1989
1990 auto new_rel = fatten(state.world, state.world.force_create_colonization(sd, state.nations_by_rank[i]));
1991 new_rel.set_level(uint8_t(1));
1992 new_rel.set_last_investment(state.current_date);
1993 new_rel.set_points_invested(uint16_t(state.defines.colonization_interest_cost_initial + (adjacent ? state.defines.colonization_interest_cost_neighbor_modifier : 0.0f)));
1994
1995 state.world.state_definition_set_colonization_stage(sd, uint8_t(1));
1996 }
1997 }
1998 }
1999 }
2000 }
2001 }
2002}
2003
2005 for(auto si : state.world.in_state_instance) {
2006 if(si.get_capital().get_is_colonial() && si.get_nation_from_state_ownership().get_is_player_controlled() == false) {
2007 if(province::can_integrate_colony(state, si)) {
2008 province::upgrade_colonial_state(state, si.get_nation_from_state_ownership(), si);
2009 }
2010 }
2011 }
2012}
2013
2014void civilize(sys::state& state) {
2015 for(auto n : state.world.in_nation) {
2016 if(!n.get_is_player_controlled() && !n.get_is_civilized() && n.get_modifier_values(sys::national_mod_offsets::civilization_progress_modifier) >= 1.0f) {
2017 nations::make_civilized(state, n);
2018 }
2019 }
2020}
2021
2023 for(auto n : state.world.in_nation) {
2024 if(n.get_is_player_controlled() || n.get_owned_province_count() == 0)
2025 continue;
2026
2027 if(n.get_is_civilized()) { // political & social
2028 // Enact social policies to deter Jacobin rebels from overruning the country
2029 // Reactionaries will popup in effect but they are MORE weak that Jacobins
2030 dcon::issue_option_id iss;
2031 float max_support = 0.0f;
2032
2033 for(auto m : state.world.nation_get_movement_within(n)) {
2034 if(m.get_movement().get_associated_issue_option() && m.get_movement().get_pop_support() > max_support) {
2035 iss = m.get_movement().get_associated_issue_option();
2036 max_support = m.get_movement().get_pop_support();
2037 }
2038 }
2039 if(!iss || !command::can_enact_issue(state, n, iss)) {
2040 max_support = 0.0f;
2041 iss = dcon::issue_option_id{};
2042 state.world.for_each_issue_option([&](dcon::issue_option_id io) {
2043 if(command::can_enact_issue(state, n, io)) {
2044 float support = 0.f;
2045 for(const auto poid : state.world.nation_get_province_ownership_as_nation(n)) {
2046 for(auto plid : state.world.province_get_pop_location_as_province(poid.get_province())) {
2047 float weigth = plid.get_pop().get_size() * 0.001f;
2048 support += pop_demographics::get_demo(state, plid.get_pop(), pop_demographics::to_key(state, io)) * weigth;
2049 }
2050 }
2051 if(support > max_support) {
2052 iss = io;
2053 max_support = support;
2054 }
2055 }
2056 });
2057 }
2058 if(iss) {
2059 nations::enact_issue(state, n, iss);
2060 }
2061 } else { // military and economic
2062 dcon::reform_option_id cheap_r;
2063 float cheap_cost = 0.0f;
2064
2065 auto e_mul = politics::get_economic_reform_multiplier(state, n);
2066 auto m_mul = politics::get_military_reform_multiplier(state, n);
2067
2068 for(auto r : state.world.in_reform_option) {
2069 bool is_military = state.world.reform_get_reform_type(state.world.reform_option_get_parent_reform(r)) == uint8_t(culture::issue_category::military);
2070
2071 auto reform = state.world.reform_option_get_parent_reform(r);
2072 auto current = state.world.nation_get_reforms(n, reform.id).id;
2073 auto allow = state.world.reform_option_get_allow(r);
2074
2075 if(r.id.index() > current.index() && (!state.world.reform_get_is_next_step_only(reform.id) || current.index() + 1 == r.id.index()) && (!allow || trigger::evaluate(state, allow, trigger::to_generic(n.id), trigger::to_generic(n.id), 0))) {
2076
2077 float base_cost = float(state.world.reform_option_get_technology_cost(r));
2078 float reform_factor = is_military ? m_mul : e_mul;
2079
2080 if(!cheap_r || base_cost * reform_factor < cheap_cost) {
2081 cheap_cost = base_cost * reform_factor;
2082 cheap_r = r.id;
2083 }
2084 }
2085 }
2086
2087 if(cheap_r && cheap_cost <= n.get_research_points()) {
2088 nations::enact_reform(state, n, cheap_r);
2089 }
2090 }
2091 }
2092}
2093
2094bool will_be_crisis_primary_attacker(sys::state& state, dcon::nation_id n) {
2095
2096 auto first_wg = state.crisis_attacker_wargoals.at(0);
2097 if(first_wg.cb == state.military_definitions.crisis_colony) {
2098 auto colonizers = state.world.state_definition_get_colonization(first_wg.state);
2099 if(colonizers.end() - colonizers.begin() < 2)
2100 return false;
2101
2102 auto defending_colonizer = (*(colonizers.begin() + 1)).get_colonizer();
2103 if(state.world.nation_get_in_sphere_of(defending_colonizer) == n)
2104 return false;
2105 if(state.world.nation_get_ai_rival(n) == defending_colonizer
2106 || (defending_colonizer.get_in_sphere_of() && !nations::are_allied(state, n, defending_colonizer) && state.world.nation_get_ai_rival(n) == defending_colonizer.get_in_sphere_of())) {
2107 return true;
2108 } else {
2109 if(state.primary_crisis_defender && state.world.nation_get_ai_rival(n) == state.primary_crisis_defender)
2110 return true;
2111 return false;
2112 }
2113 } else if(first_wg.cb == state.military_definitions.liberate) {
2114 auto state_owner = first_wg.target_nation;
2115 auto liberated = state.world.national_identity_get_nation_from_identity_holder(first_wg.wg_tag);
2116
2117 if(state_owner == n) //don't shoot ourselves
2118 return false;
2119 if(liberated == n) //except when we are shooting someone else
2120 return true;
2121 if(state.world.nation_get_in_sphere_of(state_owner) == n || nations::are_allied(state, n, state_owner))
2122 return false;
2123 if(state.world.nation_get_ai_rival(n) == state_owner)
2124 return true;
2125 if(state.world.nation_get_in_sphere_of(state_owner) && state.world.nation_get_ai_rival(n) == state.world.nation_get_in_sphere_of(state_owner))
2126 return true;
2127 if(state.world.nation_get_in_sphere_of(liberated) == n || nations::are_allied(state, n, liberated))
2128 return true;
2129 if(state.primary_crisis_defender && state.world.nation_get_ai_rival(n) == state.primary_crisis_defender)
2130 return true;
2131 return false;
2132 } else {
2133 return false;
2134 }
2135}
2136bool will_be_crisis_primary_defender(sys::state& state, dcon::nation_id n) {
2137 auto first_wg = state.crisis_attacker_wargoals.at(0);
2138 if(first_wg.cb == state.military_definitions.crisis_colony) {
2139 auto colonizers = state.world.state_definition_get_colonization(first_wg.state);
2140 if(colonizers.end() - colonizers.begin() < 2)
2141 return false;
2142
2143 auto attacking_colonizer = (*colonizers.begin()).get_colonizer();
2144
2145 if(state.world.nation_get_in_sphere_of(attacking_colonizer) == n)
2146 return false;
2147 if(state.world.nation_get_ai_rival(n) == attacking_colonizer
2148 || (attacking_colonizer.get_in_sphere_of() && !nations::are_allied(state, n, attacking_colonizer) && state.world.nation_get_ai_rival(n) == attacking_colonizer.get_in_sphere_of())
2149 || state.world.nation_get_ai_rival(n) == state.primary_crisis_attacker) {
2150 return true;
2151 } else {
2152 if(state.primary_crisis_attacker && state.world.nation_get_ai_rival(n) == state.primary_crisis_attacker)
2153 return true;
2154 return false;
2155 }
2156 } else if(first_wg.cb == state.military_definitions.liberate) {
2157 auto state_owner = first_wg.target_nation;
2158 auto liberated = state.world.national_identity_get_nation_from_identity_holder(first_wg.wg_tag);
2159
2160 if(state_owner == n) //don't shoot ourselves
2161 return false;
2162 if(liberated == n) //except when we are shooting someone else
2163 return true;
2164 if(state.world.nation_get_in_sphere_of(liberated) == n || nations::are_allied(state, n, liberated))
2165 return false;
2166 if(state.world.nation_get_ai_rival(n) == liberated)
2167 return true;
2168 if(state.world.nation_get_in_sphere_of(liberated) && state.world.nation_get_ai_rival(n) == state.world.nation_get_in_sphere_of(liberated))
2169 return true;
2170 if(state.world.nation_get_in_sphere_of(state_owner) == n || nations::are_allied(state, n, state_owner))
2171 return true;
2172 if(state.primary_crisis_attacker && state.world.nation_get_ai_rival(n) == state.primary_crisis_attacker)
2173 return true;
2174 return false;
2175 } else {
2176 return false;
2177 }
2178}
2179
2181 float attacker = 0.0f;
2182 float defender = 0.0f;
2183 bool defender_win = false;
2184 bool attacker_win = false;
2185 bool fast_victory = false;
2186};
2187
2189 float atotal = 0.0f;
2190 float dtotal = 0.0f;
2191
2192 if(state.crisis_attacker && state.crisis_attacker != state.primary_crisis_attacker) {
2193 atotal += estimate_strength(state, state.crisis_attacker);
2194 }
2195 if(state.crisis_defender && state.crisis_defender != state.primary_crisis_defender) {
2196 dtotal += estimate_strength(state, state.crisis_defender);
2197 }
2198 for(auto& i : state.crisis_participants) {
2199 if(!i.id)
2200 break;
2201 if(!i.merely_interested) {
2202 if(i.supports_attacker) {
2203 atotal += estimate_strength(state, i.id);
2204 } else {
2205 dtotal += estimate_strength(state, i.id);
2206 }
2207 }
2208 }
2209
2210 auto necessary_atk_win_ratio = 1.55f;
2211 auto necessary_def_win_ratio = 1.55f;
2212 auto necessary_fast_win_ratio = 1.9f;
2213 for(auto wg : state.crisis_attacker_wargoals) {
2214 if(!wg.cb) {
2215 break;
2216 }
2217
2218 necessary_atk_win_ratio += 0.10f;
2219 necessary_fast_win_ratio += 0.1f;
2220 }
2221 for(auto wg : state.crisis_defender_wargoals) {
2222 if(!wg.cb) {
2223 break;
2224 }
2225
2226 necessary_def_win_ratio += 0.10f;
2227 necessary_fast_win_ratio += 0.1f;
2228 }
2229
2230 auto def_victory = dtotal > atotal * necessary_def_win_ratio;
2231 auto atk_victory = atotal > dtotal * necessary_atk_win_ratio;
2232 auto fast_victory = (dtotal / atotal > necessary_fast_win_ratio) || (atotal / dtotal > necessary_fast_win_ratio);
2233 if(fast_victory) {
2234 atk_victory = atotal > dtotal;
2235 def_victory = dtotal > atotal;
2236 }
2237
2238 return crisis_str{ atotal, dtotal, def_victory, atk_victory, fast_victory};
2239}
2240
2241bool will_join_crisis_with_offer(sys::state& state, dcon::nation_id n, sys::full_wg offer) {
2242 if(offer.target_nation == state.world.nation_get_ai_rival(n))
2243 return true;
2244 auto offer_bits = state.world.cb_type_get_type_bits(offer.cb);
2246 return true;
2247 return false;
2248}
2249
2250bool ai_offer_cb(sys::state& state, dcon::cb_type_id t) {
2251 auto offer_bits = state.world.cb_type_get_type_bits(t);
2253 return false;
2254 if((offer_bits & military::cb_flag::all_allowed_states) != 0)
2255 return false;
2257 return false;
2259 return false;
2260 return true;
2261}
2262
2263/* Prioritize states inside the nation */
2264void state_target_list(std::vector<dcon::state_instance_id>& result, sys::state& state, dcon::nation_id for_nation, dcon::nation_id within) {
2265 result.clear();
2266 for(auto si : state.world.nation_get_state_ownership(within)) {
2267 result.push_back(si.get_state().id);
2268 }
2269
2270 auto distance_from = state.world.nation_get_capital(for_nation).id;
2271 int32_t first = 0;
2272
2273 if(state.world.get_nation_adjacency_by_nation_adjacency_pair(for_nation, within)) {
2274 int32_t last = int32_t(result.size());
2275 while(first < last - 1) {
2276 while(first < last && province::state_borders_nation(state, for_nation, result[first])) {
2277 ++first;
2278 }
2279 while(first < last - 1 && !province::state_borders_nation(state, for_nation, result[last - 1])) {
2280 --last;
2281 }
2282 if(first < last - 1) {
2283 std::swap(result[first], result[last - 1]);
2284 ++first;
2285 --last;
2286 }
2287 }
2288
2289 std::sort(result.begin(), result.begin() + first, [&](dcon::state_instance_id a, dcon::state_instance_id b) {
2290 auto a_distance = province::sorting_distance(state, state.world.state_instance_get_capital(a), distance_from);
2291 auto b_distance = province::sorting_distance(state, state.world.state_instance_get_capital(b), distance_from);
2292 if(a_distance != b_distance)
2293 return a_distance < b_distance;
2294 else
2295 return a.index() < b.index();
2296 });
2297 }
2298 if(state.world.nation_get_total_ports(for_nation) > 0 && state.world.nation_get_total_ports(within) > 0) {
2299 int32_t last = int32_t(result.size());
2300 while(first < last - 1) {
2301 while(first < last && province::state_is_coastal(state, result[first])) {
2302 ++first;
2303 }
2304 while(first < last - 1 && !province::state_is_coastal(state, result[last - 1])) {
2305 --last;
2306 }
2307 if(first < last - 1) {
2308 std::swap(result[first], result[last - 1]);
2309 ++first;
2310 --last;
2311 }
2312 }
2313 std::sort(result.begin(), result.begin() + first, [&](dcon::state_instance_id a, dcon::state_instance_id b) {
2314 auto a_distance = province::sorting_distance(state, state.world.state_instance_get_capital(a), distance_from);
2315 auto b_distance = province::sorting_distance(state, state.world.state_instance_get_capital(b), distance_from);
2316 if(a_distance != b_distance)
2317 return a_distance < b_distance;
2318 else
2319 return a.index() < b.index();
2320 });
2321 }
2322 if(first < int32_t(result.size())) {
2323 std::sort(result.begin() + first, result.end(), [&](dcon::state_instance_id a, dcon::state_instance_id b) {
2324 auto a_distance = province::sorting_distance(state, state.world.state_instance_get_capital(a), distance_from);
2325 auto b_distance = province::sorting_distance(state, state.world.state_instance_get_capital(b), distance_from);
2326 if(a_distance != b_distance)
2327 return a_distance < b_distance;
2328 else
2329 return a.index() < b.index();
2330 });
2331 }
2332}
2333
2335 if(state.current_crisis_state == sys::crisis_state::inactive) {
2336 return;
2337 }
2338
2339 auto str_est = estimate_crisis_str(state);
2340
2341 if(state.crisis_temperature > 75.f || str_est.fast_victory) { // make peace offer
2342 auto any_victory = str_est.attacker_win || str_est.defender_win;
2343 auto defender_victory = str_est.defender_win;
2344
2345 if(state.world.nation_get_is_player_controlled(state.primary_crisis_attacker) == false && any_victory) {
2346 assert(command::can_start_crisis_peace_offer(state, state.primary_crisis_attacker, defender_victory));
2347 command::execute_start_crisis_peace_offer(state, state.primary_crisis_attacker, defender_victory);
2348 auto pending = state.world.nation_get_peace_offer_from_pending_peace_offer(state.primary_crisis_attacker);
2349
2350 auto wargoalslist = (defender_victory) ? state.crisis_defender_wargoals : state.crisis_attacker_wargoals;
2351 for(auto swg : wargoalslist) {
2352 if(!swg.cb) {
2353 break;
2354 }
2355 auto wg = fatten(state.world, state.world.create_wargoal());
2356 wg.set_peace_offer_from_peace_offer_item(pending);
2357 wg.set_added_by(swg.added_by);
2358 wg.set_associated_state(swg.state);
2359 wg.set_associated_tag(swg.wg_tag);
2360 wg.set_secondary_nation(swg.secondary_nation);
2361 wg.set_target_nation(swg.target_nation);
2362 wg.set_type(swg.cb);
2363 assert(command::can_add_to_crisis_peace_offer(state, state.primary_crisis_attacker, swg.added_by, swg.target_nation,
2364 swg.cb, swg.state, swg.wg_tag, swg.secondary_nation));
2365 }
2366 assert(command::can_send_crisis_peace_offer(state, state.primary_crisis_attacker));
2367 command::execute_send_crisis_peace_offer(state, state.primary_crisis_attacker);
2368 } else if(state.world.nation_get_is_player_controlled(state.primary_crisis_defender) == false && any_victory) {
2369 assert(command::can_start_crisis_peace_offer(state, state.primary_crisis_defender, !defender_victory));
2370 command::execute_start_crisis_peace_offer(state, state.primary_crisis_defender, !defender_victory);
2371 auto pending = state.world.nation_get_peace_offer_from_pending_peace_offer(state.primary_crisis_defender);
2372
2373 auto wargoalslist = (defender_victory) ? state.crisis_defender_wargoals : state.crisis_attacker_wargoals;
2374 for(auto swg : wargoalslist) {
2375 if(!swg.cb) {
2376 break;
2377 }
2378 assert(command::can_add_to_crisis_peace_offer(state, state.primary_crisis_defender, swg.added_by, swg.target_nation,
2379 swg.cb, swg.state, swg.wg_tag, swg.secondary_nation));
2380 auto wg = fatten(state.world, state.world.create_wargoal());
2381 wg.set_peace_offer_from_peace_offer_item(pending);
2382 wg.set_added_by(swg.added_by);
2383 wg.set_associated_state(swg.state);
2384 wg.set_associated_tag(swg.wg_tag);
2385 wg.set_secondary_nation(swg.secondary_nation);
2386 wg.set_target_nation(swg.target_nation);
2387 wg.set_type(swg.cb);
2388
2389 }
2390 assert(command::can_send_crisis_peace_offer(state, state.primary_crisis_defender));
2391 command::execute_send_crisis_peace_offer(state, state.primary_crisis_defender);
2392 }
2393 } else if(state.crisis_temperature > 20.f) { // recruit nations
2394 if(str_est.attacker < str_est.defender && state.world.nation_get_is_player_controlled(state.primary_crisis_attacker) == false) {
2395 for(auto& par : state.crisis_participants) {
2396 if(!par.id)
2397 break;
2398 if(par.merely_interested) {
2399 auto other_cbs = state.world.nation_get_available_cbs(par.id);
2400 dcon::cb_type_id offer_cb;
2401 dcon::nation_id target;
2402
2403 [&]() {
2404 for(auto& op_par : state.crisis_participants) {
2405 if(!op_par.id)
2406 break;
2407 if(!op_par.merely_interested && op_par.supports_attacker == false) {
2408 for(auto& cb : other_cbs) {
2409 if(cb.target == op_par.id && ai_offer_cb(state, cb.cb_type) && military::cb_conditions_satisfied(state, par.id, op_par.id, cb.cb_type)) {
2410 offer_cb = cb.cb_type;
2411 target = op_par.id;
2412 return;
2413 }
2414 }
2415 for(auto cb : state.world.in_cb_type) {
2416 if((cb.get_type_bits() & military::cb_flag::always) != 0) {
2417 if(ai_offer_cb(state, cb) && military::cb_conditions_satisfied(state, par.id, op_par.id, cb)) {
2418 offer_cb = cb;
2419 target = op_par.id;
2420 return;
2421 }
2422 }
2423 }
2424 }
2425 }
2426 }();
2427
2428 if(offer_cb) {
2429 if(military::cb_requires_selection_of_a_state(state, offer_cb)) {
2430 std::vector < dcon::state_instance_id> potential_states;
2431 state_target_list(potential_states, state, par.id, target);
2432 for(auto s : potential_states) {
2433 if(military::cb_instance_conditions_satisfied(state, par.id, target, offer_cb, state.world.state_instance_get_definition(s), dcon::national_identity_id{}, dcon::nation_id{})) {
2434
2436 memset(&m, 0, sizeof(diplomatic_message::message));
2437 m.to = par.id;
2438 m.from = state.primary_crisis_attacker;
2439 m.data.crisis_offer.added_by = m.to;
2440 m.data.crisis_offer.target_nation = target;
2441 m.data.crisis_offer.secondary_nation = dcon::nation_id{};
2442 m.data.crisis_offer.state = state.world.state_instance_get_definition(s);
2443 m.data.crisis_offer.wg_tag = dcon::national_identity_id{};
2444 m.data.crisis_offer.cb = offer_cb;
2446 diplomatic_message::post(state, m);
2447
2448 break;
2449 }
2450 }
2451 } else {
2453 memset(&m, 0, sizeof(diplomatic_message::message));
2454 m.to = par.id;
2455 m.from = state.primary_crisis_attacker;
2456 m.data.crisis_offer.added_by = m.to;
2457 m.data.crisis_offer.target_nation = target;
2458 m.data.crisis_offer.secondary_nation = dcon::nation_id{};
2459 m.data.crisis_offer.state = dcon::state_definition_id{};
2460 m.data.crisis_offer.wg_tag = dcon::national_identity_id{};
2461 m.data.crisis_offer.cb = offer_cb;
2463 diplomatic_message::post(state, m);
2464 }
2465 }
2466 }
2467 }
2468 } else if(str_est.attacker < str_est.defender && state.world.nation_get_is_player_controlled(state.primary_crisis_defender) == false) {
2469 for(auto& par : state.crisis_participants) {
2470 if(!par.id)
2471 break;
2472 if(par.merely_interested) {
2473 auto other_cbs = state.world.nation_get_available_cbs(par.id);
2474 dcon::cb_type_id offer_cb;
2475 dcon::nation_id target;
2476
2477 [&]() {
2478 for(auto& op_par : state.crisis_participants) {
2479 if(!op_par.id)
2480 break;
2481 if(!op_par.merely_interested && op_par.supports_attacker == true) {
2482 for(auto& cb : other_cbs) {
2483 if(cb.target == op_par.id && ai_offer_cb(state, cb.cb_type) && military::cb_conditions_satisfied(state, par.id, op_par.id, cb.cb_type)) {
2484 offer_cb = cb.cb_type;
2485 target = op_par.id;
2486 return;
2487 }
2488 }
2489 for(auto cb : state.world.in_cb_type) {
2490 if((cb.get_type_bits() & military::cb_flag::always) != 0) {
2491 if(ai_offer_cb(state, cb) && military::cb_conditions_satisfied(state, par.id, op_par.id, cb)) {
2492 offer_cb = cb;
2493 target = op_par.id;
2494 return;
2495 }
2496 }
2497 }
2498 }
2499 }
2500 }();
2501
2502 if(offer_cb) {
2503 if(military::cb_requires_selection_of_a_state(state, offer_cb)) {
2504 std::vector < dcon::state_instance_id> potential_states;
2505 state_target_list(potential_states, state, par.id, target);
2506 for(auto s : potential_states) {
2507 if(military::cb_instance_conditions_satisfied(state, par.id, target, offer_cb, state.world.state_instance_get_definition(s), dcon::national_identity_id{}, dcon::nation_id{})) {
2508
2510 memset(&m, 0, sizeof(diplomatic_message::message));
2511 m.to = par.id;
2512 m.from = state.primary_crisis_defender;
2513 m.data.crisis_offer.added_by = m.to;
2514 m.data.crisis_offer.target_nation = target;
2515 m.data.crisis_offer.secondary_nation = dcon::nation_id{};
2516 m.data.crisis_offer.state = state.world.state_instance_get_definition(s);
2517 m.data.crisis_offer.wg_tag = dcon::national_identity_id{};
2518 m.data.crisis_offer.cb = offer_cb;
2520 diplomatic_message::post(state, m);
2521
2522 break;
2523 }
2524 }
2525 } else {
2527 memset(&m, 0, sizeof(diplomatic_message::message));
2528 m.to = par.id;
2529 m.from = state.primary_crisis_defender;
2530 m.data.crisis_offer.added_by = m.to;
2531 m.data.crisis_offer.target_nation = target;
2532 m.data.crisis_offer.secondary_nation = dcon::nation_id{};
2533 m.data.crisis_offer.state = dcon::state_definition_id{};
2534 m.data.crisis_offer.wg_tag = dcon::national_identity_id{};
2535 m.data.crisis_offer.cb = offer_cb;
2537 diplomatic_message::post(state, m);
2538 }
2539 }
2540 }
2541 }
2542 }
2543 }
2544}
2545
2546bool will_accept_crisis_peace_offer(sys::state& state, dcon::nation_id to, bool is_concession, bool missing_wg) {
2547 if(state.crisis_temperature < 50.0f)
2548 return false;
2549
2550 auto str_est = estimate_crisis_str(state);
2551
2552 if(to == state.primary_crisis_attacker) {
2553 if(str_est.attacker < str_est.defender * 0.66f)
2554 return true;
2555 if(str_est.attacker < str_est.defender * 0.75f)
2556 return is_concession;
2557
2558 if(!is_concession)
2559 return false;
2560
2561 dcon::nation_id attacker = state.primary_crisis_attacker;
2562
2563 if(missing_wg)
2564 return false;
2565
2566 return true;
2567 } else if(to == state.primary_crisis_defender) {
2568 if(str_est.defender < str_est.attacker * 0.66f)
2569 return true;
2570 if(str_est.defender < str_est.attacker * 0.75f)
2571 return is_concession;
2572
2573 if(!is_concession)
2574 return false;
2575
2576 if(missing_wg)
2577 return false;
2578
2579 return true;
2580 }
2581 return false;
2582}
2583
2584bool will_accept_crisis_peace_offer(sys::state& state, dcon::nation_id to, dcon::peace_offer_id peace) {
2585 if(state.crisis_temperature < 50.0f)
2586 return false;
2587
2588 auto str_est = estimate_crisis_str(state);
2589
2590 if(to == state.primary_crisis_attacker) {
2591 if(str_est.attacker < str_est.defender * 0.66f)
2592 return true;
2593 if(str_est.attacker < str_est.defender * 0.75f)
2594 return state.world.peace_offer_get_is_concession(peace);
2595
2596 if(!state.world.peace_offer_get_is_concession(peace))
2597 return false;
2598
2599 bool missing_wg = false;
2600 for(auto swg : state.crisis_attacker_wargoals) {
2601 bool found_wg = false;
2602 for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) {
2603 auto wg = item.get_wargoal();
2604 if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation &&
2605 wg.get_associated_tag() == swg.wg_tag) {
2606 found_wg = true; continue;
2607 }
2608 }
2609
2610 if(!found_wg) {
2611 return false;
2612 }
2613 }
2614 return true;
2615
2616 } else if(to == state.primary_crisis_defender) {
2617 if(str_est.defender < str_est.attacker * 0.66f)
2618 return true;
2619 if(str_est.defender < str_est.attacker * 0.75f)
2620 return state.world.peace_offer_get_is_concession(peace);
2621
2622 if(!state.world.peace_offer_get_is_concession(peace))
2623 return false;
2624
2625
2626 bool missing_wg = false;
2627 for(auto swg : state.crisis_defender_wargoals) {
2628 bool found_wg = false;
2629 for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) {
2630 auto wg = item.get_wargoal();
2631 if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation &&
2632 wg.get_associated_tag() == swg.wg_tag) {
2633 found_wg = true; continue;
2634 }
2635 }
2636
2637 if(!found_wg) {
2638 return false;
2639 }
2640 }
2641
2642 return true;
2643 }
2644 return false;
2645}
2646
2648 for(auto& gp : state.great_nations) {
2649 if(state.world.nation_get_is_player_controlled(gp.nation) == false && state.world.nation_get_is_at_war(gp.nation) == false){
2650 bool as_attacker = false;
2651 dcon::war_id intervention_target;
2652 [&]() {
2653 for(auto w : state.world.in_war) {
2654 //GPs will try to intervene in wars to protect smaller nations in the same cultural union
2655 if(command::can_intervene_in_war(state, gp.nation, w, false)) {
2656 auto par = state.world.war_get_primary_defender(w);
2657 if(state.world.nation_get_primary_culture(gp.nation).get_group_from_culture_group_membership() == state.world.nation_get_primary_culture(par).get_group_from_culture_group_membership()
2658 && !nations::is_great_power(state, par)
2659 ){
2660 intervention_target = w;
2661 return;
2662 }
2663 }
2664 if(w.get_is_great()) {
2665 if(command::can_intervene_in_war(state, gp.nation, w, false)) {
2666 for(auto par : w.get_war_participant()) {
2667 if(par.get_is_attacker() && military::can_use_cb_against(state, gp.nation, par.get_nation())) {
2668 intervention_target = w;
2669 return;
2670 }
2671 }
2672 }
2673 if(command::can_intervene_in_war(state, gp.nation, w, true)) {
2674 for(auto par : w.get_war_participant()) {
2675 if(!par.get_is_attacker() && military::can_use_cb_against(state, gp.nation, par.get_nation())) {
2676 intervention_target = w;
2677 as_attacker = true;
2678 return;
2679 }
2680 }
2681 }
2682 } else if(military::get_role(state, w, state.world.nation_get_ai_rival(gp.nation)) == military::war_role::attacker) {
2683 if(command::can_intervene_in_war(state, gp.nation, w, false)) {
2684 intervention_target = w;
2685 return;
2686 }
2687 }
2688 }
2689 }();
2690 if(intervention_target) {
2691 assert(command::can_intervene_in_war(state, gp.nation, intervention_target, as_attacker));
2692 command::execute_intervene_in_war(state, gp.nation, intervention_target, as_attacker);
2693 }
2694 }
2695 }
2696}
2697
2699 dcon::nation_id target;
2700 dcon::nation_id secondary_nation;
2701 dcon::national_identity_id associated_tag;
2702 dcon::state_definition_id state_def;
2703 dcon::cb_type_id cb;
2704};
2705
2706/* Filter out target_states in the target nation */
2707void place_instance_in_result(sys::state& state, std::vector<possible_cb>& result, dcon::nation_id n, dcon::nation_id target, dcon::cb_type_id cb, std::vector<dcon::state_instance_id> const& target_states) {
2708 auto can_use = state.world.cb_type_get_can_use(cb);
2709 auto allowed_substates = state.world.cb_type_get_allowed_substate_regions(cb);
2710
2711 if(allowed_substates) {
2712 if(!state.world.nation_get_is_substate(target))
2713 return;
2714 auto ruler = state.world.overlord_get_ruler(state.world.nation_get_overlord_as_subject(target));
2715 if(can_use && !trigger::evaluate(state, can_use, trigger::to_generic(ruler), trigger::to_generic(n), trigger::to_generic(ruler))) {
2716 return;
2717 }
2718 } else {
2719 if(can_use && !trigger::evaluate(state, can_use, trigger::to_generic(target), trigger::to_generic(n), trigger::to_generic(target))) {
2720 return;
2721 }
2722 }
2723
2724 auto allowed_countries = state.world.cb_type_get_allowed_countries(cb);
2725 auto allowed_states = state.world.cb_type_get_allowed_states(cb);
2726
2727 if(!allowed_countries && allowed_states) {
2728 bool any_allowed = false;
2729 for(auto si : target_states) {
2730 if(trigger::evaluate(state, allowed_states, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(n))) {
2731 assert(military::cb_conditions_satisfied(state, n, target, cb));
2732 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
2733 return;
2734 }
2735 }
2736 return;
2737 } else if(allowed_substates) { // checking for whether the target is a substate is already done above
2738 for(auto si : target_states) {
2739 if(trigger::evaluate(state, allowed_substates, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(n))) {
2740 assert(military::cb_conditions_satisfied(state, n, target, cb));
2741 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
2742 return;
2743 }
2744 }
2745 return;
2746 } else if(allowed_countries) {
2747 bool liberate = (state.world.cb_type_get_type_bits(cb) & military::cb_flag::po_transfer_provinces) != 0;
2748 for(auto other_nation : state.world.in_nation) {
2749 if(other_nation != n) {
2750 if(trigger::evaluate(state, allowed_countries, trigger::to_generic(target), trigger::to_generic(n),
2751 trigger::to_generic(other_nation.id))) {
2752 if(allowed_states) { // check whether any state within the target is valid for free / liberate
2753 for(auto i = target_states.size(); i-- > 0;) {
2754 auto si = target_states[i];
2755 if(trigger::evaluate(state, allowed_states, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(other_nation.id))) {
2756 if(liberate) {
2757 assert(military::cb_conditions_satisfied(state, n, target, cb));
2758 result.push_back(possible_cb{ target, dcon::nation_id{}, other_nation.get_identity_from_identity_holder(), state.world.state_instance_get_definition(si), cb });
2759 return;
2760 } else {
2761 assert(military::cb_conditions_satisfied(state, n, target, cb));
2762 result.push_back(possible_cb{ target, other_nation, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
2763 return;
2764 }
2765 }
2766 }
2767 } else { // no allowed states trigger
2768 if(liberate) {
2769 assert(military::cb_conditions_satisfied(state, n, target, cb));
2770 result.push_back(possible_cb{ target, dcon::nation_id{}, other_nation.get_identity_from_identity_holder(), dcon::state_definition_id{}, cb });
2771 return;
2772 } else {
2773 assert(military::cb_conditions_satisfied(state, n, target, cb));
2774 result.push_back(possible_cb{ target, other_nation, dcon::national_identity_id{}, dcon::state_definition_id{}, cb });
2775 return;
2776 }
2777 }
2778 }
2779 }
2780 }
2781 return;
2782 } else {
2783 assert(military::cb_conditions_satisfied(state, n, target, cb));
2784 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, dcon::state_definition_id{}, cb });
2785 return;
2786 }
2787}
2788
2789void sort_possible_justification_cbs(std::vector<possible_cb>& result, sys::state& state, dcon::nation_id n, dcon::nation_id target) {
2790 result.clear();
2791
2792 static std::vector<dcon::state_instance_id> target_states;
2793 state_target_list(target_states, state, n, target);
2794
2795 // AI can go after po_demand_state and po_annex
2796 for(auto cb : state.world.in_cb_type) {
2797 auto bits = state.world.cb_type_get_type_bits(cb);
2799 continue;
2800
2801 if(!military::cb_conditions_satisfied(state, n, target, cb))
2802 continue;
2803 auto sl = state.world.nation_get_in_sphere_of(target);
2804 if(sl == n)
2805 continue;
2806
2807 place_instance_in_result(state, result, n, target, cb, target_states);
2808 }
2809
2810 // Prioritize CB types
2811 std::sort(result.begin(), result.end(), [&](possible_cb const& a, possible_cb const& b) {
2812 if((state.world.nation_get_ai_rival(n) == a.target) != (state.world.nation_get_ai_rival(n) == b.target)) {
2813 return state.world.nation_get_ai_rival(n) == a.target;
2814 }
2815
2816 auto a_annexes = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_annex) != 0;
2817 auto b_annexes = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_annex) != 0;
2818 if(a_annexes != b_annexes)
2819 return a_annexes;
2820
2821 auto a_land = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_demand_state) != 0;
2822 auto b_land = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_demand_state) != 0;
2823
2824 if(a_land < b_land)
2825 return a_land;
2826
2827 auto rel_a = state.world.get_diplomatic_relation_by_diplomatic_pair(n, a.target);
2828 auto rel_b = state.world.get_diplomatic_relation_by_diplomatic_pair(n, b.target);
2829 if(state.world.diplomatic_relation_get_value(rel_a) != state.world.diplomatic_relation_get_value(rel_b))
2830 return state.world.diplomatic_relation_get_value(rel_a) < state.world.diplomatic_relation_get_value(rel_b);
2831
2832 if(a.cb != b.cb)
2833 return a.cb.index() < b.cb.index();
2834 if(a.target != b.target)
2835 return a.target.index() < b.target.index();
2837 return a.secondary_nation.index() < b.secondary_nation.index();
2839 return a.associated_tag.index() < b.associated_tag.index();
2840 return a.state_def.index() < b.state_def.index();
2841 });
2842}
2843
2844possible_cb pick_fabrication_type(sys::state& state, dcon::nation_id from, dcon::nation_id target) {
2845 static std::vector<possible_cb> possibilities;
2846
2847 sort_possible_justification_cbs(possibilities, state, from, target);
2848 // Uncivilized nations are more aggressive to westernize faster
2849 float infamy_limit = state.world.nation_get_is_civilized(from) ? state.defines.badboy_limit / 2.f : state.defines.badboy_limit;
2850
2851 if(!possibilities.empty()) {
2852 // AI only goes for top priority choice
2853 auto possibility = possibilities[0];
2854
2855 // AI doesn't go over infamy limit;
2856 if(military::cb_requires_selection_of_a_state(state, possibility.cb)) {
2857 if(state.world.nation_get_infamy(from) + military::cb_infamy(state, possibility.cb, target, possibility.state_def) > infamy_limit)
2858 return possible_cb{};
2859 } else {
2860 if(state.world.nation_get_infamy(from) + military::cb_infamy(state, possibility.cb, target) > infamy_limit)
2861 return possible_cb{};
2862 }
2863
2864 // Make sure no country higher than originator on rank is justifying on that target
2865 for (auto n : state.nations_by_rank) {
2866 if(n == from) {
2867 break;
2868 }
2869
2870 auto is_discovered = state.world.nation_get_constructing_cb_is_discovered(n);
2871
2872 if(!is_discovered) {
2873 continue;
2874 }
2875
2876 auto target_nation = state.world.nation_get_constructing_cb_target(n);
2877 auto target_state = state.world.nation_get_constructing_cb_target_state(n);
2878
2879 if(possibility.target == target_nation && possibility.state_def == target_state) {
2880 return possible_cb{};
2881 }
2882 }
2883
2884 return possibility;
2885 } else {
2886 return possible_cb{};
2887 }
2888}
2889
2890bool valid_construction_target(sys::state& state, dcon::nation_id from, dcon::nation_id target) {
2891 // Copied from commands.cpp:can_fabricate_cb()
2892 if(from == target)
2893 return false;
2894 if(state.world.nation_get_constructing_cb_type(from))
2895 return false;
2896 auto ol = state.world.nation_get_overlord_as_subject(from);
2897 if(state.world.overlord_get_ruler(ol) && state.world.overlord_get_ruler(ol) != target)
2898 return false;
2899 if(state.world.nation_get_in_sphere_of(target) == from)
2900 return false;
2901 if(military::are_at_war(state, target, from))
2902 return false;
2903 if(military::has_truce_with(state, target, from))
2904 return false;
2905 auto sl = state.world.nation_get_in_sphere_of(target);
2906 if(sl == from)
2907 return false;
2908 // Its easy to defeat a nation at war
2909 if(state.world.nation_get_is_at_war(target)) {
2910 return estimate_strength(state, from) >= estimate_strength(state, target) * 0.15f;
2911 } else {
2912 if(estimate_strength(state, from) < estimate_strength(state, target) * 0.66f)
2913 return false;
2914 }
2915 if(state.world.nation_get_owned_province_count(target) <= 2)
2916 return false;
2917 // Attacking people from other continents only if we have naval superiority
2918 if(state.world.province_get_continent(state.world.nation_get_capital(from)) != state.world.province_get_continent(state.world.nation_get_capital(target))) {
2919 // We must achieve naval superiority to even invade them
2920 if(state.world.nation_get_capital_ship_score(from) < std::max(1.f, 1.5f * state.world.nation_get_capital_ship_score(target)))
2921 return false;
2922 }
2923 return true;
2924}
2925
2927 for(auto nid : state.nations_by_rank) {
2928 auto n = dcon::fatten(state.world, nid);
2929
2930 if(!n.get_is_player_controlled() && n.get_owned_province_count() > 0) {
2931 if(n.get_is_at_war())
2932 continue;
2933 // Uncivilized nations are more aggressive to westernize faster
2934 float infamy_limit = state.world.nation_get_is_civilized(n) ? state.defines.badboy_limit / 2.f : state.defines.badboy_limit;
2935 if(n.get_infamy() > infamy_limit)
2936 continue;
2937 if(n.get_constructing_cb_type())
2938 continue;
2939 auto ol = n.get_overlord_as_subject().get_ruler().id;
2940 if(n.get_ai_rival()
2941 && n.get_ai_rival().get_in_sphere_of() != n
2942 && (!ol || ol == n.get_ai_rival())
2943 && !military::are_at_war(state, n, n.get_ai_rival())
2944 && !military::can_use_cb_against(state, n, n.get_ai_rival())
2945 && !military::has_truce_with(state, n, n.get_ai_rival())) {
2946
2947 auto cb = pick_fabrication_type(state, n, n.get_ai_rival());
2948 if(cb.cb) {
2949 n.set_constructing_cb_target(n.get_ai_rival());
2950 n.set_constructing_cb_type(cb.cb);
2951 n.set_constructing_cb_target_state(cb.state_def);
2952 continue;
2953 }
2954 }
2955
2956 static std::vector<dcon::nation_id> possible_targets;
2957 possible_targets.clear();
2958 for(auto i : state.world.in_nation) {
2959 if(valid_construction_target(state, n, i)
2960 && !military::has_truce_with(state, n, i)) {
2961 possible_targets.push_back(i.id);
2962 if(!i.get_is_civilized())
2963 possible_targets.push_back(i.id); //twice the chance!
2964 }
2965 }
2966 if(!possible_targets.empty()) {
2967 auto t = possible_targets[rng::reduce(uint32_t(rng::get_random(state, uint32_t(n.id.index())) >> 2), uint32_t(possible_targets.size()))];
2968 if(auto pcb = pick_fabrication_type(state, n, t); pcb.cb) {
2969 n.set_constructing_cb_target(t);
2970 n.set_constructing_cb_type(pcb.cb);
2971 n.set_constructing_cb_target_state(pcb.state_def);
2972 continue;
2973 }
2974 }
2975 }
2976 }
2977}
2978
2979bool will_join_war(sys::state& state, dcon::nation_id n, dcon::war_id w, bool as_attacker) {
2980 /* Forbid war between uncivs against civs of other continent except if they are a sphereling or substate */
2981 if(bool(state.defines.alice_unciv_civ_forbid_war) && !state.world.nation_get_is_civilized(n)
2982 && !state.world.nation_get_is_substate(n) && !state.world.nation_get_in_sphere_of(n)
2983 && !state.world.overlord_get_ruler(state.world.nation_get_overlord_as_subject(n))) {
2984 auto pa = state.world.war_get_primary_attacker(w);
2985 auto pd = state.world.war_get_primary_defender(w);
2986 auto pa_cap = state.world.nation_get_capital(pa);
2987 auto pd_cap = state.world.nation_get_capital(pd);
2988 auto cap = state.world.nation_get_capital(n);
2989 if(state.world.province_get_continent(pa_cap) != state.world.province_get_continent(cap))
2990 return false;
2991 if(state.world.province_get_continent(pd_cap) != state.world.province_get_continent(cap))
2992 return false;
2993 }
2994
2995 if(!as_attacker)
2996 return true;
2997 for(auto par : state.world.war_get_war_participant(w)) {
2998 if(par.get_is_attacker() == !as_attacker) {
2999 // Could use a CB against this nation?
3000 if(military::can_use_cb_against(state, n, par.get_nation()))
3001 return true;
3002 // Eager to absolutely demolish our rival if possible
3003 if(state.world.nation_get_ai_rival(n) == par.get_nation())
3004 return true;
3005 }
3006 }
3007 return false;
3008}
3009
3010void place_instance_in_result_war(sys::state& state, std::vector<possible_cb>& result, dcon::nation_id n, dcon::nation_id target, dcon::war_id w, dcon::cb_type_id cb, std::vector<dcon::state_instance_id> const& target_states) {
3011 auto can_use = state.world.cb_type_get_can_use(cb);
3012 auto allowed_substates = state.world.cb_type_get_allowed_substate_regions(cb);
3013 if(allowed_substates) {
3014 if(!state.world.nation_get_is_substate(target))
3015 return;
3016 auto ruler = state.world.overlord_get_ruler(state.world.nation_get_overlord_as_subject(target));
3017 if(can_use && !trigger::evaluate(state, can_use, trigger::to_generic(ruler), trigger::to_generic(n), trigger::to_generic(ruler))) {
3018 return;
3019 }
3020 } else {
3021 if(can_use && !trigger::evaluate(state, can_use, trigger::to_generic(target), trigger::to_generic(n), trigger::to_generic(target))) {
3022 return;
3023 }
3024 }
3025
3026 auto allowed_countries = state.world.cb_type_get_allowed_countries(cb);
3027 auto allowed_states = state.world.cb_type_get_allowed_states(cb);
3028
3029 if(!allowed_countries && allowed_states) {
3030 for(auto si : target_states) {
3031 if(trigger::evaluate(state, allowed_states, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(n))) {
3032 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, state.world.state_instance_get_definition(si), dcon::national_identity_id{}, dcon::nation_id{})) {
3033 assert(military::cb_conditions_satisfied(state, n, target, cb));
3034 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
3035 return;
3036 }
3037 }
3038 }
3039 return;
3040 } else if(allowed_substates) { // checking for whether the target is a substate is already done above
3041 for(auto si : target_states) {
3042 if(trigger::evaluate(state, allowed_substates, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(n))) {
3043 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, state.world.state_instance_get_definition(si), dcon::national_identity_id{}, dcon::nation_id{})) {
3044 assert(military::cb_conditions_satisfied(state, n, target, cb));
3045 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
3046 return;
3047 }
3048 }
3049 }
3050 return;
3051 } else if(allowed_countries) {
3052 bool liberate = (state.world.cb_type_get_type_bits(cb) & military::cb_flag::po_transfer_provinces) != 0;
3053 for(auto other_nation : state.world.in_nation) {
3054 if(other_nation != n) {
3055 if(trigger::evaluate(state, allowed_countries, trigger::to_generic(target), trigger::to_generic(n), trigger::to_generic(other_nation.id))) {
3056 if(allowed_states) { // check whether any state within the target is valid for free / liberate
3057 for(auto i = target_states.size(); i-- > 0;) {
3058 auto si = target_states[i];
3059 if(trigger::evaluate(state, allowed_states, trigger::to_generic(si), trigger::to_generic(n), trigger::to_generic(other_nation.id))) {
3060 if(liberate) {
3061 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, state.world.state_instance_get_definition(si), other_nation.get_identity_from_identity_holder(), dcon::nation_id{})) {
3062 assert(military::cb_conditions_satisfied(state, n, target, cb));
3063 result.push_back(possible_cb{ target, dcon::nation_id{}, other_nation.get_identity_from_identity_holder(), state.world.state_instance_get_definition(si), cb });
3064 return;
3065 }
3066 } else {
3067 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, state.world.state_instance_get_definition(si), dcon::national_identity_id{}, other_nation)) {
3068 assert(military::cb_conditions_satisfied(state, n, target, cb));
3069 result.push_back(possible_cb{ target, other_nation, dcon::national_identity_id{}, state.world.state_instance_get_definition(si), cb });
3070 return;
3071 }
3072 }
3073 }
3074 }
3075 } else { // no allowed states trigger
3076 if(liberate) {
3077 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, dcon::state_definition_id{}, other_nation.get_identity_from_identity_holder(), dcon::nation_id{})) {
3078 assert(military::cb_conditions_satisfied(state, n, target, cb));
3079 result.push_back(possible_cb{ target, dcon::nation_id{}, other_nation.get_identity_from_identity_holder(), dcon::state_definition_id{}, cb });
3080 return;
3081 }
3082 } else {
3083 if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, dcon::state_definition_id{}, dcon::national_identity_id{}, other_nation)) {
3084 assert(military::cb_conditions_satisfied(state, n, target, cb));
3085 result.push_back(possible_cb{ target, other_nation, dcon::national_identity_id{}, dcon::state_definition_id{}, cb });
3086 return;
3087 }
3088 }
3089 }
3090 }
3091 }
3092 }
3093 return;
3094 } else if(!military::war_goal_would_be_duplicate(state, n, w, target, cb, dcon::state_definition_id{}, dcon::national_identity_id{}, dcon::nation_id{})) {
3095 assert(military::cb_conditions_satisfied(state, n, target, cb));
3096 result.push_back(possible_cb{ target, dcon::nation_id{}, dcon::national_identity_id{}, dcon::state_definition_id{}, cb });
3097 return;
3098 }
3099}
3100
3101void sort_available_cbs(std::vector<possible_cb>& result, sys::state& state, dcon::nation_id n, dcon::war_id w) {
3102 result.clear();
3103
3104 auto is_attacker = military::get_role(state, w, n) == military::war_role::attacker;
3105 for(auto par : state.world.war_get_war_participant(w)) {
3106 if(par.get_is_attacker() != is_attacker) {
3107 static std::vector<dcon::state_instance_id> target_states;
3108 state_target_list(target_states, state, n, par.get_nation());
3109 for(auto& cb : state.world.nation_get_available_cbs(n)) {
3110 if(cb.target == par.get_nation())
3111 place_instance_in_result_war(state, result, n, par.get_nation(), w, cb.cb_type, target_states);
3112 }
3113 for(auto cb : state.world.in_cb_type) {
3114 if((cb.get_type_bits() & military::cb_flag::always) != 0) {
3115 place_instance_in_result_war(state, result, n, par.get_nation(), w, cb, target_states);
3116 }
3117 }
3118 }
3119 }
3120
3121 std::sort(result.begin(), result.end(), [&](possible_cb const& a, possible_cb const& b) {
3122 if((state.world.nation_get_ai_rival(n) == a.target) != (state.world.nation_get_ai_rival(n) == b.target)) {
3123 return state.world.nation_get_ai_rival(n) == a.target;
3124 }
3125
3126 auto a_annexes = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_annex) != 0;
3127 auto b_annexes = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_annex) != 0;
3128 if(a_annexes != b_annexes)
3129 return a_annexes;
3130
3131 auto a_land = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_demand_state) != 0;
3132 auto b_land = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_demand_state) != 0;
3133
3134 if(a_land < b_land)
3135 return a_land;
3136
3137 auto rel_a = state.world.get_diplomatic_relation_by_diplomatic_pair(n, a.target);
3138 auto rel_b = state.world.get_diplomatic_relation_by_diplomatic_pair(n, b.target);
3139 if(state.world.diplomatic_relation_get_value(rel_a) != state.world.diplomatic_relation_get_value(rel_b))
3140 return state.world.diplomatic_relation_get_value(rel_a) < state.world.diplomatic_relation_get_value(rel_b);
3141
3142 if(a.cb != b.cb)
3143 return a.cb.index() < b.cb.index();
3144 if(a.target != b.target)
3145 return a.target.index() < b.target.index();
3147 return a.secondary_nation.index() < b.secondary_nation.index();
3149 return a.associated_tag.index() < b.associated_tag.index();
3150 return a.state_def.index() < b.state_def.index();
3151 });
3152}
3153
3154void sort_available_declaration_cbs(std::vector<possible_cb>& result, sys::state& state, dcon::nation_id n, dcon::nation_id target) {
3155 result.clear();
3156
3157 static std::vector<dcon::state_instance_id> target_states;
3158 state_target_list(target_states, state, n, target);
3159
3160 // Justified CBs
3161 auto other_cbs = state.world.nation_get_available_cbs(n);
3162 for(auto& cb : other_cbs) {
3163 if(cb.target == target)
3164 place_instance_in_result(state, result, n, target, cb.cb_type, target_states);
3165 }
3166 // Always available CBs (incl acquire_core_state and annex_core_country)
3167 for(auto cb : state.world.in_cb_type) {
3168 if((cb.get_type_bits() & military::cb_flag::always) != 0) {
3169 place_instance_in_result(state, result, n, target, cb, target_states);
3170 }
3171 }
3172
3173 // Prioritize CB types
3174 std::sort(result.begin(), result.end(), [&](possible_cb const& a, possible_cb const& b) {
3175 if((state.world.nation_get_ai_rival(n) == a.target) != (state.world.nation_get_ai_rival(n) == b.target)) {
3176 return state.world.nation_get_ai_rival(n) == a.target;
3177 }
3178
3179 auto a_annexes = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_annex) != 0;
3180 auto b_annexes = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_annex) != 0;
3181 if(a_annexes != b_annexes)
3182 return a_annexes;
3183
3184 auto a_land = (state.world.cb_type_get_type_bits(a.cb) & military::cb_flag::po_demand_state) != 0;
3185 auto b_land = (state.world.cb_type_get_type_bits(b.cb) & military::cb_flag::po_demand_state) != 0;
3186
3187 if(a_land < b_land)
3188 return a_land;
3189
3190 auto rel_a = state.world.get_diplomatic_relation_by_diplomatic_pair(n, a.target);
3191 auto rel_b = state.world.get_diplomatic_relation_by_diplomatic_pair(n, b.target);
3192 if(state.world.diplomatic_relation_get_value(rel_a) != state.world.diplomatic_relation_get_value(rel_b))
3193 return state.world.diplomatic_relation_get_value(rel_a) < state.world.diplomatic_relation_get_value(rel_b);
3194
3195 if(a.cb != b.cb)
3196 return a.cb.index() < b.cb.index();
3197 if(a.target != b.target)
3198 return a.target.index() < b.target.index();
3200 return a.secondary_nation.index() < b.secondary_nation.index();
3202 return a.associated_tag.index() < b.associated_tag.index();
3203 return a.state_def.index() < b.state_def.index();
3204 });
3205}
3206
3207void add_free_ai_cbs_to_war(sys::state& state, dcon::nation_id n, dcon::war_id w) {
3208 bool is_attacker = military::is_attacker(state, w, n);
3209 if(!is_attacker && military::defenders_have_status_quo_wargoal(state, w))
3210 return;
3211 if(is_attacker && military::attackers_have_status_quo_wargoal(state, w))
3212 return;
3213
3214 bool added = false;
3215 do {
3216 added = false;
3217 static std::vector<possible_cb> potential;
3218 sort_available_cbs(potential, state, n, w);
3219 for(auto& p : potential) {
3220 if(!military::war_goal_would_be_duplicate(state, n, w, p.target, p.cb, p.state_def, p.associated_tag, p.secondary_nation)) {
3221 military::add_wargoal(state, w, n, p.target, p.cb, p.state_def, p.associated_tag, p.secondary_nation);
3222 nations::adjust_relationship(state, n, p.target, state.defines.addwargoal_relation_on_accept);
3223 added = true;
3224 }
3225 }
3226 } while(added);
3227
3228}
3229
3230dcon::cb_type_id pick_gw_extra_cb_type(sys::state& state, dcon::nation_id from, dcon::nation_id target) {
3231 static std::vector<dcon::cb_type_id> possibilities;
3232 possibilities.clear();
3233
3234 auto free_infamy = state.defines.badboy_limit - state.world.nation_get_infamy(from);
3235
3236 for(auto c : state.world.in_cb_type) {
3237 auto bits = state.world.cb_type_get_type_bits(c);
3239 continue;
3241 continue;
3242 if(military::cb_infamy(state, c, target) * state.defines.gw_justify_cb_badboy_impact > free_infamy)
3243 continue;
3244 if(!military::cb_conditions_satisfied(state, from, target, c))
3245 continue;
3246
3247 possibilities.push_back(c);
3248 }
3249
3250 if(!possibilities.empty()) {
3251 return possibilities[rng::reduce(uint32_t(rng::get_random(state, uint32_t((from.index() << 3) ^ target.index()))), uint32_t(possibilities.size()))];
3252 } else {
3253 return dcon::cb_type_id{};
3254 }
3255}
3256
3257dcon::nation_id pick_gw_target(sys::state& state, dcon::nation_id from, dcon::war_id w, bool is_attacker) {
3258
3259 if(is_attacker && military::get_role(state, w, state.world.nation_get_ai_rival(from)) == military::war_role::defender)
3260 return state.world.nation_get_ai_rival(from);
3261 if(!is_attacker && military::get_role(state, w, state.world.nation_get_ai_rival(from)) == military::war_role::attacker)
3262 return state.world.nation_get_ai_rival(from);
3263
3264 static std::vector<dcon::nation_id> possibilities;
3265 possibilities.clear();
3266
3267 for(auto par : state.world.war_get_war_participant(w)) {
3268 if(par.get_is_attacker() != is_attacker) {
3269 if(state.world.get_nation_adjacency_by_nation_adjacency_pair(from, par.get_nation()))
3270 possibilities.push_back(par.get_nation().id);
3271 }
3272 }
3273 if(possibilities.empty()) {
3274 for(auto par : state.world.war_get_war_participant(w)) {
3275 if(par.get_is_attacker() != is_attacker) {
3276 if(nations::is_great_power(state, par.get_nation()))
3277 possibilities.push_back(par.get_nation().id);
3278 }
3279 }
3280 }
3281 if(!possibilities.empty()) {
3282 return possibilities[rng::reduce(uint32_t(rng::get_random(state, uint32_t(from.index() ^ 3))), uint32_t(possibilities.size()))];
3283 } else {
3284 return dcon::nation_id{};
3285 }
3286}
3287
3288void add_wg_to_great_war(sys::state& state, dcon::nation_id n, dcon::war_id w) {
3289 auto rval = rng::get_random(state, n.index() ^ w.index() << 2);
3290 if((rval & 1) == 0)
3291 return;
3292
3293 if(n == state.world.war_get_primary_attacker(w) || n == state.world.war_get_primary_defender(w)) {
3294 if(((rval >> 1) & 1) == 0) {
3295 add_free_ai_cbs_to_war(state, n, w);
3296 }
3297 }
3298
3299 auto totalpop = state.world.nation_get_demographics(n, demographics::total);
3300 auto jingoism_perc = totalpop > 0 ? state.world.nation_get_demographics(n, demographics::to_key(state, state.culture_definitions.jingoism)) / totalpop : 0.0f;
3301
3302 if(jingoism_perc < state.defines.wargoal_jingoism_requirement * state.defines.gw_wargoal_jingoism_requirement_mod)
3303 return;
3304
3305 bool attacker = military::get_role(state, w, n) == military::war_role::attacker;
3306 auto spare_ws = attacker ? (military::primary_warscore(state, w) - military::attacker_peace_cost(state, w)) : (-military::primary_warscore(state, w) - military::defender_peace_cost(state, w));
3307 if(spare_ws < 1.0f)
3308 return;
3309
3310 auto target = pick_gw_target(state, n, w, attacker);
3311 if(!target)
3312 return;
3313
3314 auto cb = pick_gw_extra_cb_type(state, n, target);
3315 if(!cb)
3316 return;
3317
3318 static std::vector<dcon::state_instance_id> target_states;
3319 state_target_list(target_states, state, n, target);
3320 static std::vector<possible_cb> result;
3321 result.clear();
3322 place_instance_in_result_war(state, result, n, target, w, cb, target_states);
3323 if(!result.empty() && result[0].target) {
3324 military::add_wargoal(state, w, n, target, cb, result[0].state_def, result[0].associated_tag, result[0].secondary_nation);
3325 nations::adjust_relationship(state, n, target, state.defines.addwargoal_relation_on_accept);
3326 state.world.nation_get_infamy(n) += military::cb_infamy(state, cb, target) * state.defines.gw_justify_cb_badboy_impact;
3327 }
3328}
3329
3331 for(auto w : state.world.in_war) {
3332 if(w.get_is_great()) {
3333 for(auto par : w.get_war_participant()) {
3334 if(par.get_nation().get_is_player_controlled() == false) {
3335 add_wg_to_great_war(state, par.get_nation(), w);
3336 }
3337 }
3338 }
3339 }
3340}
3341
3342bool has_cores_occupied(sys::state& state, dcon::nation_id n) {
3343 if(bool(state.defines.alice_surrender_on_cores_lost)) {
3344 auto i = state.world.nation_get_identity_from_identity_holder(n);
3345 auto cores = state.world.national_identity_get_core(i);
3346 bool has_owned_cores = false;
3347 for(auto c : cores) {
3348 if(c.get_province().get_nation_from_province_ownership() == n) {
3349 has_owned_cores = true;
3350 } if(c.get_province().get_nation_from_province_control() == n) {
3351 return false;
3352 }
3353 }
3354 auto pc = state.world.nation_get_province_control(n); //no cores or some cores are occupied but some are not
3355 return has_owned_cores || pc.begin() == pc.end(); //controls anything?
3356 }
3357 return false;
3358}
3359
3361 auto send_offer_up_to = [&](dcon::nation_id from, dcon::nation_id to, dcon::war_id w, bool attacker, int32_t score_max, bool concession) {
3362 if(auto off = state.world.nation_get_peace_offer_from_pending_peace_offer(from); off) {
3363 if(state.world.peace_offer_get_is_crisis_offer(off) == true || state.world.peace_offer_get_war_from_war_settlement(off))
3364 return; // offer in flight
3365 state.world.delete_peace_offer(off); // else -- offer has been already resolved and was just pending gc
3366 }
3367
3368 assert(command::can_start_peace_offer(state, from, to, w, concession));
3369 command::execute_start_peace_offer(state, from, to, w, concession);
3370 auto pending = state.world.nation_get_peace_offer_from_pending_peace_offer(from);
3371 if(!pending)
3372 return;
3373
3374 score_max = std::min(score_max, 100);
3375 int32_t current_value = 0;
3376 for(auto wg : state.world.war_get_wargoals_attached(w)) {
3377 if((military::is_attacker(state, w, wg.get_wargoal().get_added_by()) == attacker) == !concession) {
3378 auto goal_cost = military::peace_cost(state, w, wg.get_wargoal().get_type(), wg.get_wargoal().get_added_by(), wg.get_wargoal().get_target_nation(), wg.get_wargoal().get_secondary_nation(), wg.get_wargoal().get_associated_state(), wg.get_wargoal().get_associated_tag());
3379 if(current_value + goal_cost <= score_max) {
3380 current_value += goal_cost;
3381 state.world.force_create_peace_offer_item(pending, wg.get_wargoal().id);
3382 }
3383 }
3384 }
3385
3388 };
3389
3390 for(auto w : state.world.in_war) {
3391 if((w.get_primary_attacker().get_is_player_controlled() == false || w.get_primary_defender().get_is_player_controlled() == false)
3392 && w.get_primary_attacker().get_owned_province_count() > 0
3393 && w.get_primary_defender().get_owned_province_count() > 0) {
3394 //postpone until military gc does magic
3395 if(military::get_role(state, w, w.get_primary_attacker()) != military::war_role::attacker)
3396 continue;
3397 if(military::get_role(state, w, w.get_primary_defender()) != military::war_role::defender)
3398 continue;
3399
3400 auto overall_score = military::primary_warscore(state, w);
3401 if(overall_score >= 0) { // attacker winning
3402 bool defender_surrender = has_cores_occupied(state, w.get_primary_defender());
3403 auto total_po_cost = military::attacker_peace_cost(state, w);
3404 if(w.get_primary_attacker().get_is_player_controlled() == false) { // attacker makes offer
3405 if(defender_surrender || (overall_score >= 100 || (overall_score >= 50 && overall_score >= total_po_cost * 2))) {
3406 send_offer_up_to(w.get_primary_attacker(), w.get_primary_defender(), w, true, int32_t(overall_score), false);
3407 continue;
3408 }
3409 if(w.get_primary_defender().get_is_player_controlled() == false) {
3410 auto war_duration = state.current_date.value - state.world.war_get_start_date(w).value;
3411 if(war_duration >= 365) {
3412 float willingness_factor = float(war_duration - 365) * 10.f / 365.0f;
3413 if(defender_surrender || (overall_score > (total_po_cost - willingness_factor) && (-overall_score / 2 + total_po_cost - willingness_factor) < 0)) {
3414 send_offer_up_to(w.get_primary_attacker(), w.get_primary_defender(), w, true, int32_t(total_po_cost), false);
3415 continue;
3416 }
3417 }
3418 }
3419 } else if(w.get_primary_defender().get_is_player_controlled() == false) { // defender may surrender
3420 if(defender_surrender || (overall_score >= 100 || (overall_score >= 50 && overall_score >= total_po_cost * 2))) {
3421 send_offer_up_to(w.get_primary_defender(), w.get_primary_attacker(), w, false, int32_t(overall_score), true);
3422 continue;
3423 }
3424 }
3425 } else {
3426 bool attacker_surrender = has_cores_occupied(state, w.get_primary_attacker());
3427 auto total_po_cost = military::defender_peace_cost(state, w);
3428 if(w.get_primary_defender().get_is_player_controlled() == false) { // defender makes offer
3429 if(attacker_surrender || (overall_score <= -100 || (overall_score <= -50 && overall_score <= -total_po_cost * 2))) {
3430 send_offer_up_to(w.get_primary_defender(), w.get_primary_attacker(), w, false, int32_t(-overall_score), false);
3431 continue;
3432 }
3433 if(w.get_primary_attacker().get_is_player_controlled() == false) {
3434 auto war_duration = state.current_date.value - state.world.war_get_start_date(w).value;
3435 if(war_duration >= 365) {
3436 float willingness_factor = float(war_duration - 365) * 10.f / 365.0f;
3437 if(attacker_surrender || (-overall_score > (total_po_cost - willingness_factor) && (overall_score / 2 + total_po_cost - willingness_factor) < 0)) {
3438 send_offer_up_to(w.get_primary_defender(), w.get_primary_attacker(), w, false, int32_t(total_po_cost), false);
3439 continue;
3440 }
3441 }
3442 }
3443 } else if(w.get_primary_attacker().get_is_player_controlled() == false) { // attacker may surrender
3444 if(attacker_surrender || (overall_score <= -100 || (overall_score <= -50 && overall_score <= -total_po_cost * 2))) {
3445 send_offer_up_to(w.get_primary_attacker(), w.get_primary_defender(), w, true, int32_t(-overall_score), true);
3446 continue;
3447 }
3448 }
3449 }
3450 }
3451 }
3452}
3453
3455 dcon::nation_id n, dcon::nation_id from,
3456 dcon::nation_id prime_attacker, dcon::nation_id prime_defender,
3457 float primary_warscore, float scoreagainst_me,
3458 bool offer_from_attacker, bool concession,
3459 int32_t overall_po_value, int32_t my_po_target,
3460 int32_t target_personal_po_value, int32_t potential_peace_score_against,
3461 int32_t my_side_against_target, int32_t my_side_peace_cost,
3462 int32_t war_duration, bool contains_sq) {
3463 bool is_attacking = !offer_from_attacker;
3464
3465 auto overall_score = primary_warscore;
3466 if(concession && overall_score <= -50.0f) {
3467 return true;
3468 }
3469
3470 if(!concession) {
3471 overall_po_value = -overall_po_value;
3472 }
3473 if(overall_po_value < -100)
3474 return false;
3475
3476 auto personal_score_saved = target_personal_po_value - potential_peace_score_against;
3477
3478 if((prime_attacker == n || prime_defender == n) && (prime_attacker == from || prime_defender == from)) {
3479 if(overall_score <= -50 && overall_score <= overall_po_value * 2)
3480 return true;
3481
3482 if(concession && my_side_peace_cost <= overall_po_value)
3483 return true; // offer contains everything
3484 if(war_duration < 365) {
3485 return false;
3486 }
3487 float willingness_factor = float(war_duration - 365) * 10.f / 365.0f;
3488 if(overall_score >= 0) {
3489 if(concession && ((overall_score * 2 - overall_po_value - willingness_factor) < 0))
3490 return true;
3491 } else {
3492 if((overall_score - willingness_factor) <= overall_po_value && (overall_score / 2 - overall_po_value - willingness_factor) < 0)
3493 return true;
3494 }
3495
3496 } else if((prime_attacker == n || prime_defender == n) && concession) {
3497 if(scoreagainst_me > 50)
3498 return true;
3499
3500 if(overall_score < 0.0f) { // we are losing
3501 if(my_side_against_target - scoreagainst_me <= overall_po_value + personal_score_saved)
3502 return true;
3503 } else {
3504 if(my_side_against_target <= overall_po_value)
3505 return true;
3506 }
3507
3508 } else {
3509 if(contains_sq)
3510 return false;
3511
3512 if(scoreagainst_me > 50 && scoreagainst_me > -overall_po_value * 2)
3513 return true;
3514
3515 if(overall_score < 0.0f) { // we are losing
3516 if(personal_score_saved > 0 && scoreagainst_me + personal_score_saved - my_po_target >= -overall_po_value)
3517 return true;
3518
3519 } else { // we are winning
3520 if(my_po_target > 0 && my_po_target >= overall_po_value)
3521 return true;
3522 }
3523 }
3524
3525 //will accept anything
3526 if(has_cores_occupied(state, n))
3527 return true;
3528 return false;
3529}
3530
3531bool will_accept_peace_offer(sys::state& state, dcon::nation_id n, dcon::nation_id from, dcon::peace_offer_id p) {
3532 auto w = state.world.peace_offer_get_war_from_war_settlement(p);
3533 auto prime_attacker = state.world.war_get_primary_attacker(w);
3534 auto prime_defender = state.world.war_get_primary_defender(w);
3535 bool is_attacking = military::is_attacker(state, w, n);
3536 bool contains_sq = false;
3537
3538 auto overall_score = military::primary_warscore(state, w);
3539 if(!is_attacking)
3540 overall_score = -overall_score;
3541
3542 auto concession = state.world.peace_offer_get_is_concession(p);
3543
3544 if(concession && overall_score <= -50.0f) {
3545 return true;
3546 }
3547
3548 int32_t overall_po_value = 0;
3549 int32_t personal_po_value = 0;
3550 int32_t my_po_target = 0;
3551 for(auto wg : state.world.peace_offer_get_peace_offer_item(p)) {
3552 auto wg_value = military::peace_cost(state, w, wg.get_wargoal().get_type(), wg.get_wargoal().get_added_by(), wg.get_wargoal().get_target_nation(), wg.get_wargoal().get_secondary_nation(), wg.get_wargoal().get_associated_state(), wg.get_wargoal().get_associated_tag());
3553 overall_po_value += wg_value;
3554
3555 if((wg.get_wargoal().get_type().get_type_bits() & military::cb_flag::po_status_quo) != 0)
3556 contains_sq = true;
3557
3558 if(wg.get_wargoal().get_target_nation() == n) {
3559 personal_po_value += wg_value;
3560 }
3561 }
3562 if(!concession) {
3563 overall_po_value = -overall_po_value;
3564 }
3565 if(overall_po_value < -100)
3566 return false;
3567
3568 int32_t potential_peace_score_against = 0;
3569 for(auto wg : state.world.war_get_wargoals_attached(w)) {
3570 if(wg.get_wargoal().get_target_nation() == n || wg.get_wargoal().get_added_by() == n) {
3571 auto wg_value = military::peace_cost(state, w, wg.get_wargoal().get_type(), wg.get_wargoal().get_added_by(), n, wg.get_wargoal().get_secondary_nation(), wg.get_wargoal().get_associated_state(), wg.get_wargoal().get_associated_tag());
3572
3573 if(wg.get_wargoal().get_target_nation() == n && (wg.get_wargoal().get_added_by() == from || from == prime_attacker || from == prime_defender)) {
3574 potential_peace_score_against += wg_value;
3575 }
3576 if(wg.get_wargoal().get_added_by() == n && (wg.get_wargoal().get_target_nation() == from || from == prime_attacker || from == prime_defender)) {
3577 my_po_target += wg_value;
3578 }
3579 }
3580 }
3581 auto personal_score_saved = personal_po_value - potential_peace_score_against;
3582
3583 if((prime_attacker == n || prime_defender == n) && (prime_attacker == from || prime_defender == from)) {
3584 if(overall_score <= -50 && overall_score <= overall_po_value * 2)
3585 return true;
3586
3587 auto war_duration = state.current_date.value - state.world.war_get_start_date(w).value;
3588 if(concession && (is_attacking ? military::attacker_peace_cost(state, w) : military::defender_peace_cost(state, w)) <= overall_po_value)
3589 return true; // offer contains everything
3590 if(war_duration < 365) {
3591 return false;
3592 }
3593 float willingness_factor = float(war_duration - 365) * 10.f / 365.0f;
3594 if(overall_score >= 0) {
3595 if(concession && ((overall_score * 2 - overall_po_value - willingness_factor) < 0))
3596 return true;
3597 } else {
3598 if((overall_score - willingness_factor) <= overall_po_value && (overall_score / 2 - overall_po_value - willingness_factor) < 0)
3599 return true;
3600 }
3601
3602 } else if((prime_attacker == n || prime_defender == n) && concession) {
3603 auto scoreagainst_me = military::directed_warscore(state, w, from, n);
3604
3605 if(scoreagainst_me > 50)
3606 return true;
3607
3608 int32_t my_side_against_target = 0;
3609 for(auto wg : state.world.war_get_wargoals_attached(w)) {
3610 if(wg.get_wargoal().get_target_nation() == from) {
3611 auto wg_value = military::peace_cost(state, w, wg.get_wargoal().get_type(), wg.get_wargoal().get_added_by(), n, wg.get_wargoal().get_secondary_nation(), wg.get_wargoal().get_associated_state(), wg.get_wargoal().get_associated_tag());
3612
3613 my_side_against_target += wg_value;
3614 }
3615 }
3616
3617 if(overall_score < 0.0f) { // we are losing
3618 if(my_side_against_target - scoreagainst_me <= overall_po_value + personal_score_saved)
3619 return true;
3620 } else {
3621 if(my_side_against_target <= overall_po_value)
3622 return true;
3623 }
3624
3625 } else {
3626 if(contains_sq)
3627 return false;
3628
3629 auto scoreagainst_me = military::directed_warscore(state, w, from, n);
3630 if(scoreagainst_me > 50 && scoreagainst_me > -overall_po_value * 2)
3631 return true;
3632
3633 if(overall_score < 0.0f) { // we are losing
3634 if(personal_score_saved > 0 && scoreagainst_me + personal_score_saved - my_po_target >= -overall_po_value)
3635 return true;
3636
3637 } else { // we are winning
3638 if(my_po_target > 0 && my_po_target >= overall_po_value)
3639 return true;
3640 }
3641 }
3642
3643 //will accept anything
3644 if(has_cores_occupied(state, n))
3645 return true;
3646 return false;
3647}
3648
3649bool naval_supremacy(sys::state& state, dcon::nation_id n, dcon::nation_id target) {
3650 auto self_sup = state.world.nation_get_used_naval_supply_points(n);
3651
3652 auto real_target = state.world.overlord_get_ruler(state.world.nation_get_overlord_as_subject(target));
3653 if(!real_target)
3654 real_target = target;
3655
3656 if(self_sup <= state.world.nation_get_used_naval_supply_points(real_target))
3657 return false;
3658
3659 if(self_sup <= state.world.nation_get_in_sphere_of(real_target).get_used_naval_supply_points())
3660 return false;
3661
3662 for(auto a : state.world.nation_get_diplomatic_relation(real_target)) {
3663 if(!a.get_are_allied())
3664 continue;
3665 auto other = a.get_related_nations(0) != real_target ? a.get_related_nations(0) : a.get_related_nations(1);
3666 if(self_sup <= other.get_used_naval_supply_points())
3667 return false;
3668 }
3669
3670 return true;
3671}
3672
3674 auto targets = ve::vectorizable_buffer<dcon::nation_id, dcon::nation_id>(state.world.nation_size());
3675 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t i) {
3676 dcon::nation_id n{ dcon::nation_id::value_base_t(i) };
3677 if(state.world.nation_get_owned_province_count(n) == 0)
3678 return;
3679 if(state.world.nation_get_is_at_war(n))
3680 return;
3681 if(state.world.nation_get_is_player_controlled(n))
3682 return;
3683 if(state.world.nation_get_military_score(n) == 0)
3684 return;
3685 if(auto ol = state.world.nation_get_overlord_as_subject(n); state.world.overlord_get_ruler(ol))
3686 return;
3687 auto base_strength = estimate_strength(state, n);
3688 float best_difference = 2.0f;
3689 //Great powers should look for non-neighbor nations to use their existing wargoals on; helpful for forcing unification/repay debts wars to happen
3690 if(nations::is_great_power(state, n)) {
3691 for(auto target : state.world.in_nation) {
3692 auto real_target = target.get_overlord_as_subject().get_ruler() ? target.get_overlord_as_subject().get_ruler() : target;
3693 if(target == n || real_target == n)
3694 continue;
3695 if(state.world.nation_get_owned_province_count(real_target) == 0)
3696 continue;
3697 if(nations::are_allied(state, n, real_target))
3698 continue;
3699 if(target.get_in_sphere_of() == n)
3700 continue;
3701 if(military::has_truce_with(state, n, real_target))
3702 continue;
3703 if(!military::can_use_cb_against(state, n, target))
3704 continue;
3705 //If it neighbors one of our spheres and we can pathfind to each other's capitals, we don't need naval supremacy to reach this nation
3706 //Generally here to help Prussia realize it doesn't need a navy to attack Denmark
3707 for(auto adj : state.world.nation_get_nation_adjacency(target)) {
3708 auto other = adj.get_connected_nations(0) != n ? adj.get_connected_nations(0) : adj.get_connected_nations(1);
3709 auto neighbor = other;
3710 if(neighbor.get_in_sphere_of() == n){
3711 auto path = province::make_safe_land_path(state, state.world.nation_get_capital(n), state.world.nation_get_capital(neighbor), n);
3712 if(path.empty()) {
3713 continue;
3714 }
3715 auto str_difference = base_strength + estimate_additional_offensive_strength(state, n, real_target) - estimate_defensive_strength(state, real_target);
3716 if(str_difference > best_difference) {
3717 best_difference = str_difference;
3718 targets.set(n, target.id);
3719 break;
3720 }
3721 }
3722 }
3723 if(!state.world.get_nation_adjacency_by_nation_adjacency_pair(n, target) && !naval_supremacy(state, n, target))
3724 continue;
3725 auto str_difference = base_strength + estimate_additional_offensive_strength(state, n, real_target) - estimate_defensive_strength(state, real_target);
3726 if(str_difference > best_difference) {
3727 best_difference = str_difference;
3728 targets.set(n, target.id);
3729 }
3730 }
3731 }
3732 for(auto adj : state.world.nation_get_nation_adjacency(n)) {
3733 auto other = adj.get_connected_nations(0) != n ? adj.get_connected_nations(0) : adj.get_connected_nations(1);
3734 auto real_target = other.get_overlord_as_subject().get_ruler() ? other.get_overlord_as_subject().get_ruler() : other;
3735 if(real_target == n)
3736 continue;
3737 if(nations::are_allied(state, n, real_target) || nations::are_allied(state, n, other))
3738 continue;
3739 if(real_target.get_in_sphere_of() == n)
3740 continue;
3741 if(state.world.nation_get_in_sphere_of(other) == n)
3742 continue;
3743 if(military::has_truce_with(state, n, other) || military::has_truce_with(state, n, real_target))
3744 continue;
3745 if(!military::can_use_cb_against(state, n, other))
3746 continue;
3747 if(!state.world.get_nation_adjacency_by_nation_adjacency_pair(n, other) && !naval_supremacy(state, n, other))
3748 continue;
3749 auto str_difference = base_strength + estimate_additional_offensive_strength(state, n, real_target) - estimate_defensive_strength(state, real_target);
3750 if(str_difference > best_difference) {
3751 best_difference = str_difference;
3752 targets.set(n, other.id);
3753 }
3754 }
3755 if(state.world.nation_get_central_ports(n) > 0) {
3756 // try some random coastal nations
3757 for(uint32_t j = 0; j < 6; ++j) {
3758 auto rvalue = rng::get_random(state, uint32_t((n.index() << 3) + j));
3759 auto reduced_value = rng::reduce(uint32_t(rvalue), state.world.nation_size());
3760 dcon::nation_id other{ dcon::nation_id::value_base_t(reduced_value) };
3761 auto real_target = fatten(state.world, other).get_overlord_as_subject().get_ruler() ? fatten(state.world, other).get_overlord_as_subject().get_ruler() : fatten(state.world, other);
3762 if(other == n || real_target == n)
3763 continue;
3764 if(state.world.nation_get_owned_province_count(other) == 0 || state.world.nation_get_owned_province_count(real_target) == 0)
3765 continue;
3766 if(state.world.nation_get_central_ports(other) == 0 || state.world.nation_get_central_ports(real_target) == 0)
3767 continue;
3768 if(nations::are_allied(state, n, real_target) || nations::are_allied(state, n, other))
3769 continue;
3770 if(real_target.get_in_sphere_of() == n)
3771 continue;
3772 if(state.world.nation_get_in_sphere_of(other) == n)
3773 continue;
3774 if(military::has_truce_with(state, n, other) || military::has_truce_with(state, n, real_target))
3775 continue;
3776 if(!military::can_use_cb_against(state, n, other))
3777 continue;
3778 if(!state.world.get_nation_adjacency_by_nation_adjacency_pair(n, other) && !naval_supremacy(state, n, other))
3779 continue;
3780 auto str_difference = base_strength + estimate_additional_offensive_strength(state, n, real_target) - estimate_defensive_strength(state, real_target);
3781 if(str_difference > best_difference) {
3782 best_difference = str_difference;
3783 targets.set(n, other);
3784 }
3785 }
3786 }
3787 });
3788 for(auto n : state.world.in_nation) {
3789 if(n.get_is_at_war() == false && targets.get(n)) {
3790 static std::vector<possible_cb> potential;
3791 sort_available_declaration_cbs(potential, state, n, targets.get(n));
3792 if(!potential.empty()) {
3793 assert(command::can_declare_war(state, n, targets.get(n), potential[0].cb, potential[0].state_def, potential[0].associated_tag, potential[0].secondary_nation));
3794 command::execute_declare_war(state, n, targets.get(n), potential[0].cb, potential[0].state_def, potential[0].associated_tag, potential[0].secondary_nation, true, false);
3795 }
3796 }
3797 }
3798}
3799
3801 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t i) {
3802 dcon::nation_id nid{ dcon::nation_id::value_base_t(i) };
3803 auto n = fatten(state.world, nid);
3804 if(n.get_is_player_controlled() || n.get_owned_province_count() == 0)
3805 return;
3806
3807 float base_income = economy::estimate_daily_income(state, n) + n.get_stockpiles(economy::money) / 365.f;
3808
3809 // they don't have to add up to 1.f
3810 // the reason they are there is to slow down AI spendings,
3811 // make them more or less balanced
3812 // and stabilize economy faster
3813 // not to allow it to hoard money
3814
3815 float land_budget_ratio = 0.15f;
3816 float sea_budget_ratio = 0.05f;
3817 float education_budget_ratio = 0.30f;
3818 float investments_budget_ratio = 0.05f;
3819 float soldiers_budget_ratio = 0.40f;
3820 float construction_budget_ratio = 0.50f;
3821 float administration_budget_ratio = 0.15f;
3822 float overseas_maintenance_budget_ratio = 0.10f;
3823
3824 if(n.get_is_at_war()) {
3825 land_budget_ratio = 2.f;
3826 sea_budget_ratio = 2.f;
3827
3828 administration_budget_ratio *= 0.15f;
3829 education_budget_ratio *= 0.15f;
3830 overseas_maintenance_budget_ratio *= 0.15f;
3831 //n.set_land_spending(int8_t(100));
3832 //n.set_naval_spending(int8_t(100));
3833 } else if(n.get_ai_is_threatened()) {
3834 land_budget_ratio = 0.5f;
3835 sea_budget_ratio = 0.25f;
3836
3837 administration_budget_ratio *= 0.75f;
3838 education_budget_ratio *= 0.75f;
3839 overseas_maintenance_budget_ratio *= 0.75f;
3840 //n.set_land_spending(int8_t(50));
3841 //n.set_naval_spending(int8_t(50));
3842 } else {
3843 //n.set_land_spending(int8_t(25));
3844 //n.set_naval_spending(int8_t(25));
3845 }
3846 float land_budget = land_budget_ratio * base_income;
3847 float naval_budget = sea_budget_ratio * base_income;
3848 float education_budget = education_budget_ratio * base_income;
3849 float construction_budget = construction_budget_ratio * base_income;
3850 float administration_budget = administration_budget_ratio * base_income;
3851 float soldiers_budget = soldiers_budget_ratio * base_income;
3852 float overseas_budget = overseas_maintenance_budget_ratio * base_income;
3853
3854 float ratio_land = 100.f * land_budget / (1.f + economy::estimate_land_spending(state, n));
3855 float ratio_naval = 100.f * naval_budget / (1.f + economy::estimate_naval_spending(state, n));
3856
3857 ratio_land = std::clamp(ratio_land, 0.f, 100.f);
3858 ratio_naval = std::clamp(ratio_naval, 0.f, 100.f);
3859 // Reduce spending at peace
3860 if(!state.world.nation_get_is_at_war(n) && future_rebels_in_nation(state, n) == 0) {
3861 ratio_land /= 10.f;
3862 ratio_naval /= 10.f;
3863 }
3864 n.set_land_spending(int8_t(ratio_land));
3865 n.set_naval_spending(int8_t(ratio_naval));
3866
3867 n.set_construction_spending(75);
3868
3869 float max_education_budget = 1.f + economy::estimate_pop_payouts_by_income_type(state, n, culture::income_type::education);
3870 float max_soldiers_budget = 1.f + economy::estimate_pop_payouts_by_income_type(state, n, culture::income_type::military);
3872 float max_overseas_budget = 1.f + economy::estimate_overseas_penalty_spending(state, n);
3873
3874 // solving x^2 * max = desired
3875 float ratio_education = 100.f * math::sqrt(education_budget / max_education_budget);
3876 ratio_education = std::clamp(ratio_education, 0.f, 100.f);
3877 n.set_education_spending(int8_t(ratio_education));
3878
3879 // If State can build factories - why subsidize capitalists
3880 auto rules = n.get_combined_issue_rules();
3881 if(n.get_is_civilized() && (rules & issue_rule::build_factory) == 0) {
3882 float investment_budget = investments_budget_ratio * base_income;
3883 float max_investment_budget = 1.f + economy::estimate_domestic_investment(state, n);
3884 float investment_ratio = 100.f * math::sqrt(investment_budget / max_investment_budget);
3885 investment_ratio = std::clamp(investment_ratio, 0.f, 100.f);
3886 n.set_domestic_investment_spending(int8_t(investment_ratio));
3887 } else {
3888 n.set_domestic_investment_spending(int8_t(0));
3889 }
3890
3891 float soldiers_max_ratio = 100.f * math::sqrt(soldiers_budget / max_soldiers_budget);
3892 soldiers_max_ratio = std::clamp(soldiers_max_ratio, 0.f, 100.f);
3893
3894 float administration_max_ratio = 100.f * math::sqrt(administration_budget / max_admin_budget);
3895 administration_max_ratio = std::clamp(administration_max_ratio, 0.f, 100.f);
3896
3897 float overseas_max_ratio = std::clamp(100.f * overseas_budget / max_overseas_budget, 0.f, 100.f);
3898
3899 n.set_tariffs_import(int8_t(5));
3900 n.set_tariffs_export(int8_t(5));
3901
3902 float poor_militancy = (state.world.nation_get_demographics(n, demographics::poor_militancy) / std::max(1.0f, state.world.nation_get_demographics(n, demographics::poor_total))) / 10.f;
3903 float mid_militancy = (state.world.nation_get_demographics(n, demographics::middle_militancy) / std::max(1.0f, state.world.nation_get_demographics(n, demographics::middle_total))) / 10.f;
3904 float rich_militancy = (state.world.nation_get_demographics(n, demographics::rich_militancy) / std::max(1.0f, state.world.nation_get_demographics(n, demographics::rich_total))) / 10.f;
3905
3906 if((rules & issue_rule::expand_factory) != 0 || (rules & issue_rule::build_factory) != 0) {
3907 // Non-lf prioritize poor people
3908 int max_poor_tax = int(10.f + 70.f * (1.f - poor_militancy));
3909 int max_mid_tax = int(10.f + 80.f * (1.f - mid_militancy));
3910 int max_rich_tax = int(10.f + 90.f * (1.f - rich_militancy));
3911 int max_social = int(100.f * poor_militancy);
3912
3913 // enough tax?
3914 bool enough_tax = true;
3915 if(ratio_education < 50.f) {
3916 enough_tax = false;
3917 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() + 2, 10, std::max(10, max_poor_tax))));
3918 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() + 3, 10, std::max(10, max_mid_tax))));
3919 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() + 5, 10, std::max(10, max_rich_tax))));
3920 }
3921
3922 if(n.get_spending_level() < 1.0f || n.get_last_treasury() >= n.get_stockpiles(economy::money) || ratio_education < 50.f) { // losing money
3923 //if(n.get_administrative_efficiency() > 0.98f) {
3924 // n.set_administrative_spending(int8_t(std::max(0, n.get_administrative_spending() - 2)));
3925 //}
3926 if(!n.get_ai_is_threatened()) {
3927 n.set_military_spending(int8_t(std::max(50, n.get_military_spending() - 5)));
3928 }
3929 n.set_social_spending(int8_t(std::max(0, n.get_social_spending() - 2)));
3930
3931 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() + 2, 10, std::max(10, max_poor_tax))));
3932 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() + 3, 10, std::max(10, max_mid_tax))));
3933 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() + 5, 10, std::max(10, max_rich_tax))));
3934 } else if(n.get_last_treasury() < n.get_stockpiles(economy::money)) { // gaining money
3935 //if(n.get_administrative_efficiency() < 0.98f) {
3936 // n.set_administrative_spending(int8_t(std::min(100, n.get_administrative_spending() + 2)));
3937 //}
3938 if(n.get_ai_is_threatened()) {
3939 n.set_military_spending(int8_t(std::min(100, n.get_military_spending() + 10)));
3940 } else {
3941 n.set_military_spending(int8_t(std::min(75, n.get_military_spending() + 10)));
3942 }
3943 n.set_social_spending(int8_t(std::min(max_social, n.get_social_spending() + 2)));
3944
3945 if(enough_tax) {
3946 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() - 2, 10, std::max(10, max_poor_tax))));
3947 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() - 3, 10, std::max(10, max_mid_tax))));
3948 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() - 5, 10, std::max(10, max_rich_tax))));
3949 }
3950 }
3951 } else {
3952 int max_poor_tax = int(10.f + 90.f * (1.f - poor_militancy));
3953 int max_mid_tax = int(10.f + 90.f * (1.f - mid_militancy));
3954 int max_rich_tax = int(10.f + 40.f * (1.f - rich_militancy));
3955 int max_social = int(100.f * poor_militancy);
3956
3957 // enough tax?
3958 bool enough_tax = true;
3959 if(ratio_education < 50.f) {
3960 enough_tax = false;
3961 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() + 5, 10, std::max(10, max_poor_tax))));
3962 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() + 3, 10, std::max(10, max_mid_tax))));
3963 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() + 2, 10, std::max(10, max_rich_tax))));
3964 }
3965
3966 // Laissez faire prioritize tax free capitalists
3967 if(n.get_spending_level() < 1.0f || n.get_last_treasury() >= n.get_stockpiles(economy::money) || ratio_education < 50.f) { // losing money
3968 //if(n.get_administrative_efficiency() > 0.98f) {
3969 // n.set_administrative_spending(int8_t(std::max(0, n.get_administrative_spending() - 2)));
3970 //}
3971 if(!n.get_ai_is_threatened()) {
3972 n.set_military_spending(int8_t(std::max(50, n.get_military_spending() - 5)));
3973 }
3974 n.set_social_spending(int8_t(std::max(0, n.get_social_spending() - 2)));
3975
3976 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() + 5, 10, std::max(10, max_poor_tax))));
3977 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() + 3, 10, std::max(10, max_mid_tax))));
3978 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() + 2, 10, std::max(10, max_rich_tax))));
3979 } else if(n.get_last_treasury() < n.get_stockpiles(economy::money)) { // gaining money
3980 //if(n.get_administrative_efficiency() < 0.98f) {
3981 // n.set_administrative_spending(int8_t(std::min(100, n.get_administrative_spending() + 2)));
3982 //}
3983 if(n.get_ai_is_threatened()) {
3984 n.set_military_spending(int8_t(std::min(100, n.get_military_spending() + 10)));
3985 } else {
3986 n.set_military_spending(int8_t(std::min(75, n.get_military_spending() + 10)));
3987 }
3988 n.set_social_spending(int8_t(std::min(max_social, n.get_social_spending() + 2)));
3989
3990 if(enough_tax) {
3991 n.set_poor_tax(int8_t(std::clamp(n.get_poor_tax() - 5, 10, std::max(10, max_poor_tax))));
3992 n.set_middle_tax(int8_t(std::clamp(n.get_middle_tax() - 3, 10, std::max(10, max_mid_tax))));
3993 n.set_rich_tax(int8_t(std::clamp(n.get_rich_tax() - 2, 10, std::max(10, max_rich_tax))));
3994 }
3995 }
3996 }
3997
3998 n.set_administrative_spending(int8_t(std::min(int8_t(administration_max_ratio), n.get_administrative_spending())));
3999 n.set_administrative_spending(int8_t(administration_max_ratio));
4000 n.set_military_spending(int8_t(std::min(int8_t(soldiers_max_ratio), n.get_military_spending())));
4001 n.set_overseas_spending(int8_t(overseas_max_ratio));
4002
4004 });
4005}
4006
4007
4008void remove_ai_data(sys::state& state, dcon::nation_id n) {
4009 for(auto ar : state.world.nation_get_army_control(n)) {
4010 ar.get_army().set_ai_activity(0);
4011 ar.get_army().set_ai_province(dcon::province_id{});
4012 }
4013 for(auto v : state.world.nation_get_navy_control(n)) {
4014 v.get_navy().set_ai_activity(0);
4015 }
4016}
4017
4018bool unit_on_ai_control(sys::state& state, dcon::army_id a) {
4019 auto fat_id = dcon::fatten(state.world, a);
4020 return fat_id.get_controller_from_army_control().get_is_player_controlled()
4021 ? fat_id.get_is_ai_controlled()
4022 : true;
4023}
4024/*bool unit_on_ai_control(sys::state& state, dcon::navy_id a) {
4025 auto fat_id = dcon::fatten(state.world, a);
4026 return fat_id.get_controller_from_navy_control().get_is_player_controlled()
4027 ? fat_id.get_is_ai_controlled()
4028 : true;
4029}*/
4030
4032 static std::vector<dcon::ship_id> to_delete;
4033 to_delete.clear();
4034 for(auto n : state.world.in_nation) {
4035 if(n.get_is_player_controlled())
4036 continue;
4037 if(n.get_is_at_war() == false && nations::is_landlocked(state, n)) {
4038 for(auto v : n.get_navy_control()) {
4039 if(!v.get_navy().get_battle_from_navy_battle_participation()) {
4040 for(auto shp : v.get_navy().get_navy_membership()) {
4041 to_delete.push_back(shp.get_ship().id);
4042 }
4043 }
4044 }
4045 } else if(n.get_is_at_war() == false) {
4046 dcon::unit_type_id best_transport;
4047 dcon::unit_type_id best_light;
4048 dcon::unit_type_id best_big;
4049 for(uint32_t i = 2; i < state.military_definitions.unit_base_definitions.size(); ++i) {
4050 dcon::unit_type_id j{ dcon::unit_type_id::value_base_t(i) };
4051 if(!n.get_active_unit(j) && !state.military_definitions.unit_base_definitions[j].active)
4052 continue;
4053 if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::transport) {
4054 if(!best_transport || state.military_definitions.unit_base_definitions[best_transport].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4055 best_transport = j;
4056 }
4057 } else if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::light_ship) {
4058 if(!best_light || state.military_definitions.unit_base_definitions[best_light].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4059 best_light = j;
4060 }
4061 } else if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::big_ship) {
4062 if(!best_big || state.military_definitions.unit_base_definitions[best_big].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4063 best_big = j;
4064 }
4065 }
4066 }
4067 for(auto v : n.get_navy_control()) {
4068 if(!v.get_navy().get_battle_from_navy_battle_participation()) {
4069 auto trange = v.get_navy().get_army_transport();
4070 bool transporting = trange.begin() != trange.end();
4071
4072 for(auto shp : v.get_navy().get_navy_membership()) {
4073 auto type = shp.get_ship().get_type();
4074
4075 if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::transport && !transporting) {
4076 if(best_transport && type != best_transport)
4077 to_delete.push_back(shp.get_ship().id);
4078 } else if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::light_ship) {
4079 if(best_light && type != best_light)
4080 to_delete.push_back(shp.get_ship().id);
4081 } else if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::big_ship) {
4082 if(best_big && type != best_big)
4083 to_delete.push_back(shp.get_ship().id);
4084 }
4085 }
4086 }
4087 }
4088 }
4089 }
4090 for(auto s : to_delete) {
4091 state.world.delete_ship(s);
4092 }
4093}
4094
4096 for(auto n : state.world.in_nation) {
4097 if(!n.get_is_player_controlled() && n.get_province_naval_construction().begin() == n.get_province_naval_construction().end()) {
4098 auto disarm = n.get_disarmed_until();
4099 if(disarm && state.current_date < disarm)
4100 continue;
4101
4102 dcon::unit_type_id best_transport;
4103 dcon::unit_type_id best_light;
4104 dcon::unit_type_id best_big;
4105
4106 for(uint32_t i = 2; i < state.military_definitions.unit_base_definitions.size(); ++i) {
4107 dcon::unit_type_id j{ dcon::unit_type_id::value_base_t(i) };
4108 if(!n.get_active_unit(j) && !state.military_definitions.unit_base_definitions[j].active)
4109 continue;
4110
4111 if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::transport) {
4112 if(!best_transport || state.military_definitions.unit_base_definitions[best_transport].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4113 best_transport = j;
4114 }
4115 } else if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::light_ship) {
4116 if(!best_light || state.military_definitions.unit_base_definitions[best_light].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4117 best_light = j;
4118 }
4119 } else if(state.military_definitions.unit_base_definitions[j].type == military::unit_type::big_ship) {
4120 if(!best_big || state.military_definitions.unit_base_definitions[best_big].defence_or_hull < state.military_definitions.unit_base_definitions[j].defence_or_hull) {
4121 best_big = j;
4122 }
4123 }
4124 }
4125
4126 int32_t num_transports = 0;
4127 int32_t fleet_cap_in_transports = 0;
4128 int32_t fleet_cap_in_small = 0;
4129 int32_t fleet_cap_in_big = 0;
4130
4131 for(auto v : n.get_navy_control()) {
4132 for(auto s : v.get_navy().get_navy_membership()) {
4133 auto type = s.get_ship().get_type();
4134 if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::transport) {
4135 ++num_transports;
4136 fleet_cap_in_transports += state.military_definitions.unit_base_definitions[type].supply_consumption_score;
4137 } else if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::big_ship) {
4138 fleet_cap_in_big += state.military_definitions.unit_base_definitions[type].supply_consumption_score;
4139 } else if(state.military_definitions.unit_base_definitions[type].type == military::unit_type::light_ship) {
4140 fleet_cap_in_small += state.military_definitions.unit_base_definitions[type].supply_consumption_score;
4141 }
4142 }
4143 }
4144
4145 static std::vector<dcon::province_id> owned_ports;
4146 owned_ports.clear();
4147 for(auto p : n.get_province_ownership()) {
4148 if(p.get_province().get_is_coast() && p.get_province().get_nation_from_province_control() == n) {
4149 owned_ports.push_back(p.get_province().id);
4150 }
4151 }
4152 auto cap = n.get_capital().id;
4153 std::sort(owned_ports.begin(), owned_ports.end(), [&](dcon::province_id a, dcon::province_id b) {
4154 auto a_dist = province::sorting_distance(state, a, cap);
4155 auto b_dist = province::sorting_distance(state, b, cap);
4156 if(a_dist != b_dist)
4157 return a_dist < b_dist;
4158 else
4159 return a.index() < b.index();
4160 });
4161
4162 int32_t constructing_fleet_cap = 0;
4163 if(best_transport) {
4164 if(fleet_cap_in_transports * 3 < n.get_naval_supply_points()) {
4165 auto overseas_allowed = state.military_definitions.unit_base_definitions[best_transport].can_build_overseas;
4166 auto level_req = state.military_definitions.unit_base_definitions[best_transport].min_port_level;
4167 auto supply_pts = state.military_definitions.unit_base_definitions[best_transport].supply_consumption_score;
4168
4169 for(uint32_t j = 0; j < owned_ports.size() && (fleet_cap_in_transports + constructing_fleet_cap) * 3 < n.get_naval_supply_points(); ++j) {
4170 if((overseas_allowed || !province::is_overseas(state, owned_ports[j]))
4171 && state.world.province_get_building_level(owned_ports[j], uint8_t(economy::province_building_type::naval_base)) >= level_req) {
4172 assert(command::can_start_naval_unit_construction(state, n, owned_ports[j], best_transport));
4173 auto c = fatten(state.world, state.world.try_create_province_naval_construction(owned_ports[j], n));
4174 c.set_type(best_transport);
4175 constructing_fleet_cap += supply_pts;
4176 }
4177 }
4178 } else if(num_transports < 10) {
4179 auto overseas_allowed = state.military_definitions.unit_base_definitions[best_transport].can_build_overseas;
4180 auto level_req = state.military_definitions.unit_base_definitions[best_transport].min_port_level;
4181 auto supply_pts = state.military_definitions.unit_base_definitions[best_transport].supply_consumption_score;
4182
4183 for(uint32_t j = 0; j < owned_ports.size() && num_transports < 10; ++j) {
4184 if((overseas_allowed || !province::is_overseas(state, owned_ports[j]))
4185 && state.world.province_get_building_level(owned_ports[j], uint8_t(economy::province_building_type::naval_base)) >= level_req) {
4186 assert(command::can_start_naval_unit_construction(state, n, owned_ports[j], best_transport));
4187 auto c = fatten(state.world, state.world.try_create_province_naval_construction(owned_ports[j], n));
4188 c.set_type(best_transport);
4189 ++num_transports;
4190 constructing_fleet_cap += supply_pts;
4191 }
4192 }
4193 }
4194 }
4195
4196 int32_t used_points = n.get_used_naval_supply_points();
4197 auto rem_free = n.get_naval_supply_points() - (fleet_cap_in_transports + fleet_cap_in_small + fleet_cap_in_big + constructing_fleet_cap);
4198 fleet_cap_in_small = std::max(fleet_cap_in_small, 1);
4199 fleet_cap_in_big = std::max(fleet_cap_in_big, 1);
4200
4201 auto free_big_points = best_light ? rem_free * fleet_cap_in_small / (fleet_cap_in_small + fleet_cap_in_big) : rem_free;
4202 auto free_small_points = best_big ? rem_free * fleet_cap_in_big / (fleet_cap_in_small + fleet_cap_in_big) : rem_free;
4203
4204 if(best_light) {
4205 auto overseas_allowed = state.military_definitions.unit_base_definitions[best_light].can_build_overseas;
4206 auto level_req = state.military_definitions.unit_base_definitions[best_light].min_port_level;
4207 auto supply_pts = state.military_definitions.unit_base_definitions[best_light].supply_consumption_score;
4208
4209 for(uint32_t j = 0; j < owned_ports.size() && supply_pts <= free_small_points; ++j) {
4210 if((overseas_allowed || !province::is_overseas(state, owned_ports[j]))
4211 && state.world.province_get_building_level(owned_ports[j], uint8_t(economy::province_building_type::naval_base)) >= level_req) {
4212 assert(command::can_start_naval_unit_construction(state, n, owned_ports[j], best_light));
4213 auto c = fatten(state.world, state.world.try_create_province_naval_construction(owned_ports[j], n));
4214 c.set_type(best_light);
4215 free_small_points -= supply_pts;
4216 }
4217 }
4218 }
4219 if(best_big) {
4220 auto overseas_allowed = state.military_definitions.unit_base_definitions[best_big].can_build_overseas;
4221 auto level_req = state.military_definitions.unit_base_definitions[best_big].min_port_level;
4222 auto supply_pts = state.military_definitions.unit_base_definitions[best_big].supply_consumption_score;
4223
4224 for(uint32_t j = 0; j < owned_ports.size() && supply_pts <= free_big_points; ++j) {
4225 if((overseas_allowed || !province::is_overseas(state, owned_ports[j]))
4226 && state.world.province_get_building_level(owned_ports[j], uint8_t(economy::province_building_type::naval_base)) >= level_req) {
4227 assert(command::can_start_naval_unit_construction(state, n, owned_ports[j], best_big));
4228 auto c = fatten(state.world, state.world.try_create_province_naval_construction(owned_ports[j], n));
4229 c.set_type(best_big);
4230 free_big_points -= supply_pts;
4231 }
4232 }
4233 }
4234 }
4235 }
4236}
4237
4238dcon::province_id get_home_port(sys::state& state, dcon::nation_id n) {
4239 auto cap = state.world.nation_get_capital(n);
4240 int32_t max_level = -1;
4241 dcon::province_id result;
4242 float current_distance = 1.0f;
4243 for(auto p : state.world.nation_get_province_ownership(n)) {
4244 if(p.get_province().get_is_coast() && p.get_province().get_nation_from_province_control() == n) {
4245 if(p.get_province().get_building_level(uint8_t(economy::province_building_type::naval_base)) > max_level) {
4246 max_level = p.get_province().get_building_level(uint8_t(economy::province_building_type::naval_base));
4247 result = p.get_province();
4248 current_distance = province::sorting_distance(state, cap, p.get_province());
4249 } else if(result && p.get_province().get_building_level(uint8_t(economy::province_building_type::naval_base)) == max_level && province::sorting_distance(state, cap, p.get_province()) < current_distance) {
4250 current_distance = province::sorting_distance(state, cap, p.get_province());
4251 result = p.get_province();
4252 }
4253 }
4254 }
4255 return result;
4256}
4257
4259 for(auto n : state.world.in_nation) {
4260 if(!n.get_is_player_controlled() && n.get_owned_province_count() > 0) {
4261 n.set_ai_home_port(get_home_port(state, n));
4262 }
4263 }
4264}
4265
4267
4268}
4269
4270
4271bool navy_needs_repair(sys::state& state, dcon::navy_id n) {
4272 /*
4273 auto in_nation = fatten(state.world, state.world.navy_get_controller_from_navy_control(n));
4274 auto base_spending_level = in_nation.get_effective_naval_spending();
4275 float oversize_amount =
4276 in_nation.get_naval_supply_points() > 0
4277 ? std::min(float(in_nation.get_used_naval_supply_points()) / float(in_nation.get_naval_supply_points()), 1.75f)
4278 : 1.75f;
4279 float over_size_penalty = oversize_amount > 1.0f ? 2.0f - oversize_amount : 1.0f;
4280 auto spending_level = base_spending_level * over_size_penalty;
4281 auto max_org = 0.25f + 0.75f * spending_level;
4282 */
4283
4284 for(auto shp : state.world.navy_get_navy_membership(n)) {
4285 if(shp.get_ship().get_strength() < 0.5f)
4286 return true;
4287 //if(shp.get_ship().get_org() < 0.75f * max_org)
4288 // return true;
4289 }
4290 return false;
4291}
4292
4293bool naval_advantage(sys::state& state, dcon::nation_id n) {
4294 for(auto par : state.world.nation_get_war_participant(n)) {
4295 for(auto other : par.get_war().get_war_participant()) {
4296 if(other.get_is_attacker() != par.get_is_attacker()) {
4297 if(other.get_nation().get_used_naval_supply_points() > state.world.nation_get_used_naval_supply_points(n))
4298 return false;
4299 }
4300 }
4301 }
4302 return true;
4303}
4304
4305void send_fleet_home(sys::state& state, dcon::navy_id n, fleet_activity moving_status = fleet_activity::returning_to_base, fleet_activity at_base = fleet_activity::idle) {
4306 auto v = fatten(state.world, n);
4307 auto home_port = v.get_controller_from_navy_control().get_ai_home_port();
4308 if(v.get_location_from_navy_location() == home_port) {
4309 v.set_ai_activity(uint8_t(at_base));
4310 } else if(!home_port) {
4311 v.set_ai_activity(uint8_t(fleet_activity::unspecified));
4312 } else if(auto naval_path = province::make_naval_path(state, v.get_location_from_navy_location(), home_port); naval_path.size() > 0) {
4313 auto new_size = uint32_t(naval_path.size());
4314 auto existing_path = v.get_path();
4315 existing_path.resize(new_size);
4316
4317 for(uint32_t i = 0; i < new_size; ++i) {
4318 existing_path[i] = naval_path[i];
4319 }
4320 v.set_arrival_time(military::arrival_time_to(state, v, naval_path.back()));
4321 v.set_ai_activity(uint8_t(moving_status));
4322 } else {
4323 v.set_ai_activity(uint8_t(fleet_activity::unspecified));
4324 }
4325}
4326
4327bool set_fleet_target(sys::state& state, dcon::nation_id n, dcon::province_id start, dcon::navy_id for_navy) {
4328 dcon::province_id result;
4329 float closest = 0.0f;
4330 for(auto par : state.world.nation_get_war_participant(n)) {
4331 for(auto other : par.get_war().get_war_participant()) {
4332 if(other.get_is_attacker() != par.get_is_attacker()) {
4333 for(auto nv : other.get_nation().get_navy_control()) {
4334 auto loc = nv.get_navy().get_location_from_navy_location();
4335 auto dist = province::sorting_distance(state, start, loc);
4336 if(!result || dist < closest) {
4337 if(loc.id.index() < state.province_definitions.first_sea_province.index()) {
4338 result = loc.get_port_to();
4339 } else {
4340 result = loc;
4341 }
4342 closest = dist;
4343 }
4344 }
4345 }
4346 }
4347 }
4348
4349 if(result == start)
4350 return true;
4351
4352 if(result) {
4353 auto existing_path = state.world.navy_get_path(for_navy);
4354 auto path = province::make_naval_path(state, start, result);
4355 if(path.size() > 0) {
4356 auto new_size = std::min(uint32_t(path.size()), uint32_t(4));
4357 existing_path.resize(new_size);
4358 for(uint32_t i = new_size; i-- > 0;) {
4359 assert(path[path.size() - 1 - i]);
4360 existing_path[new_size - 1 - i] = path[path.size() - 1 - i];
4361 }
4362 state.world.navy_set_arrival_time(for_navy, military::arrival_time_to(state, for_navy, path.back()));
4363 state.world.navy_set_ai_activity(for_navy, uint8_t(fleet_activity::attacking));
4364 return true;
4365 } else {
4366 return false;
4367 }
4368 } else {
4369 return false;
4370 }
4371}
4372
4373void unload_units_from_transport(sys::state& state, dcon::navy_id n) {
4374 auto transported_armies = state.world.navy_get_army_transport(n);
4375 auto location = state.world.navy_get_location_from_navy_location(n);
4376
4377
4378 for(auto ar : transported_armies) {
4379 auto path = province::make_land_path(state, location, ar.get_army().get_ai_province(), ar.get_army().get_controller_from_army_control(), ar.get_army());
4380 if(path.size() > 0) {
4381 auto existing_path = ar.get_army().get_path();
4382 auto new_size = uint32_t(path.size());
4383 existing_path.resize(new_size);
4384
4385 for(uint32_t i = 0; i < new_size; ++i) {
4386 assert(path[i]);
4387 existing_path[i] = path[i];
4388 }
4389 ar.get_army().set_arrival_time(military::arrival_time_to(state, ar.get_army(), path.back()));
4390 ar.get_army().set_dig_in(0);
4391 auto activity = army_activity(ar.get_army().get_ai_activity());
4392 if(activity == army_activity::transport_guard) {
4393 ar.get_army().set_ai_activity(uint8_t(army_activity::on_guard));
4394 } else if(activity == army_activity::transport_attack) {
4395 ar.get_army().set_ai_activity(uint8_t(army_activity::attack_gathered));
4396 }
4397 }
4398 }
4399
4400 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::unloading));
4401}
4402
4403bool merge_fleet(sys::state& state, dcon::navy_id n, dcon::province_id p, dcon::nation_id owner) {
4404 auto merge_target = [&]() {
4405 dcon::navy_id largest;
4406 int32_t largest_size = 0;
4407 for(auto on : state.world.province_get_navy_location(p)) {
4408 if(on.get_navy() != n && on.get_navy().get_controller_from_navy_control() == owner) {
4409 auto other_mem = on.get_navy().get_navy_membership();
4410 if(auto sz = int32_t(other_mem.end() - other_mem.begin()); sz > largest_size) {
4411 largest = on.get_navy().id;
4412 largest_size = sz;
4413 }
4414 }
4415 }
4416 return largest;
4417 }();
4418
4419 if(!merge_target) {
4420 return false;
4421 }
4422
4423 auto regs = state.world.navy_get_navy_membership(n);
4424 while(regs.begin() != regs.end()) {
4425 auto reg = (*regs.begin()).get_ship();
4426 reg.set_navy_from_navy_membership(merge_target);
4427 }
4428
4429 auto transported = state.world.navy_get_army_transport(n);
4430 while(transported.begin() != transported.end()) {
4431 auto arm = (*transported.begin()).get_army();
4432 arm.set_navy_from_army_transport(merge_target);
4433 }
4434 return true;
4435}
4436
4438 for(auto n : state.world.in_navy) {
4439 if(n.get_battle_from_navy_battle_participation())
4440 continue;
4441 if(n.get_arrival_time())
4442 continue;
4443
4444 auto owner = n.get_controller_from_navy_control();
4445
4446 if(!owner || owner.get_is_player_controlled() || owner.get_owned_province_count() == 0)
4447 continue;
4448
4449 auto home_port = state.world.nation_get_ai_home_port(owner);
4450 if(!home_port)
4451 continue;
4452
4453 auto location = state.world.navy_get_location_from_navy_location(n);
4454 auto activity = fleet_activity(state.world.navy_get_ai_activity(n));
4455
4456 switch(activity) {
4457 case fleet_activity::unspecified:
4458 if(location == home_port) {
4459 if(!merge_fleet(state, n, location, owner))
4460 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::idle));
4461 } else if(!home_port) {
4462
4463 } else {
4464 // move to home port to merge
4465 send_fleet_home(state, n, fleet_activity::merging);
4466 }
4467 break;
4468 case fleet_activity::boarding:
4469 {
4470 bool all_loaded = true;
4471 for(auto ar : state.world.nation_get_army_control(owner)) {
4472 if(ar.get_army().get_ai_activity() == uint8_t(army_activity::transport_guard) || ar.get_army().get_ai_activity() == uint8_t(army_activity::transport_attack)) {
4473 if(ar.get_army().get_navy_from_army_transport() != n)
4474 all_loaded = false;
4475 }
4476 }
4477
4478 if(all_loaded) {
4479 auto transporting_range = n.get_army_transport();
4480 if(transporting_range.begin() == transporting_range.end()) { // failed to pick up troops
4481 send_fleet_home(state, n);
4482 } else {
4483 auto transported_dest = (*(transporting_range.begin())).get_army().get_ai_province();
4484
4485 // move to closest port or closest off_shore
4486 if(transported_dest.get_is_coast()) {
4487 auto target_prov = transported_dest.id;
4488 if(!province::has_naval_access_to_province(state, owner, target_prov)) {
4489 target_prov = state.world.province_get_port_to(target_prov);
4490 }
4491 auto naval_path = province::make_naval_path(state, location, target_prov);
4492
4493 auto existing_path = n.get_path();
4494 auto new_size = uint32_t(naval_path.size());
4495 existing_path.resize(new_size);
4496
4497 for(uint32_t k = 0; k < new_size; ++k) {
4498 existing_path[k] = naval_path[k];
4499 }
4500 if(new_size > 0) {
4501 n.set_arrival_time(military::arrival_time_to(state, n, naval_path.back()));
4502 n.set_ai_activity(uint8_t(fleet_activity::transporting));
4503 } else {
4504 n.set_arrival_time(sys::date{});
4505 send_fleet_home(state, n);
4506 }
4507
4508 } else if(auto path = province::make_path_to_nearest_coast(state, owner, transported_dest); path.empty()) {
4509 send_fleet_home(state, n);
4510 } else {
4511 auto target_prov = path.front();
4512 if(!province::has_naval_access_to_province(state, owner, target_prov)) {
4513 target_prov = state.world.province_get_port_to(target_prov);
4514 }
4515 auto naval_path = province::make_naval_path(state, location, target_prov);
4516
4517 auto existing_path = n.get_path();
4518 auto new_size = uint32_t(naval_path.size());
4519 existing_path.resize(new_size);
4520
4521 for(uint32_t k = 0; k < new_size; ++k) {
4522 existing_path[k] = naval_path[k];
4523 }
4524 if(new_size > 0) {
4525 n.set_arrival_time(military::arrival_time_to(state, n, naval_path.back()));
4526 n.set_ai_activity(uint8_t(fleet_activity::transporting));
4527 } else {
4528 n.set_arrival_time(sys::date{});
4529 send_fleet_home(state, n);
4530 }
4531 }
4532 }
4533 }
4534 }
4535 break;
4536 case fleet_activity::transporting:
4538 break;
4539 case fleet_activity::failed_transport:
4540 if(location == home_port) {
4541 if(!merge_fleet(state, n, location, owner))
4542 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::idle));
4543 } else if(home_port) {
4544 auto existing_path = state.world.navy_get_path(n);
4545 auto path = province::make_naval_path(state, location, home_port);
4546 if(path.size() > 0) {
4547 auto new_size = uint32_t(path.size());
4548 existing_path.resize(new_size);
4549
4550 for(uint32_t i = 0; i < new_size; ++i) {
4551 assert(path[i]);
4552 existing_path[i] = path[i];
4553 }
4554 state.world.navy_set_arrival_time(n, military::arrival_time_to(state, n, path.back()));
4555 }
4556 }
4557 break;
4558 case fleet_activity::returning_to_base:
4559 if(location == home_port) {
4560 if(!merge_fleet(state, n, location, owner))
4561 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::idle));
4562 } else {
4563 send_fleet_home(state, n);
4564 }
4565 break;
4566 case fleet_activity::attacking:
4567 if(state.world.nation_get_is_at_war(owner) == false) {
4568 send_fleet_home(state, n);
4569 } else if(navy_needs_repair(state, n)) {
4570 send_fleet_home(state, n);
4571 } else {
4572 if(naval_advantage(state, owner) && set_fleet_target(state, owner, state.world.navy_get_location_from_navy_location(n), n)) {
4573 // do nothing -- target set successfully
4574 } else {
4575 send_fleet_home(state, n);
4576 }
4577 }
4578 break;
4579 case fleet_activity::merging:
4580 if(location == home_port) {
4581 if(!merge_fleet(state, n, location, owner))
4582 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::idle));
4583 } else {
4584 send_fleet_home(state, n);
4585 }
4586 break;
4587 case fleet_activity::idle:
4588 if(location != home_port) {
4589 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::unspecified));
4590 } else if(owner.get_is_at_war()) {
4591 if(!navy_needs_repair(state, n)) {
4592 bool valid_attacker = true;
4593 auto self_ships = state.world.navy_get_navy_membership(n);
4594 int32_t self_sz = int32_t(self_ships.end() - self_ships.begin());
4595 for(auto o : owner.get_navy_control()) {
4596 if(o.get_navy() != n) {
4597 if(o.get_navy().get_ai_activity() == uint8_t(fleet_activity::attacking)) {
4598 valid_attacker = false;
4599 break;
4600 }
4601 auto orange = o.get_navy().get_navy_membership();
4602 if(int32_t(orange.end() - orange.begin()) >= self_sz) {
4603 valid_attacker = false;
4604 break;
4605 }
4606 }
4607 }
4608 if(valid_attacker && naval_advantage(state, owner)) {
4609 set_fleet_target(state, owner, state.world.navy_get_location_from_navy_location(n), n);
4610 }
4611 }
4612 }
4613 break;
4614 case fleet_activity::unloading:
4615 {
4616 bool failed_transport = true;
4617
4618 auto transporting = state.world.navy_get_army_transport(n);
4619 for(auto ar : transporting) {
4620 if(ar.get_army().get_path().size() != 0) {
4621 failed_transport = false;
4622 }
4623 }
4624
4625 if(transporting.begin() == transporting.end()) {
4626 // all unloaded -> set to unspecified to send home later in this routine
4627 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::unspecified));
4628 } else if(failed_transport) {
4629 // an army is stuck on the boats
4630 state.world.navy_set_ai_activity(n, uint8_t(fleet_activity::failed_transport));
4631 } else {
4632 // do nothing, still unloading
4633 }
4634 }
4635 break;
4636 }
4637 }
4638}
4639
4640
4642 interior = 0,
4643 coast = 1,
4645 border = 3,
4646 threat_border = 4,
4647 hostile_border = 5,
4648 count = 6
4649};
4650
4652 dcon::province_id id;
4654};
4655
4656void distribute_guards(sys::state& state, dcon::nation_id n) {
4657 std::vector<classified_province> provinces;
4658 provinces.reserve(state.world.province_size());
4659
4660 auto cap = state.world.nation_get_capital(n);
4661
4662 for(auto c : state.world.nation_get_province_control(n)) {
4663 province_class cls = c.get_province().get_is_coast() ? province_class::coast : province_class::interior;
4664 if(c.get_province() == cap)
4665 cls = province_class::border;
4666
4667 for(auto padj : c.get_province().get_province_adjacency()) {
4668 auto other = padj.get_connected_provinces(0) == c.get_province() ? padj.get_connected_provinces(1) : padj.get_connected_provinces(0);
4669 auto n_controller = other.get_nation_from_province_control();
4670 auto ovr = n_controller.get_overlord_as_subject().get_ruler();
4671
4672 if(n_controller == n) {
4673 // own province
4674 } else if(!n_controller && !other.get_rebel_faction_from_province_rebel_control()) {
4675 // uncolonized or sea
4676 } else if(other.get_rebel_faction_from_province_rebel_control()) {
4677 cls = province_class::hostile_border;
4678 break;
4679 } else if(military::are_at_war(state, n, n_controller)) {
4680 cls = province_class::hostile_border;
4681 break;
4682 } else if(nations::are_allied(state, n, n_controller) || (ovr && ovr == n) || (ovr && nations::are_allied(state, n, ovr))) {
4683 // allied controller or subject of allied controller or our "parent" overlord
4684 if(uint8_t(cls) < uint8_t(province_class::low_priority_border)) {
4685 cls = province_class::low_priority_border;
4686 }
4687 } else {
4688 /* We will target POTENTIAL enemies of the nation;
4689 we could also check if the CB can be used on us, but
4690 that is expensive, so instead we use available_cbs! */
4691 bool is_threat = false;
4692 if(n_controller) {
4693 is_threat |= n_controller.get_ai_rival() == n;
4694 is_threat |= state.world.nation_get_ai_rival(n) == n_controller.id;
4695 if(ovr) {
4696 /* subjects cannot negotiate by themselves, but the overlord may */
4697 is_threat |= ovr.get_ai_rival() == n;
4698 is_threat |= state.world.nation_get_ai_rival(n) == ovr.id;
4699 //
4700 is_threat |= ovr.get_constructing_cb_target() == n;
4701 for(auto cb : ovr.get_available_cbs())
4702 is_threat |= cb.target == n;
4703 } else {
4704 is_threat |= n_controller.get_constructing_cb_target() == n;
4705 for(auto cb : n_controller.get_available_cbs())
4706 is_threat |= cb.target == n;
4707 }
4708 }
4709 if(is_threat) {
4710 if(uint8_t(cls) < uint8_t(province_class::threat_border)) {
4711 cls = province_class::threat_border;
4712 }
4713 } else { // other border
4714 if(uint8_t(cls) < uint8_t(province_class::border)) {
4715 cls = province_class::border;
4716 }
4717 }
4718 }
4719 }
4720 provinces.push_back(classified_province{ c.get_province().id, cls });
4721 }
4722
4723 std::sort(provinces.begin(), provinces.end(), [&](classified_province& a, classified_province& b) {
4724 if(a.c != b.c) {
4725 return uint8_t(a.c) > uint8_t(b.c);
4726 }
4727 auto adist = province::sorting_distance(state, a.id, cap);
4728 auto bdist = province::sorting_distance(state, b.id, cap);
4729 if(adist != bdist) {
4730 return adist < bdist;
4731 }
4732 return a.id.index() < b.id.index();
4733 });
4734
4735 // form list of guards
4736 std::vector<dcon::army_id> guards_list;
4737 guards_list.reserve(state.world.army_size());
4738 for(auto a : state.world.nation_get_army_control(n)) {
4739 if(a.get_army().get_ai_activity() == uint8_t(army_activity::on_guard)) {
4740 guards_list.push_back(a.get_army().id);
4741 }
4742 }
4743
4744 // distribute target provinces
4745 uint32_t end_of_stage = 0;
4746
4747 for(uint8_t stage = uint8_t(province_class::count); stage-- > 0 && !guards_list.empty(); ) {
4748 uint32_t start_of_stage = end_of_stage;
4749
4750 for(; end_of_stage < provinces.size(); ++end_of_stage) {
4751 if(uint8_t(provinces[end_of_stage].c) != stage)
4752 break;
4753 }
4754
4755 uint32_t full_loops_through = 0;
4756 bool guard_assigned = false;
4757 do {
4758 guard_assigned = false;
4759 for(uint32_t j = start_of_stage; j < end_of_stage && !guards_list.empty(); ++j) {
4760 auto p = provinces[j].id;
4761 auto p_region = state.world.province_get_connected_region_id(provinces[j].id);
4762 assert(p_region > 0);
4763 bool p_region_is_coastal = state.province_definitions.connected_region_is_coastal[p_region - 1];
4764
4765 if(10.0f * (1 + full_loops_through) <= military::peacetime_attrition_limit(state, n, p)) {
4766 uint32_t nearest_index = 0;
4767 dcon::army_id nearest;
4768 float nearest_distance = 1.0f;
4769 for(uint32_t k = uint32_t(guards_list.size()); k-- > 0;) {
4770 auto guard_loc = state.world.army_get_location_from_army_location(guards_list[k]);
4771
4772 if(military::relative_attrition_amount(state, guards_list[k], p) >= 2.f)
4773 continue; //too heavy
4774
4775 /*
4776 // this wont work because a unit could end up in, for example, a subject's region at the end of a war
4777 // this region could be landlocked, resulting in this thinking that the unit can only be stationed in that
4778 // same region, even though it could walk out to another region
4779
4780 auto g_region = state.world.province_get_connected_region_id(guard_loc);
4781 assert(g_region > 0);
4782
4783 if(p_region != g_region && !(state.world.army_get_black_flag(guards_list[k])) && (!p_region_is_coastal || !state.province_definitions.connected_region_is_coastal[g_region - 1]))
4784 continue;
4785 */
4786
4787 if(auto d = province::sorting_distance(state, guard_loc, p); !nearest || d < nearest_distance) {
4788
4789 nearest_index = k;
4790 nearest_distance = d;
4791 nearest = guards_list[k];
4792 }
4793 }
4794
4795 // assign nearest guard
4796 if(nearest) {
4797 state.world.army_set_ai_province(nearest, p);
4798 guards_list[nearest_index] = guards_list.back();
4799 guards_list.pop_back();
4800
4801 guard_assigned = true;
4802 }
4803 }
4804 }
4805
4806 ++full_loops_through;
4807 } while(guard_assigned);
4808
4809 }
4810}
4811
4812dcon::navy_id find_transport_fleet(sys::state& state, dcon::nation_id controller) {
4813 int32_t n_size = 0;
4814 dcon::navy_id transport_fleet;
4815
4816 for(auto v : state.world.nation_get_navy_control(controller)) {
4817 if(v.get_navy().get_battle_from_navy_battle_participation())
4818 continue;
4819 auto members = v.get_navy().get_navy_membership();
4820
4821 auto tsize = int32_t(members.end() - members.begin());
4822 if(tsize <= n_size || tsize <= 1)
4823 continue;
4824
4825 n_size = tsize;
4826 transport_fleet = dcon::navy_id{};
4827
4828 fleet_activity activity = fleet_activity(v.get_navy().get_ai_activity());
4829 if(activity == fleet_activity::attacking || activity == fleet_activity::idle || activity == fleet_activity::returning_to_base) {
4830 auto in_transport = v.get_navy().get_army_transport();
4831 if(in_transport.begin() == in_transport.end()) {
4832 transport_fleet = v.get_navy();
4833 }
4834 }
4835 }
4836
4837 return transport_fleet;
4838}
4839
4841 std::vector<dcon::army_id> require_transport;
4842 require_transport.reserve(state.world.army_size());
4843
4844 for(auto ar : state.world.in_army) {
4845 if(ar.get_ai_activity() == uint8_t(army_activity::on_guard)
4846 && ar.get_ai_province()
4847 && ar.get_ai_province() != ar.get_location_from_army_location()
4848 && ar.get_controller_from_army_control()
4849 && unit_on_ai_control(state, ar)
4850 && !ar.get_arrival_time()
4851 && !ar.get_battle_from_army_battle_participation()
4852 && !ar.get_navy_from_army_transport()) {
4853
4854 auto path = ar.get_black_flag() ? province::make_unowned_land_path(state, ar.get_location_from_army_location(), ar.get_ai_province()) : province::make_land_path(state, ar.get_location_from_army_location(), ar.get_ai_province(), ar.get_controller_from_army_control(), ar);
4855 if(path.size() > 0) {
4856 auto existing_path = ar.get_path();
4857 auto new_size = uint32_t(path.size());
4858 existing_path.resize(new_size);
4859
4860 for(uint32_t i = 0; i < new_size; ++i) {
4861 assert(path[i]);
4862 existing_path[i] = path[i];
4863 }
4864 ar.set_arrival_time(military::arrival_time_to(state, ar, path.back()));
4865 ar.set_dig_in(0);
4866 } else {
4867 //Units delegated to the AI won't transport themselves on their own
4868 if(!ar.get_controller_from_army_control().get_is_player_controlled())
4869 require_transport.push_back(ar.id);
4870 }
4871 }
4872 }
4873
4874 for(uint32_t i = 0; i < require_transport.size(); ++i) {
4875 auto coastal_target_prov = state.world.army_get_location_from_army_location(require_transport[i]);
4876 auto controller = state.world.army_get_controller_from_army_control(require_transport[i]);
4877
4878 dcon::navy_id transport_fleet = find_transport_fleet(state, controller);
4879
4880 auto regs = state.world.army_get_army_membership(require_transport[i]);
4881
4882 auto tcap = military::transport_capacity(state, transport_fleet);
4883 tcap -= int32_t(regs.end() - regs.begin());
4884
4885 if(tcap < 0 || (state.world.nation_get_is_at_war(controller) && !naval_advantage(state, controller))) {
4886 for(uint32_t j = uint32_t(require_transport.size()); j-- > i + 1;) {
4887 if(state.world.army_get_controller_from_army_control(require_transport[j]) == controller) {
4888 state.world.army_set_ai_province(require_transport[j], dcon::province_id{}); // stop rechecking these units
4889 require_transport[j] = require_transport.back();
4890 require_transport.pop_back();
4891 }
4892 }
4893 state.world.army_set_ai_province(require_transport[i], dcon::province_id{}); // stop rechecking unit
4894 continue;
4895 }
4896
4897 if(!state.world.province_get_is_coast(coastal_target_prov)) {
4898 auto path = state.world.army_get_black_flag(require_transport[i])
4899 ? province::make_unowned_path_to_nearest_coast(state, coastal_target_prov)
4900 : province::make_path_to_nearest_coast(state, controller, coastal_target_prov);
4901 if(path.empty()) {
4902 state.world.army_set_ai_province(require_transport[i], dcon::province_id{}); // stop rechecking unit
4903 continue; // army could not reach coast
4904 } else {
4905 coastal_target_prov = path.front();
4906
4907 auto existing_path = state.world.army_get_path(require_transport[i]);
4908 auto new_size = uint32_t(path.size());
4909 existing_path.resize(new_size);
4910
4911 for(uint32_t k = 0; k < new_size; ++k) {
4912 assert(path[k]);
4913 existing_path[k] = path[k];
4914 }
4915 state.world.army_set_arrival_time(require_transport[i], military::arrival_time_to(state, require_transport[i], path.back()));
4916 state.world.army_set_dig_in(require_transport[i], 0);
4917 state.world.army_set_dig_in(require_transport[i], 0);
4918 }
4919 }
4920
4921 {
4922 auto fleet_destination = province::has_naval_access_to_province(state, controller, coastal_target_prov) ? coastal_target_prov : state.world.province_get_port_to(coastal_target_prov);
4923 if(fleet_destination == state.world.navy_get_location_from_navy_location(transport_fleet)) {
4924 state.world.navy_get_path(transport_fleet).clear();
4925 state.world.navy_set_arrival_time(transport_fleet, sys::date{});
4926 state.world.navy_set_ai_activity(transport_fleet, uint8_t(fleet_activity::boarding));
4927 } else if(auto fleet_path = province::make_naval_path(state, state.world.navy_get_location_from_navy_location(transport_fleet), fleet_destination); fleet_path.empty()) { // this essentially should be impossible ...
4928 continue;
4929 } else {
4930 auto existing_path = state.world.navy_get_path(transport_fleet);
4931 auto new_size = uint32_t(fleet_path.size());
4932 existing_path.resize(new_size);
4933
4934 for(uint32_t k = 0; k < new_size; ++k) {
4935 assert(fleet_path[k]);
4936 existing_path[k] = fleet_path[k];
4937 }
4938 state.world.navy_set_arrival_time(transport_fleet, military::arrival_time_to(state, transport_fleet, fleet_path.back()));
4939 state.world.navy_set_ai_activity(transport_fleet, uint8_t(fleet_activity::boarding));
4940 }
4941 }
4942
4943 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_guard));
4944
4945 auto destination_region = state.world.province_get_connected_region_id(state.world.army_get_ai_province(require_transport[i]));
4946
4947 // scoop up other armies to transport
4948 for(uint32_t j = uint32_t(require_transport.size()); j-- > i + 1;) {
4949 if(state.world.army_get_controller_from_army_control(require_transport[j]) == controller) {
4950 auto jregs = state.world.army_get_army_membership(require_transport[j]);
4951 if(tcap >= (jregs.end() - jregs.begin())) { // check if it will fit
4952 if(state.world.province_get_connected_region_id(state.world.army_get_ai_province(require_transport[j])) != destination_region)
4953 continue;
4954
4955 if(state.world.army_get_location_from_army_location(require_transport[j]) == coastal_target_prov) {
4956 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_guard));
4957 tcap -= int32_t(jregs.end() - jregs.begin());
4958 } else {
4959 auto jpath = state.world.army_get_black_flag(require_transport[j])
4960 ? province::make_land_path(state, state.world.army_get_location_from_army_location(require_transport[j]), coastal_target_prov, controller, require_transport[j])
4961 : province::make_unowned_land_path(state, state.world.army_get_location_from_army_location(require_transport[j]), coastal_target_prov);
4962 if(!jpath.empty()) {
4963 auto existing_path = state.world.army_get_path(require_transport[j]);
4964 auto new_size = uint32_t(jpath.size());
4965 existing_path.resize(new_size);
4966
4967 for(uint32_t k = 0; k < new_size; ++k) {
4968 assert(jpath[k]);
4969 existing_path[k] = jpath[k];
4970 }
4971 state.world.army_set_arrival_time(require_transport[j], military::arrival_time_to(state, require_transport[j], jpath.back()));
4972 state.world.army_set_dig_in(require_transport[j], 0);
4973 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_guard));
4974 tcap -= int32_t(jregs.end() - jregs.begin());
4975 }
4976 }
4977 }
4978
4979 require_transport[j] = require_transport.back();
4980 require_transport.pop_back();
4981 }
4982 }
4983 }
4984}
4985
4987
4988 // set armies to move into transports
4989 for(auto ar : state.world.in_army) {
4990 if(ar.get_battle_from_army_battle_participation())
4991 continue;
4992 if(ar.get_navy_from_army_transport())
4993 continue;
4994 if(ar.get_arrival_time())
4995 continue;
4996
4997 if(ar.get_ai_activity() == uint8_t(army_activity::transport_guard) || ar.get_ai_activity() == uint8_t(army_activity::transport_attack)) {
4998 auto controller = ar.get_controller_from_army_control();
4999 dcon::navy_id transports;
5000 for(auto v : controller.get_navy_control()) {
5001 if(v.get_navy().get_ai_activity() == uint8_t(fleet_activity::boarding)) {
5002 transports = v.get_navy();
5003 }
5004 }
5005 if(!transports) {
5006 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5007 ar.set_ai_province(dcon::province_id{});
5008 continue;
5009 }
5010 if(state.world.navy_get_arrival_time(transports) || state.world.navy_get_battle_from_navy_battle_participation(transports))
5011 continue; // still moving
5012
5013 auto army_location = ar.get_location_from_army_location();
5014 auto transport_location = state.world.navy_get_location_from_navy_location(transports);
5015 if(transport_location == army_location) {
5016 ar.set_navy_from_army_transport(transports);
5017 ar.set_black_flag(false);
5018 } else if(army_location.get_port_to() == transport_location) {
5019 auto existing_path = ar.get_path();
5020 existing_path.resize(1);
5021 assert(transport_location);
5022 existing_path[0] = transport_location;
5023 ar.set_arrival_time(military::arrival_time_to(state, ar, transport_location));
5024 ar.set_dig_in(0);
5025 } else { // transport arrived in inaccessible location
5026 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5027 ar.set_ai_province(dcon::province_id{});
5028 }
5029 }
5030 }
5031}
5032
5033bool army_ready_for_battle(sys::state& state, dcon::nation_id n, dcon::army_id a) {
5034 dcon::regiment_id sample_reg;
5035 auto regs = state.world.army_get_army_membership(a);
5036 if(regs.begin() != regs.end()) {
5037 sample_reg = (*regs.begin()).get_regiment().id;
5038 } else {
5039 return false;
5040 }
5041
5042
5043 auto spending_level = state.world.nation_get_effective_land_spending(n);
5044 auto max_org = 0.25f + 0.75f * spending_level;
5045
5046 return state.world.regiment_get_org(sample_reg) > 0.7f * max_org;
5047}
5048
5049void gather_to_battle(sys::state& state, dcon::nation_id n, dcon::province_id p) {
5050 for(auto ar : state.world.nation_get_army_control(n)) {
5051 army_activity activity = army_activity(ar.get_army().get_ai_activity());
5052 if(ar.get_army().get_battle_from_army_battle_participation()
5053 || ar.get_army().get_navy_from_army_transport()
5054 || ar.get_army().get_black_flag()
5055 || ar.get_army().get_arrival_time()
5056 || (activity != army_activity::on_guard && activity != army_activity::attacking && activity != army_activity::attack_gathered && activity != army_activity::attack_transport)
5057 || !army_ready_for_battle(state, n, ar.get_army())) {
5058
5059 continue;
5060 }
5061
5062 auto location = ar.get_army().get_location_from_army_location();
5063 if(location == p)
5064 continue;
5065
5066 auto sdist = province::sorting_distance(state, location, p);
5067 if(sdist > state.defines.alice_ai_gather_radius)
5068 continue;
5069
5070 auto jpath = province::make_land_path(state, location, p, n, ar.get_army());
5071 if(!jpath.empty()) {
5072
5073 auto existing_path = ar.get_army().get_path();
5074 auto new_size = uint32_t(jpath.size());
5075 existing_path.resize(new_size * 2);
5076
5077 for(uint32_t k = 0; k < new_size; ++k) {
5078 assert(jpath[k]);
5079 existing_path[new_size + k] = jpath[k];
5080 }
5081 for(uint32_t k = 1; k < new_size; ++k) {
5082 assert(jpath[k]);
5083 existing_path[new_size - k] = jpath[k];
5084 }
5085 assert(location);
5086 existing_path[0] = location;
5087 ar.get_army().set_arrival_time(military::arrival_time_to(state, ar.get_army(), jpath.back()));
5088 ar.get_army().set_dig_in(0);
5089 }
5090
5091 }
5092}
5093
5094float estimate_balanced_composition_factor(sys::state& state, dcon::army_id a) {
5095 auto regs = state.world.army_get_army_membership(a);
5096 if(regs.begin() == regs.end())
5097 return 0.0f;
5098 // account composition
5099 // Ideal composition: 4/1/4 (1 cavalry for each 4 infantry and 1 infantry for each arty)
5100 float total_str = 0.f;
5101 float str_art = 0.f;
5102 float str_inf = 0.f;
5103 float str_cav = 0.f;
5104 for(const auto reg : regs) {
5105 float str = reg.get_regiment().get_strength() * reg.get_regiment().get_org();
5106 if(auto utid = reg.get_regiment().get_type(); utid) {
5107 switch(state.military_definitions.unit_base_definitions[utid].type) {
5109 str_inf += str;
5110 break;
5112 str_cav += str;
5113 break;
5116 str_art += str;
5117 break;
5118 default:
5119 break;
5120 }
5121 }
5122 total_str += str;
5123 }
5124 if(total_str == 0.f)
5125 return 0.f;
5126 // provide continous function for each military unit composition
5127 // such that 4x times the infantry (we min with arty for equality reasons) and 1/4th of cavalry
5128 float min_cav = std::min(str_cav, str_inf * (1.f / 4.f)); // more cavalry isn't bad (if the rest of the composition is 4x/y/4x), just don't underestimate it!
5129 float scale = 1.f - math::sin(std::abs(std::min(str_art / total_str, str_inf / total_str) - (4.f * min_cav / total_str)));
5130 return total_str * scale;
5131}
5132
5133float estimate_army_defensive_strength(sys::state& state, dcon::army_id a) {
5134 float scale = state.world.army_get_controller_from_army_control(a) ? 1.f : 0.5f;
5135 // account general
5136 if(auto gen = state.world.army_get_general_from_army_leadership(a); gen) {
5137 auto n = state.world.army_get_controller_from_army_control(a);
5138 if(!n)
5139 n = state.world.national_identity_get_nation_from_identity_holder(state.national_definitions.rebel_id);
5140 auto back = state.world.leader_get_background(gen);
5141 auto pers = state.world.leader_get_personality(gen);
5142 float morale = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::org_regain)
5143 + state.world.leader_trait_get_morale(back)
5144 + state.world.leader_trait_get_morale(pers) + 1.0f;
5145 float org = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::land_organisation)
5146 + state.world.leader_trait_get_organisation(back)
5147 + state.world.leader_trait_get_organisation(pers) + 1.0f;
5148 float def = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::land_defense_modifier)
5149 + state.world.leader_trait_get_defense(back)
5150 + state.world.leader_trait_get_defense(pers) + 1.0f;
5151 scale += def * morale * org;
5152 scale += state.world.nation_get_has_gas_defense(n) ? 10.f : 0.f;
5153 }
5154 // terrain defensive bonus
5155 float terrain_bonus = state.world.province_get_modifier_values(state.world.army_get_location_from_army_location(a), sys::provincial_mod_offsets::defense);
5156 scale += terrain_bonus;
5157 float defender_fort = 1.0f + 0.1f * state.world.province_get_building_level(state.world.army_get_location_from_army_location(a), uint8_t(economy::province_building_type::fort));
5158 scale += defender_fort;
5159 // composition bonus
5160 float strength = estimate_balanced_composition_factor(state, a);
5161 return std::max(0.1f, strength * scale);
5162}
5163
5164float estimate_army_offensive_strength(sys::state& state, dcon::army_id a) {
5165 float scale = state.world.army_get_controller_from_army_control(a) ? 1.f : 0.5f;
5166 // account general
5167 if(auto gen = state.world.army_get_general_from_army_leadership(a); gen) {
5168 auto n = state.world.army_get_controller_from_army_control(a);
5169 if(!n)
5170 n = state.world.national_identity_get_nation_from_identity_holder(state.national_definitions.rebel_id);
5171 auto back = state.world.leader_get_background(gen);
5172 auto pers = state.world.leader_get_personality(gen);
5173 float morale = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::org_regain)
5174 + state.world.leader_trait_get_morale(back)
5175 + state.world.leader_trait_get_morale(pers) + 1.0f;
5176 float org = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::land_organisation)
5177 + state.world.leader_trait_get_organisation(back)
5178 + state.world.leader_trait_get_organisation(pers) + 1.0f;
5179 float atk = state.world.nation_get_modifier_values(n, sys::national_mod_offsets::land_attack_modifier)
5180 + state.world.leader_trait_get_attack(back)
5181 + state.world.leader_trait_get_attack(pers) + 1.0f;
5182 scale += atk * morale * org;
5183 scale += state.world.nation_get_has_gas_attack(n) ? 10.f : 0.f;
5184 }
5185 // composition bonus
5186 float strength = estimate_balanced_composition_factor(state, a);
5187 return std::max(0.1f, strength * scale);
5188}
5189
5190float estimate_enemy_defensive_force(sys::state& state, dcon::province_id target, dcon::nation_id by) {
5191 float strength_total = 0.f;
5192 if(state.world.nation_get_is_at_war(by)) {
5193 for(auto ar : state.world.in_army) {
5194 if(ar.get_is_retreating()
5195 || ar.get_battle_from_army_battle_participation()
5196 || ar.get_controller_from_army_control() == by)
5197 continue;
5198 auto loc = ar.get_location_from_army_location();
5199 auto sdist = province::sorting_distance(state, loc, target);
5200 if(sdist < state.defines.alice_ai_threat_radius) {
5201 auto other_nation = ar.get_controller_from_army_control();
5202 if(!other_nation || military::are_at_war(state, other_nation, by)) {
5203 strength_total += estimate_army_defensive_strength(state, ar);
5204 }
5205 }
5206 }
5207 } else { // not at war -- rebel fighting
5208 for(auto ar : state.world.province_get_army_location(target)) {
5209 auto other_nation = ar.get_army().get_controller_from_army_control();
5210 if(!other_nation) {
5211 strength_total += estimate_army_defensive_strength(state, ar.get_army());
5212 }
5213 }
5214 }
5215 return state.defines.alice_ai_offensive_strength_overestimate * strength_total;
5216}
5217
5218void assign_targets(sys::state& state, dcon::nation_id n) {
5219 struct a_str {
5220 dcon::province_id p;
5221 float str = 0.0f;
5222 };
5223 std::vector<a_str> ready_armies;
5224 ready_armies.reserve(state.world.province_size());
5225
5226 int32_t ready_count = 0;
5227 for(auto ar : state.world.nation_get_army_control(n)) {
5228 army_activity activity = army_activity(ar.get_army().get_ai_activity());
5229 if(ar.get_army().get_battle_from_army_battle_participation()
5230 || ar.get_army().get_navy_from_army_transport()
5231 || ar.get_army().get_black_flag()
5232 || ar.get_army().get_arrival_time()
5233 || activity != army_activity::on_guard
5234 || !army_ready_for_battle(state, n, ar.get_army())) {
5235
5236 continue;
5237 }
5238
5239 ++ready_count;
5240 auto loc = ar.get_army().get_location_from_army_location().id;
5241 if(std::find_if(ready_armies.begin(), ready_armies.end(), [loc](a_str const& v) { return loc == v.p; }) == ready_armies.end()) {
5242 ready_armies.push_back(a_str{ loc, 0.0f });
5243 }
5244 }
5245
5246 if(ready_armies.empty())
5247 return; // nothing to attack with
5248
5249 struct army_target {
5250 float minimal_distance;
5251 dcon::province_id location;
5252 float strength_estimate = 0.0f;
5253 };
5254
5255 /* Ourselves */
5256 std::vector<army_target> potential_targets;
5257 potential_targets.reserve(state.world.province_size());
5258 for(auto o : state.world.nation_get_province_ownership(n)) {
5259 if(!o.get_province().get_nation_from_province_control()
5260 || military::rebel_army_in_province(state, o.get_province())
5261 ) {
5262 potential_targets.push_back(
5263 army_target{ province::sorting_distance(state, o.get_province(), ready_armies[0].p), o.get_province().id, 0.0f }
5264 );
5265 }
5266 }
5267 /* Nations we're at war with OR hostile to */
5268 std::vector<dcon::nation_id> at_war_with;
5269 at_war_with.reserve(state.world.nation_size());
5270 for(auto w : state.world.nation_get_war_participant(n)) {
5271 auto attacker = w.get_is_attacker();
5272 for(auto p : w.get_war().get_war_participant()) {
5273 if(p.get_is_attacker() != attacker) {
5274 if(std::find(at_war_with.begin(), at_war_with.end(), p.get_nation().id) == at_war_with.end()) {
5275 at_war_with.push_back(p.get_nation().id);
5276 }
5277 }
5278 }
5279 }
5280 for(auto w : at_war_with) {
5281 for(auto o : state.world.nation_get_province_control(w)) {
5282 potential_targets.push_back(
5283 army_target{ province::sorting_distance(state, o.get_province(), ready_armies[0].p), o.get_province().id, 0.0f }
5284 );
5285 }
5286 for(auto o : state.world.nation_get_province_ownership(w)) {
5287 if(!o.get_province().get_nation_from_province_control()) {
5288 potential_targets.push_back(
5289 army_target{ province::sorting_distance(state, o.get_province(), ready_armies[0].p), o.get_province().id,0.0f }
5290 );
5291 }
5292 }
5293 }
5294 /* Our allies (mainly our substates, vassals) - we need to care of them! */
5295 for(const auto ovr : state.world.nation_get_overlord_as_ruler(n)) {
5296 auto w = ovr.get_subject();
5297 for(auto o : state.world.nation_get_province_ownership(w)) {
5298 if(!o.get_province().get_nation_from_province_control()
5299 || military::rebel_army_in_province(state, o.get_province())
5300 ) {
5301 potential_targets.push_back(
5302 army_target{ province::sorting_distance(state, o.get_province(), ready_armies[0].p), o.get_province().id, 0.0f }
5303 );
5304 }
5305 }
5306 }
5307
5308 for(auto& pt : potential_targets) {
5309 for(uint32_t i = uint32_t(ready_armies.size()); i-- > 1;) {
5310 auto sdist = province::sorting_distance(state, ready_armies[i].p, pt.location);
5311 if(sdist < pt.minimal_distance) {
5312 pt.minimal_distance = sdist;
5313 }
5314 }
5315 }
5316 std::sort(potential_targets.begin(), potential_targets.end(), [&](army_target& a, army_target& b) {
5317 if(a.minimal_distance != b.minimal_distance)
5318 return a.minimal_distance < b.minimal_distance;
5319 else
5320 return a.location.index() < b.location.index();
5321 });
5322
5323 // organize attack stacks
5324 bool is_at_war = state.world.nation_get_is_at_war(n);
5325 int32_t min_ready_count = std::min(ready_count, 3); //Atleast 3 attacks
5326 int32_t max_attacks_to_make = is_at_war ? std::max(min_ready_count, (ready_count + 1) / 3) : ready_count; // not at war -- allow all stacks to attack rebels
5327 auto const psize = potential_targets.size();
5328
5329 for(uint32_t i = 0; i < psize && max_attacks_to_make > 0; ++i) {
5330 if(!potential_targets[i].location)
5331 continue; // target has been removed as too close by some earlier iteration
5332 if(potential_targets[i].strength_estimate == 0.0f)
5333 potential_targets[i].strength_estimate = estimate_enemy_defensive_force(state, potential_targets[i].location, n) + 0.00001f;
5334
5335 auto target_attack_force = potential_targets[i].strength_estimate;
5336 std::sort(ready_armies.begin(), ready_armies.end(), [&](a_str const& a, a_str const& b) {
5337 auto adist = province::sorting_distance(state, a.p, potential_targets[i].location);
5338 auto bdist = province::sorting_distance(state, b.p, potential_targets[i].location);
5339 if(adist != bdist)
5340 return adist > bdist;
5341 else
5342 return a.p.index() < b.p.index();
5343 });
5344
5345 // make list of attackers
5346 float a_force_str = 0.f;
5347 int32_t k = int32_t(ready_armies.size());
5348 for(; k-- > 0 && a_force_str <= target_attack_force;) {
5349 if(ready_armies[k].str == 0.0f) {
5350 for(auto ar : state.world.province_get_army_location(ready_armies[k].p)) {
5351 if(ar.get_army().get_battle_from_army_battle_participation()
5352 || n != ar.get_army().get_controller_from_army_control()
5353 || ar.get_army().get_navy_from_army_transport()
5354 || ar.get_army().get_black_flag()
5355 || ar.get_army().get_arrival_time()
5356 || army_activity(ar.get_army().get_ai_activity()) != army_activity::on_guard
5357 || !army_ready_for_battle(state, n, ar.get_army())) {
5358
5359 continue;
5360 }
5361
5362 ready_armies[k].str += estimate_army_offensive_strength(state, ar.get_army());
5363 }
5364 ready_armies[k].str += 0.00001f;
5365 }
5366 a_force_str += ready_armies[k].str;
5367 }
5368
5369 if(a_force_str < target_attack_force) {
5370 return; // end assigning attackers completely
5371 }
5372
5373 // find central province
5374 dcon::province_id central_province;
5375
5376 glm::vec3 accumulated{ 0.0f, 0.0f, 0.0f };
5377 float minimal_distance = 2.0f;
5378
5379 for(int32_t m = int32_t(ready_armies.size()); m-- > k + 1; ) {
5380 accumulated += state.world.province_get_mid_point_b(ready_armies[m].p);
5381 }
5382 auto magnitude = math::sqrt((accumulated.x * accumulated.x + accumulated.y * accumulated.y) + accumulated.z * accumulated.z);
5383 if(magnitude > 0.00001f)
5384 accumulated /= magnitude;
5385
5386 province::for_each_land_province(state, [&](dcon::province_id p) {
5388 return;
5389 auto pmid = state.world.province_get_mid_point_b(p);
5390 if(auto dist = -((accumulated.x * pmid.x + accumulated.y * pmid.y) + accumulated.z * pmid.z); dist < minimal_distance) {
5391 minimal_distance = dist;
5392 central_province = p;
5393 }
5394 });
5395 if(!central_province)
5396 continue;
5397
5398 // issue safe-move gather command
5399 for(int32_t m = int32_t(ready_armies.size()); m-- > k + 1; ) {
5400 assert(m >= 0 && m < int32_t(ready_armies.size()));
5401 for(auto ar : state.world.province_get_army_location(ready_armies[m].p)) {
5402 if(ar.get_army().get_battle_from_army_battle_participation()
5403 || n != ar.get_army().get_controller_from_army_control()
5404 || ar.get_army().get_navy_from_army_transport()
5405 || ar.get_army().get_black_flag()
5406 || ar.get_army().get_arrival_time()
5407 || army_activity(ar.get_army().get_ai_activity()) != army_activity::on_guard
5408 || !army_ready_for_battle(state, n, ar.get_army())) {
5409
5410 continue;
5411 }
5412
5413 if(ready_armies[m].p == central_province) {
5414 ar.get_army().set_ai_province(potential_targets[i].location);
5415 ar.get_army().set_ai_activity(uint8_t(army_activity::attacking));
5416 } else if(auto path = province::make_safe_land_path(state, ready_armies[m].p, central_province, n); !path.empty()) {
5417 auto existing_path = ar.get_army().get_path();
5418 auto new_size = uint32_t(path.size());
5419 existing_path.resize(new_size);
5420
5421 for(uint32_t q = 0; q < new_size; ++q) {
5422 assert(path[q]);
5423 existing_path[q] = path[q];
5424 }
5425 ar.get_army().set_arrival_time(military::arrival_time_to(state, ar.get_army(), path.back()));
5426 ar.get_army().set_dig_in(0);
5427 ar.get_army().set_ai_province(potential_targets[i].location);
5428 ar.get_army().set_ai_activity(uint8_t(army_activity::attacking));
5429 }
5430 }
5431 }
5432
5433 ready_armies.resize(k + 1);
5434 --max_attacks_to_make;
5435
5436 // remove subsequent targets that are too close
5437 if(is_at_war) {
5438 for(uint32_t j = i + 1; j < psize; ++j) {
5439 if(province::sorting_distance(state, potential_targets[j].location, potential_targets[i].location) < state.defines.alice_ai_attack_target_radius)
5440 potential_targets[j].location = dcon::province_id{};
5441 }
5442 }
5443 }
5444}
5445
5447 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t i) {
5448 dcon::nation_id n{ dcon::nation_id::value_base_t(i) };
5449 if(state.world.nation_is_valid(n)) {
5450 assign_targets(state, n);
5451 }
5452 });
5453}
5454
5456 concurrency::parallel_for(uint32_t(0), state.world.nation_size(), [&](uint32_t i) {
5457 dcon::nation_id n{ dcon::nation_id::value_base_t(i) };
5458 if(state.world.nation_is_valid(n)) {
5459 distribute_guards(state, n);
5460 }
5461 });
5462}
5463
5465 static std::vector<dcon::army_id> require_transport;
5466 require_transport.clear();
5467
5468 for(auto ar : state.world.in_army) {
5469 if(ar.get_ai_activity() == uint8_t(army_activity::attack_transport)) {
5470 if(!ar.get_arrival_time()
5471 && !ar.get_battle_from_army_battle_participation()
5472 && !ar.get_navy_from_army_transport()
5473 && std::find(require_transport.begin(), require_transport.end(), ar.id) == require_transport.end()) {
5474
5475 // try to transport
5476 if(province::has_access_to_province(state, ar.get_controller_from_army_control(), ar.get_ai_province())) {
5477 require_transport.push_back(ar.id);
5478 } else {
5479 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5480 ar.set_ai_province(dcon::province_id{});
5481 }
5482 }
5483 } else if(ar.get_ai_activity() == uint8_t(army_activity::attack_gathered)) {
5484 if(!ar.get_arrival_time()
5485 && !ar.get_battle_from_army_battle_participation()
5486 && !ar.get_navy_from_army_transport()) {
5487
5488 if(ar.get_location_from_army_location() == ar.get_ai_province()) { // attack finished ?
5489 if(ar.get_location_from_army_location().get_nation_from_province_control() && !military::are_at_war(state, ar.get_location_from_army_location().get_nation_from_province_control(), ar.get_controller_from_army_control())) {
5490
5491 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5492 ar.set_ai_province(dcon::province_id{});
5493 }
5494 } else {
5495 if(province::has_access_to_province(state, ar.get_controller_from_army_control(), ar.get_ai_province())) {
5496 if(auto path = province::make_land_path(state, ar.get_location_from_army_location(), ar.get_ai_province(), ar.get_controller_from_army_control(), ar); path.size() > 0) {
5497
5498 auto existing_path = ar.get_path();
5499 auto new_size = uint32_t(path.size());
5500 existing_path.resize(new_size);
5501
5502 for(uint32_t i = 0; i < new_size; ++i) {
5503 assert(path[i]);
5504 existing_path[i] = path[i];
5505 }
5506 ar.set_arrival_time(military::arrival_time_to(state, ar, path.back()));
5507 ar.set_dig_in(0);
5508 } else {
5509 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5510 ar.set_ai_province(dcon::province_id{});
5511 }
5512 } else {
5513 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5514 ar.set_ai_province(dcon::province_id{});
5515 }
5516 }
5517 }
5518 } else if(ar.get_ai_activity() == uint8_t(army_activity::attacking)
5519 && ar.get_ai_province() != ar.get_location_from_army_location()
5520 && !ar.get_arrival_time()
5521 && !ar.get_battle_from_army_battle_participation()
5522 && !ar.get_navy_from_army_transport()) {
5523
5524 bool all_gathered = true;
5525 for(auto o : ar.get_controller_from_army_control().get_army_control()) {
5526 if(o.get_army().get_ai_province() == ar.get_ai_province()) {
5527 if(ar.get_location_from_army_location() != o.get_army().get_location_from_army_location()) {
5528 // an army with the same target on a different location
5529 if(o.get_army().get_path().size() > 0 && o.get_army().get_path()[0] == ar.get_location_from_army_location()) {
5530 all_gathered = false;
5531 break;
5532 }
5533 } else {
5534 // on same location
5535 if(o.get_army().get_battle_from_army_battle_participation()) { // is in a battle
5536 all_gathered = false;
5537 break;
5538 }
5539 }
5540 }
5541 }
5542
5543 if(all_gathered) {
5544 if(province::has_access_to_province(state, ar.get_controller_from_army_control(), ar.get_ai_province())) {
5545 if(ar.get_ai_province() == ar.get_location_from_army_location()) {
5546 for(auto o : ar.get_location_from_army_location().get_army_location()) {
5547 if(o.get_army().get_ai_province() == ar.get_ai_province()
5548 && o.get_army().get_path().size() == 0) {
5549
5550 o.get_army().set_ai_activity(uint8_t(army_activity::attack_gathered));
5551 }
5552 }
5553 } else if(auto path = province::make_land_path(state, ar.get_location_from_army_location(), ar.get_ai_province(), ar.get_controller_from_army_control(), ar); path.size() > 0) {
5554
5555 for(auto o : ar.get_location_from_army_location().get_army_location()) {
5556 if(o.get_army().get_ai_province() == ar.get_ai_province()
5557 && o.get_army().get_path().size() == 0) {
5558
5559 auto existing_path = o.get_army().get_path();
5560 auto new_size = uint32_t(path.size());
5561 existing_path.resize(new_size);
5562
5563 for(uint32_t i = 0; i < new_size; ++i) {
5564 assert(path[i]);
5565 existing_path[i] = path[i];
5566 }
5567 o.get_army().set_arrival_time(military::arrival_time_to(state, o.get_army(), path.back()));
5568 o.get_army().set_dig_in(0);
5569 o.get_army().set_ai_activity(uint8_t(army_activity::attack_gathered));
5570 }
5571 }
5572 } else {
5573 for(auto o : ar.get_location_from_army_location().get_army_location()) {
5574 if(o.get_army().get_ai_province() == ar.get_ai_province()
5575 && o.get_army().get_path().size() == 0) {
5576
5577 require_transport.push_back(o.get_army().id);
5578 ar.set_ai_activity(uint8_t(army_activity::attack_transport));
5579 }
5580 }
5581 }
5582 } else {
5583 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5584 ar.set_ai_province(dcon::province_id{});
5585 }
5586 }
5587 }
5588 }
5589
5590 for(uint32_t i = 0; i < require_transport.size(); ++i) {
5591 auto coastal_target_prov = state.world.army_get_location_from_army_location(require_transport[i]);
5592 auto controller = state.world.army_get_controller_from_army_control(require_transport[i]);
5593
5594 dcon::navy_id transport_fleet = find_transport_fleet(state, controller);
5595
5596 auto regs = state.world.army_get_army_membership(require_transport[i]);
5597
5598 auto tcap = military::transport_capacity(state, transport_fleet);
5599 tcap -= int32_t(regs.end() - regs.begin());
5600
5601 if(tcap < 0 || (state.world.nation_get_is_at_war(controller) && !naval_advantage(state, controller))) {
5602 for(uint32_t j = uint32_t(require_transport.size()); j-- > i + 1;) {
5603 if(state.world.army_get_controller_from_army_control(require_transport[j]) == controller) {
5604 state.world.army_set_ai_activity(require_transport[j], uint8_t(army_activity::on_guard));
5605 state.world.army_set_ai_province(require_transport[j], dcon::province_id{}); // stop rechecking these units
5606 require_transport[j] = require_transport.back();
5607 require_transport.pop_back();
5608 }
5609 }
5610 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::on_guard));
5611 state.world.army_set_ai_province(require_transport[i], dcon::province_id{}); // stop rechecking these units
5612 continue;
5613 }
5614
5615 if(!state.world.province_get_is_coast(coastal_target_prov)) {
5616 auto path = province::make_path_to_nearest_coast(state, controller, coastal_target_prov);
5617 if(path.empty()) {
5618 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::on_guard));
5619 state.world.army_set_ai_province(require_transport[i], dcon::province_id{});
5620 continue; // army could not reach coast
5621 } else {
5622 coastal_target_prov = path.front();
5623
5624 auto existing_path = state.world.army_get_path(require_transport[i]);
5625 auto new_size = uint32_t(path.size());
5626 existing_path.resize(new_size);
5627
5628 for(uint32_t k = 0; k < new_size; ++k) {
5629 assert(path[k]);
5630 existing_path[k] = path[k];
5631 }
5632 state.world.army_set_arrival_time(require_transport[i], military::arrival_time_to(state, require_transport[i], path.back()));
5633 state.world.army_set_dig_in(require_transport[i], 0);
5634 }
5635 }
5636
5637 {
5638 auto fleet_destination = province::has_naval_access_to_province(state, controller, coastal_target_prov) ? coastal_target_prov : state.world.province_get_port_to(coastal_target_prov);
5639 if(fleet_destination == state.world.navy_get_location_from_navy_location(transport_fleet)) {
5640 state.world.navy_get_path(transport_fleet).clear();
5641 state.world.navy_set_arrival_time(transport_fleet, sys::date{});
5642 state.world.navy_set_ai_activity(transport_fleet, uint8_t(fleet_activity::boarding));
5643 } else if(auto fleet_path = province::make_naval_path(state, state.world.navy_get_location_from_navy_location(transport_fleet), fleet_destination); fleet_path.empty()) {
5644 continue;
5645 } else {
5646 auto existing_path = state.world.navy_get_path(transport_fleet);
5647 auto new_size = uint32_t(fleet_path.size());
5648 existing_path.resize(new_size);
5649
5650 for(uint32_t k = 0; k < new_size; ++k) {
5651 assert(fleet_path[k]);
5652 existing_path[k] = fleet_path[k];
5653 }
5654 state.world.navy_set_arrival_time(transport_fleet, military::arrival_time_to(state, transport_fleet, fleet_path.back()));
5655 state.world.navy_set_ai_activity(transport_fleet, uint8_t(fleet_activity::boarding));
5656 }
5657 }
5658
5659 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_attack));
5660
5661 auto destination_region = state.world.province_get_connected_region_id(state.world.army_get_ai_province(require_transport[i]));
5662
5663 // scoop up other armies to transport
5664 for(uint32_t j = uint32_t(require_transport.size()); j-- > i + 1;) {
5665 if(state.world.army_get_controller_from_army_control(require_transport[j]) == controller) {
5666 auto jregs = state.world.army_get_army_membership(require_transport[j]);
5667 if(tcap >= (jregs.end() - jregs.begin())) { // check if it will fit
5668 if(state.world.province_get_connected_region_id(state.world.army_get_ai_province(require_transport[j])) != destination_region)
5669 continue;
5670
5671 if(state.world.army_get_location_from_army_location(require_transport[j]) == coastal_target_prov) {
5672 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_attack));
5673 tcap -= int32_t(jregs.end() - jregs.begin());
5674 } else {
5675 auto jpath = state.world.army_get_black_flag(require_transport[j])
5676 ? province::make_land_path(state, state.world.army_get_location_from_army_location(require_transport[j]), coastal_target_prov, controller, require_transport[j])
5677 : province::make_unowned_land_path(state, state.world.army_get_location_from_army_location(require_transport[j]), coastal_target_prov);
5678 if(!jpath.empty()) {
5679 auto existing_path = state.world.army_get_path(require_transport[j]);
5680 auto new_size = uint32_t(jpath.size());
5681 existing_path.resize(new_size);
5682
5683 for(uint32_t k = 0; k < new_size; ++k) {
5684 assert(jpath[k]);
5685 existing_path[k] = jpath[k];
5686 }
5687 state.world.army_set_arrival_time(require_transport[j], military::arrival_time_to(state, require_transport[j], jpath.back()));
5688 state.world.army_set_dig_in(require_transport[j], 0);
5689 state.world.army_set_ai_activity(require_transport[i], uint8_t(army_activity::transport_attack));
5690 tcap -= int32_t(jregs.end() - jregs.begin());
5691 }
5692 }
5693 }
5694
5695 require_transport[j] = require_transport.back();
5696 require_transport.pop_back();
5697 }
5698 }
5699 }
5700}
5701
5702bool will_upgrade_units(sys::state& state, dcon::nation_id n) {
5703 auto fid = dcon::fatten(state.world, n);
5704
5705 auto total = fid.get_active_regiments();
5706 auto unfull = 0;
5707
5708 for(auto ar : state.world.nation_get_army_control(n)) {
5709 for(auto r : ar.get_army().get_army_membership()) {
5710 if(r.get_regiment().get_strength() < 0.8f) {
5711 unfull++;
5712
5713 if(unfull > total * 0.1) {
5714 return false;
5715 }
5716 }
5717 }
5718 }
5719
5720 return true;
5721}
5722
5724 for(auto n : state.world.in_nation) {
5725 if(n.get_is_player_controlled() || n.get_owned_province_count() == 0)
5726 continue;
5727 auto disarm = n.get_disarmed_until();
5728 if(disarm && state.current_date < disarm)
5729 continue;
5730
5731 static std::vector<dcon::province_land_construction_id> hopeless_construction;
5732 hopeless_construction.clear();
5733
5734 state.world.nation_for_each_province_land_construction(n, [&](dcon::province_land_construction_id plcid) {
5735 auto fat_plc = dcon::fatten(state.world, plcid);
5736 auto prov = fat_plc.get_pop().get_province_from_pop_location();
5737 if(prov.get_nation_from_province_control() != n)
5738 hopeless_construction.push_back(plcid);
5739 });
5740
5741 for(auto item : hopeless_construction) {
5742 state.world.delete_province_land_construction(item);
5743 }
5744
5745 auto constructions = state.world.nation_get_province_land_construction(n);
5746 if(constructions.begin() != constructions.end())
5747 continue;
5748
5749 int32_t num_frontline = 0;
5750 int32_t num_support = 0;
5751
5752 // Nation-wide best unit types
5753 auto inf_type = military::get_best_infantry(state, n);
5754 auto art_type = military::get_best_artillery(state, n);
5756 if (art_type)
5757 art_def = state.military_definitions.unit_base_definitions[art_type];
5758 bool art_req_pc = art_def.primary_culture;
5759 auto cav_type = military::get_best_cavalry(state, n);
5760
5761 for(auto ar : state.world.nation_get_army_control(n)) {
5762 for(auto r : ar.get_army().get_army_membership()) {
5763 auto type = r.get_regiment().get_type();
5764 auto etype = state.military_definitions.unit_base_definitions[type].type;
5766 ++num_support;
5767 } else {
5768 ++num_frontline;
5769 }
5770
5771 /* AI units upgrade
5772 * AI upgrades units only if less than 10% of the army is currently under 80% strength (requiring supplies for reinforcement)
5773 */
5774 if(will_upgrade_units(state, n)) {
5775 auto primary_culture = r.get_regiment().get_pop_from_regiment_source().get_culture() == n.get_primary_culture();
5776
5777 // AI can upgrade into primary-culture-specific units such as guards
5778 if(primary_culture) {
5779 auto pc_adj_inf_type = military::get_best_infantry(state, n, primary_culture);
5780 auto pc_adj_art_type = military::get_best_artillery(state, n, primary_culture);
5781 auto pc_adj_cav_type = military::get_best_cavalry(state, n, primary_culture);
5782
5783 if(etype == military::unit_type::infantry && pc_adj_inf_type && military::is_infantry_better(state, n, type, pc_adj_inf_type)) {
5784 r.get_regiment().set_type(pc_adj_inf_type);
5785 r.get_regiment().set_strength(0.01f);
5786 } else if(etype == military::unit_type::support && pc_adj_art_type && military::is_artillery_better(state, n, type, pc_adj_art_type)) {
5787 r.get_regiment().set_type(pc_adj_art_type);
5788 r.get_regiment().set_strength(0.01f);
5789 } else if(etype == military::unit_type::cavalry && pc_adj_cav_type && military::is_cavalry_better(state, n, type, pc_adj_cav_type)) {
5790 r.get_regiment().set_type(pc_adj_cav_type);
5791 r.get_regiment().set_strength(0.01f);
5792 }
5793 }
5794 // Keep non-primary-culture units as nation-wide best units
5795 else {
5796 if(etype == military::unit_type::infantry && inf_type && military::is_infantry_better(state, n, type, inf_type)) {
5797 r.get_regiment().set_type(inf_type);
5798 r.get_regiment().set_strength(0.01f);
5799 } else if(etype == military::unit_type::support && art_type && military::is_artillery_better(state, n, type, art_type)) {
5800 r.get_regiment().set_type(art_type);
5801 r.get_regiment().set_strength(0.01f);
5802 } else if(etype == military::unit_type::cavalry && cav_type && military::is_cavalry_better(state, n, type, cav_type)) {
5803 r.get_regiment().set_type(cav_type);
5804 r.get_regiment().set_strength(0.01f);
5805 }
5806 }
5807 }
5808 }
5809 }
5810
5811 const auto decide_type = [&](bool pc) {
5812 if(art_type && (!art_req_pc || (art_req_pc && pc))) {
5813 if(num_frontline > num_support) {
5814 ++num_support;
5815 return art_type;
5816 } else {
5817 ++num_frontline;
5818 return inf_type;
5819 }
5820 } else {
5821 return inf_type;
5822 }
5823 };
5824
5825 auto num_to_build_nation = calculate_desired_army_size(state, n) - num_frontline - num_support;
5826
5827 for(auto p : state.world.nation_get_province_ownership(n)) {
5828 if(p.get_province().get_nation_from_province_control() != n)
5829 continue;
5830
5831 if(p.get_province().get_is_colonial()) {
5832 float divisor = state.defines.pop_size_per_regiment * state.defines.pop_min_size_for_regiment_colony_multiplier;
5833 float minimum = state.defines.pop_min_size_for_regiment;
5834
5835 for(auto pop : p.get_province().get_pop_location()) {
5836 if(pop.get_pop().get_poptype() == state.culture_definitions.soldiers) {
5837 if(pop.get_pop().get_size() >= minimum) {
5838 auto amount = int32_t((pop.get_pop().get_size() / divisor) + 1);
5839 auto regs = pop.get_pop().get_regiment_source();
5840 auto building = pop.get_pop().get_province_land_construction();
5841 auto num_to_make_local = amount - ((regs.end() - regs.begin()) + (building.end() - building.begin()));
5842 while(num_to_make_local > 0 && num_to_build_nation > 0) {
5843 auto t = decide_type(pop.get_pop().get_is_primary_or_accepted_culture());
5844 assert(command::can_start_land_unit_construction(state, n, pop.get_province(), pop.get_pop().get_culture(), t));
5845 auto c = fatten(state.world, state.world.try_create_province_land_construction(pop.get_pop().id, n));
5846 c.set_type(t);
5847 --num_to_make_local;
5848 --num_to_build_nation;
5849 }
5850 }
5851 }
5852 }
5853 } else if(!p.get_province().get_is_owner_core()) {
5854 float divisor = state.defines.pop_size_per_regiment * state.defines.pop_min_size_for_regiment_noncore_multiplier;
5855 float minimum = state.defines.pop_min_size_for_regiment;
5856
5857 dcon::pop_id non_preferred;
5858 for(auto pop : p.get_province().get_pop_location()) {
5859 if(pop.get_pop().get_poptype() == state.culture_definitions.soldiers) {
5860 if(pop.get_pop().get_size() >= minimum) {
5861 auto amount = int32_t((pop.get_pop().get_size() / divisor) + 1);
5862 auto regs = pop.get_pop().get_regiment_source();
5863 auto building = pop.get_pop().get_province_land_construction();
5864 auto num_to_make_local = amount - ((regs.end() - regs.begin()) + (building.end() - building.begin()));
5865 while(num_to_make_local > 0 && num_to_build_nation > 0) {
5866 auto t = decide_type(pop.get_pop().get_is_primary_or_accepted_culture());
5867 assert(command::can_start_land_unit_construction(state, n, pop.get_province(), pop.get_pop().get_culture(), t));
5868 auto c = fatten(state.world, state.world.try_create_province_land_construction(pop.get_pop().id, n));
5869 c.set_type(t);
5870 --num_to_make_local;
5871 --num_to_build_nation;
5872 }
5873 }
5874 }
5875 }
5876 } else {
5877 float divisor = state.defines.pop_size_per_regiment;
5878 float minimum = state.defines.pop_min_size_for_regiment;
5879
5880 dcon::pop_id non_preferred;
5881 for(auto pop : p.get_province().get_pop_location()) {
5882 if(pop.get_pop().get_poptype() == state.culture_definitions.soldiers) {
5883 if(pop.get_pop().get_size() >= minimum) {
5884 auto amount = int32_t((pop.get_pop().get_size() / divisor) + 1);
5885 auto regs = pop.get_pop().get_regiment_source();
5886 auto building = pop.get_pop().get_province_land_construction();
5887 auto num_to_make_local = amount - ((regs.end() - regs.begin()) + (building.end() - building.begin()));
5888 while(num_to_make_local > 0 && num_to_build_nation) {
5889 auto t = decide_type(pop.get_pop().get_is_primary_or_accepted_culture());
5890 assert(command::can_start_land_unit_construction(state, n, pop.get_province(), pop.get_pop().get_culture(), t));
5891 auto c = fatten(state.world, state.world.try_create_province_land_construction(pop.get_pop().id, n));
5892 c.set_type(t);
5893 --num_to_make_local;
5894 --num_to_build_nation;
5895 }
5896 }
5897 }
5898 }
5899 }
5900 }
5901 }
5902}
5903
5905 for(auto ar : state.world.in_army) {
5906 auto controller = ar.get_controller_from_army_control();
5907 if(controller
5908 && unit_on_ai_control(state, ar)
5909 && !ar.get_battle_from_army_battle_participation()
5910 && !ar.get_navy_from_army_transport()
5911 && !ar.get_arrival_time()) {
5912
5913 auto location = ar.get_location_from_army_location();
5914
5915 if(ar.get_black_flag() || army_activity(ar.get_ai_activity()) == army_activity::unspecified) {
5916 auto regs = ar.get_army_membership();
5917 if(regs.begin() == regs.end()) {
5918 // empty army -- cleanup will get it
5919 } else if(regs.end() - regs.begin() > 1) {
5920 // existing multi-unit formation
5921 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5922 } else {
5923 auto type = (*regs.begin()).get_regiment().get_type();
5924 auto etype = state.military_definitions.unit_base_definitions[type].type;
5925 auto is_art = etype == military::unit_type::support;
5926 dcon::province_id target_location;
5927 float nearest_distance = 1.0f;
5928
5929 // find army to merge with
5930 for(auto o : controller.get_army_control()) {
5931 auto other_location = o.get_army().get_location_from_army_location();
5932 auto sdist = province::sorting_distance(state, other_location, location);
5933 if(army_activity(o.get_army().get_ai_activity()) == army_activity::on_guard
5934 && other_location.get_connected_region_id() == location.get_connected_region_id()
5935 && (!target_location || sdist < nearest_distance)) {
5936
5937 int32_t num_support = 0;
5938 int32_t num_frontline = 0;
5939 for(auto r : o.get_army().get_army_membership()) {
5940 auto stype = r.get_regiment().get_type();
5941 auto setype = state.military_definitions.unit_base_definitions[stype].type;
5943 ++num_support;
5944 } else {
5945 ++num_frontline;
5946 }
5947 }
5948
5949 if((is_art && num_support < 5) || (!is_art && num_frontline < 5)) {
5950 target_location = other_location;
5951 nearest_distance = sdist;
5952 }
5953 }
5954 }
5955
5956 if(target_location) {
5957 if(target_location == location) {
5958 ar.set_ai_province(target_location);
5959 ar.set_ai_activity(uint8_t(army_activity::merging));
5960 } else if(auto path = province::make_land_path(state, location, target_location, controller, ar); path.size() > 0) {
5961 auto existing_path = ar.get_path();
5962 auto new_size = uint32_t(path.size());
5963 existing_path.resize(new_size);
5964
5965 for(uint32_t i = 0; i < new_size; ++i) {
5966 assert(path[i]);
5967 existing_path[i] = path[i];
5968 }
5969 ar.set_arrival_time(military::arrival_time_to(state, ar, path.back()));
5970 ar.set_dig_in(0);
5971 ar.set_ai_province(target_location);
5972 ar.set_ai_activity(uint8_t(army_activity::merging));
5973 } else {
5974 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5975 }
5976 } else {
5977 ar.set_ai_activity(uint8_t(army_activity::on_guard));
5978 }
5979 }
5980 } else if(army_activity(ar.get_ai_activity()) == army_activity::merging) {
5981 auto regs = ar.get_army_membership();
5982 if(regs.begin() == regs.end()) {
5983 // empty army -- cleanup will get it
5984 continue;
5985 }
5986 auto type = (*regs.begin()).get_regiment().get_type();
5987 auto etype = state.military_definitions.unit_base_definitions[type].type;
5988 auto is_art = etype == military::unit_type::support;
5989 for(auto o : location.get_army_location()) {
5990 if(o.get_army().get_ai_activity() == uint8_t(army_activity::on_guard)
5991 && o.get_army().get_controller_from_army_control() == controller) {
5992
5993 int32_t num_support = 0;
5994 int32_t num_frontline = 0;
5995 for(auto r : o.get_army().get_army_membership()) {
5996 auto stype = r.get_regiment().get_type();
5997 auto setype = state.military_definitions.unit_base_definitions[stype].type;
5999 ++num_support;
6000 } else {
6001 ++num_frontline;
6002 }
6003 }
6004
6005 if((is_art && num_support < 5) || (!is_art && num_frontline < 5)) {
6006 (*regs.begin()).get_regiment().set_army_from_army_membership(o.get_army());
6007 break;
6008 }
6009 }
6010 }
6011 ar.set_ai_activity(uint8_t(army_activity::unspecified)); // if merging fails, this will try to find a merge target again
6012 }
6013 }
6014 }
6015
6016}
6017
6019 auto v = state.current_date.value;
6020 auto r = v % 8;
6021
6022 switch(r) {
6023 case 0:
6024 pickup_idle_ships(state);
6025 break;
6026 case 1:
6027 move_idle_guards(state);
6028 break;
6029 case 2:
6030 new_units_and_merging(state);
6031 break;
6032 case 3:
6034 break;
6035 case 4:
6037 break;
6038 case 5:
6039 move_idle_guards(state);
6040 break;
6041 case 6:
6042 break;
6043 case 7:
6045 break;
6046 }
6047
6048 auto d = v % 100;
6049
6050 if(d == 0) {
6051 for(auto nation : state.world.in_nation) {
6052 if(state.world.nation_get_is_player_controlled(nation)) {
6053 continue;
6054 }
6055
6056 if(nation.get_is_great_power()) {
6057 nation.set_ai_strategy(ai_strategies::militant);
6058 }
6059 else {
6060 nation.set_ai_strategy(ai_strategies::industrious);
6061 }
6062 }
6063 }
6064}
6065
6066float estimate_rebel_strength(sys::state& state, dcon::province_id p) {
6067 float v = 0.f;
6068 for(auto ar : state.world.province_get_army_location(p))
6069 if(ar.get_army().get_controller_from_army_rebel_control())
6070 v += estimate_army_defensive_strength(state, ar.get_army());
6071 return v;
6072}
6073
6074}
#define assert(condition)
Definition: debug.h:74
Definition: ai.cpp:13
void update_crisis_leaders(sys::state &state)
Definition: ai.cpp:2334
void get_craved_factory_types(sys::state &state, dcon::nation_id nid, dcon::market_id mid, std::vector< dcon::factory_type_id > &desired_types)
Definition: ai.cpp:1063
void initialize_ai_tech_weights(sys::state &state)
Definition: ai.cpp:451
void place_instance_in_result_war(sys::state &state, std::vector< possible_cb > &result, dcon::nation_id n, dcon::nation_id target, dcon::war_id w, dcon::cb_type_id cb, std::vector< dcon::state_instance_id > const &target_states)
Definition: ai.cpp:3010
void daily_cleanup(sys::state &state)
Definition: ai.cpp:4266
bool naval_supremacy(sys::state &state, dcon::nation_id n, dcon::nation_id target)
Definition: ai.cpp:3649
void take_reforms(sys::state &state)
Definition: ai.cpp:2022
void form_alliances(sys::state &state)
Definition: ai.cpp:158
float estimate_balanced_composition_factor(sys::state &state, dcon::army_id a)
Definition: ai.cpp:5094
void sort_available_declaration_cbs(std::vector< possible_cb > &result, sys::state &state, dcon::nation_id n, dcon::nation_id target)
Definition: ai.cpp:3154
void update_ai_ruling_party(sys::state &state)
Definition: ai.cpp:1030
void make_war_decs(sys::state &state)
Definition: ai.cpp:3673
void upgrade_colonies(sys::state &state)
Definition: ai.cpp:2004
void add_free_ai_cbs_to_war(sys::state &state, dcon::nation_id n, dcon::war_id w)
Definition: ai.cpp:3207
void update_cb_fabrication(sys::state &state)
Definition: ai.cpp:2926
bool ai_will_accept_alliance(sys::state &state, dcon::nation_id target, dcon::nation_id from)
Definition: ai.cpp:283
bool will_accept_peace_offer(sys::state &state, dcon::nation_id n, dcon::nation_id from, dcon::peace_offer_id p)
Definition: ai.cpp:3531
void pickup_idle_ships(sys::state &state)
Definition: ai.cpp:4437
void state_target_list(std::vector< dcon::state_instance_id > &result, sys::state &state, dcon::nation_id for_nation, dcon::nation_id within)
Definition: ai.cpp:2264
void make_defense(sys::state &state)
Definition: ai.cpp:5455
void assign_targets(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:5218
float estimate_army_offensive_strength(sys::state &state, dcon::army_id a)
Definition: ai.cpp:5164
bool ai_will_grant_access(sys::state &state, dcon::nation_id target, dcon::nation_id from)
Definition: ai.cpp:351
float estimate_army_defensive_strength(sys::state &state, dcon::army_id a)
Definition: ai.cpp:5133
void refresh_home_ports(sys::state &state)
Definition: ai.cpp:4258
province_class
Definition: ai.cpp:4641
void send_fleet_home(sys::state &state, dcon::navy_id n, fleet_activity moving_status=fleet_activity::returning_to_base, fleet_activity at_base=fleet_activity::idle)
Definition: ai.cpp:4305
bool ai_is_close_enough(sys::state &state, dcon::nation_id target, dcon::nation_id from)
Definition: ai.cpp:251
bool set_fleet_target(sys::state &state, dcon::nation_id n, dcon::province_id start, dcon::navy_id for_navy)
Definition: ai.cpp:4327
void remove_ai_data(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:4008
void sort_possible_justification_cbs(std::vector< possible_cb > &result, sys::state &state, dcon::nation_id n, dcon::nation_id target)
Definition: ai.cpp:2789
bool valid_construction_target(sys::state &state, dcon::nation_id from, dcon::nation_id target)
Definition: ai.cpp:2890
void distribute_guards(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:4656
fleet_activity
Definition: constants.hpp:610
bool will_be_crisis_primary_attacker(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:2094
possible_cb pick_fabrication_type(sys::state &state, dcon::nation_id from, dcon::nation_id target)
Definition: ai.cpp:2844
float estimate_enemy_defensive_force(sys::state &state, dcon::province_id target, dcon::nation_id by)
Definition: ai.cpp:5190
bool will_join_crisis_with_offer(sys::state &state, dcon::nation_id n, sys::full_wg offer)
Definition: ai.cpp:2241
void update_ai_colony_starting(sys::state &state)
Definition: ai.cpp:1954
bool ai_offer_cb(sys::state &state, dcon::cb_type_id t)
Definition: ai.cpp:2250
army_activity
Definition: constants.hpp:622
void gather_to_battle(sys::state &state, dcon::nation_id n, dcon::province_id p)
Definition: ai.cpp:5049
crisis_str estimate_crisis_str(sys::state &state)
Definition: ai.cpp:2188
void update_naval_transport(sys::state &state)
Definition: ai.cpp:4986
void update_focuses(sys::state &state)
Definition: ai.cpp:785
void get_desired_factory_types(sys::state &state, dcon::nation_id nid, dcon::market_id mid, std::vector< dcon::factory_type_id > &desired_types)
Definition: ai.cpp:1095
int32_t future_rebels_in_nation(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:1267
dcon::province_id get_home_port(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:4238
void add_wg_to_great_war(sys::state &state, dcon::nation_id n, dcon::war_id w)
Definition: ai.cpp:3288
constexpr float ally_overestimate
Definition: ai.cpp:281
float estimate_strength(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:19
void place_instance_in_result(sys::state &state, std::vector< possible_cb > &result, dcon::nation_id n, dcon::nation_id target, dcon::cb_type_id cb, std::vector< dcon::state_instance_id > const &target_states)
Definition: ai.cpp:2707
void update_budget(sys::state &state)
Definition: ai.cpp:3800
bool ai_can_appoint_political_party(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:1016
void new_units_and_merging(sys::state &state)
Definition: ai.cpp:5904
void move_gathered_attackers(sys::state &state)
Definition: ai.cpp:5464
float estimate_rebel_strength(sys::state &state, dcon::province_id p)
Definition: ai.cpp:6066
void update_ai_colonial_investment(sys::state &state)
Definition: ai.cpp:1895
dcon::cb_type_id pick_gw_extra_cb_type(sys::state &state, dcon::nation_id from, dcon::nation_id target)
Definition: ai.cpp:3230
void perform_influence_actions(sys::state &state)
Definition: ai.cpp:726
void make_attacks(sys::state &state)
Definition: ai.cpp:5446
void update_ships(sys::state &state)
Definition: ai.cpp:4031
void update_ai_econ_construction(sys::state &state)
Definition: ai.cpp:1344
bool will_join_war(sys::state &state, dcon::nation_id n, dcon::war_id w, bool as_attacker)
Definition: ai.cpp:2979
void build_ships(sys::state &state)
Definition: ai.cpp:4095
void prune_alliances(sys::state &state)
Definition: ai.cpp:187
float estimate_pop_party_support(sys::state &state, dcon::nation_id n, dcon::political_party_id pid)
Definition: ai.cpp:1004
void update_war_intervention(sys::state &state)
Definition: ai.cpp:2647
dcon::navy_id find_transport_fleet(sys::state &state, dcon::nation_id controller)
Definition: ai.cpp:4812
ai_strategies
Definition: ai.cpp:15
@ technological
Definition: ai.cpp:16
@ militant
Definition: ai.cpp:16
@ industrious
Definition: ai.cpp:16
void get_state_desired_factory_types(sys::state &state, dcon::nation_id nid, dcon::market_id mid, std::vector< dcon::factory_type_id > &desired_types)
Definition: ai.cpp:1204
float estimate_defensive_strength(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:29
void unload_units_from_transport(sys::state &state, dcon::navy_id n)
Definition: ai.cpp:4373
bool unit_on_ai_control(sys::state &state, dcon::army_id a)
Definition: ai.cpp:4018
void explain_ai_access_reasons(sys::state &state, dcon::nation_id target, text::layout_base &contents, int32_t indent)
Definition: ai.cpp:371
void explain_ai_alliance_reasons(sys::state &state, dcon::nation_id target, text::layout_base &contents, int32_t indent)
Definition: ai.cpp:332
void update_influence_priorities(sys::state &state)
Definition: ai.cpp:600
void update_ai_research(sys::state &state)
Definition: ai.cpp:375
bool navy_needs_repair(sys::state &state, dcon::navy_id n)
Definition: ai.cpp:4271
void update_ai_general_status(sys::state &state)
Definition: ai.cpp:58
void update_factory_types_priority(sys::state &state)
Definition: ai.cpp:496
void update_land_constructions(sys::state &state)
Definition: ai.cpp:5723
int16_t calculate_desired_army_size(sys::state &state, dcon::nation_id nation)
Definition: ai.cpp:1279
bool will_be_crisis_primary_defender(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:2136
void sort_available_cbs(std::vector< possible_cb > &result, sys::state &state, dcon::nation_id n, dcon::war_id w)
Definition: ai.cpp:3101
void identify_focuses(sys::state &state)
Definition: ai.cpp:766
void civilize(sys::state &state)
Definition: ai.cpp:2014
void make_peace_offers(sys::state &state)
Definition: ai.cpp:3360
bool will_accept_crisis_peace_offer(sys::state &state, dcon::nation_id to, bool is_concession, bool missing_wg)
Definition: ai.cpp:2546
bool naval_advantage(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:4293
bool army_ready_for_battle(sys::state &state, dcon::nation_id n, dcon::army_id a)
Definition: ai.cpp:5033
void take_ai_decisions(sys::state &state)
Definition: ai.cpp:923
bool will_upgrade_units(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:5702
void general_ai_unit_tick(sys::state &state)
Definition: ai.cpp:6018
float estimate_additional_offensive_strength(sys::state &state, dcon::nation_id n, dcon::nation_id target)
Definition: ai.cpp:44
bool has_cores_occupied(sys::state &state, dcon::nation_id n)
Definition: ai.cpp:3342
bool will_accept_peace_offer_value(sys::state &state, dcon::nation_id n, dcon::nation_id from, dcon::nation_id prime_attacker, dcon::nation_id prime_defender, float primary_warscore, float scoreagainst_me, bool offer_from_attacker, bool concession, int32_t overall_po_value, int32_t my_po_target, int32_t target_personal_po_value, int32_t potential_peace_score_against, int32_t my_side_against_target, int32_t my_side_peace_cost, int32_t war_duration, bool contains_sq)
Definition: ai.cpp:3454
void move_idle_guards(sys::state &state)
Definition: ai.cpp:4840
void add_gw_goals(sys::state &state)
Definition: ai.cpp:3330
dcon::nation_id pick_gw_target(sys::state &state, dcon::nation_id from, dcon::war_id w, bool is_attacker)
Definition: ai.cpp:3257
bool merge_fleet(sys::state &state, dcon::navy_id n, dcon::province_id p, dcon::nation_id owner)
Definition: ai.cpp:4403
void get_state_craved_factory_types(sys::state &state, dcon::nation_id nid, dcon::market_id mid, std::vector< dcon::factory_type_id > &desired_types)
Definition: ai.cpp:1184
bool can_increase_opinion(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target)
Definition: commands.cpp:1302
void execute_send_peace_offer(sys::state &state, dcon::nation_id source)
Definition: commands.cpp:3159
bool can_send_crisis_peace_offer(sys::state &state, dcon::nation_id source)
Definition: commands.cpp:3222
bool can_add_to_sphere(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target)
Definition: commands.cpp:1439
void execute_start_crisis_peace_offer(sys::state &state, dcon::nation_id source, bool is_concession)
Definition: commands.cpp:2998
bool can_intervene_in_war(sys::state &state, dcon::nation_id source, dcon::war_id w, bool for_attacker)
Definition: commands.cpp:1697
void execute_declare_war(sys::state &state, dcon::nation_id source, dcon::nation_id target, dcon::cb_type_id primary_cb, dcon::state_definition_id cb_state, dcon::national_identity_id cb_tag, dcon::nation_id cb_secondary_nation, bool call_attacker_allies, bool run_conference)
Definition: commands.cpp:2746
bool can_start_crisis_peace_offer(sys::state &state, dcon::nation_id source, bool is_concession)
Definition: commands.cpp:2989
void execute_add_to_sphere(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target)
Definition: commands.cpp:1471
void execute_intervene_in_war(sys::state &state, dcon::nation_id source, dcon::war_id w, bool for_attacker)
Definition: commands.cpp:1780
bool can_set_national_focus(sys::state &state, dcon::nation_id source, dcon::state_instance_id target_state, dcon::national_focus_id focus)
Definition: commands.cpp:122
void execute_start_peace_offer(sys::state &state, dcon::nation_id source, dcon::nation_id target, dcon::war_id war, bool is_concession)
Definition: commands.cpp:2973
bool can_start_peace_offer(sys::state &state, dcon::nation_id source, dcon::nation_id target, dcon::war_id war, bool is_concession)
Definition: commands.cpp:2932
bool can_start_land_unit_construction(sys::state &state, dcon::nation_id source, dcon::province_id location, dcon::culture_id soldier_culture, dcon::unit_type_id type, dcon::province_id template_province)
Definition: commands.cpp:705
void execute_remove_from_sphere(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target, dcon::nation_id affected_gp)
Definition: commands.cpp:1532
void execute_send_crisis_peace_offer(sys::state &state, dcon::nation_id source)
Definition: commands.cpp:3233
void execute_increase_opinion(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target)
Definition: commands.cpp:1330
bool can_send_peace_offer(sys::state &state, dcon::nation_id source)
Definition: commands.cpp:3153
bool can_add_to_crisis_peace_offer(sys::state &state, dcon::nation_id source, dcon::nation_id wargoal_from, dcon::nation_id target, dcon::cb_type_id primary_cb, dcon::state_definition_id cb_state, dcon::national_identity_id cb_tag, dcon::nation_id cb_secondary_nation)
Definition: commands.cpp:3091
bool can_remove_from_sphere(sys::state &state, dcon::nation_id source, dcon::nation_id influence_target, dcon::nation_id affected_gp)
Definition: commands.cpp:1499
void execute_cancel_alliance(sys::state &state, dcon::nation_id source, dcon::nation_id target)
Definition: commands.cpp:2685
bool can_cancel_alliance(sys::state &state, dcon::nation_id source, dcon::nation_id target, bool ignore_cost)
Definition: commands.cpp:2665
bool can_declare_war(sys::state &state, dcon::nation_id source, dcon::nation_id target, dcon::cb_type_id primary_cb, dcon::state_definition_id cb_state, dcon::national_identity_id cb_tag, dcon::nation_id cb_secondary_nation)
Definition: commands.cpp:2709
bool can_enact_issue(sys::state &state, dcon::nation_id source, dcon::issue_option_id i)
Definition: commands.cpp:1915
bool can_start_naval_unit_construction(sys::state &state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province)
Definition: commands.cpp:646
float effective_technology_cost(sys::state &state, uint32_t current_year, dcon::nation_id target_nation, dcon::technology_id tech_id)
Definition: culture.cpp:969
constexpr uint64_t to_bits(dcon::ideology_id id)
Definition: culture.hpp:92
pop_satisfaction_wrapper_fat fatten(data_container const &c, pop_satisfaction_wrapper_id id) noexcept
constexpr dcon::demographics_key rich_militancy(10)
constexpr dcon::demographics_key middle_total(21)
constexpr dcon::demographics_key total(0)
constexpr dcon::demographics_key poor_militancy(8)
constexpr dcon::demographics_key middle_militancy(9)
dcon::demographics_key to_key(sys::state const &state, dcon::pop_type_id v)
constexpr dcon::demographics_key rich_total(22)
constexpr dcon::demographics_key poor_total(20)
dcon::demographics_key to_employment_key(sys::state const &state, dcon::pop_type_id v)
void post(sys::state &state, message const &m)
Pushes a diplomatic message to the list of pending diplomatic requests for the specified recipient (m...
float factory_type_output_cost(sys::state &state, dcon::nation_id n, dcon::market_id m, dcon::factory_type_id factory_type)
Definition: economy.cpp:7578
float estimate_construction_spending_from_budget(sys::state &state, dcon::nation_id n, float current_budget)
Definition: economy.cpp:3433
int32_t state_factory_count(sys::state &state, dcon::state_instance_id sid, dcon::nation_id n)
Definition: economy.cpp:8347
float supply(sys::state &state, dcon::market_id s, dcon::commodity_id c)
Definition: economy.cpp:206
float estimate_pop_payouts_by_income_type(sys::state &state, dcon::nation_id n, culture::income_type in)
Definition: economy.cpp:8023
float estimate_domestic_investment(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:8147
float estimate_overseas_penalty_spending(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:4041
float estimate_land_spending(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:8177
float estimate_daily_income(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:8660
float price(sys::state const &state, dcon::state_instance_id s, dcon::commodity_id c)
Definition: economy.cpp:150
constexpr dcon::commodity_id money(0)
float factory_type_build_cost(sys::state &state, dcon::nation_id n, dcon::market_id m, dcon::factory_type_id factory_type)
Definition: economy.cpp:7559
float demand_satisfaction(sys::state &state, dcon::market_id s, dcon::commodity_id c)
Definition: economy.cpp:395
float estimate_naval_spending(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:8194
void bound_budget_settings(sys::state &state, dcon::nation_id n)
Definition: economy.cpp:8812
const float days_prepaid
Definition: economy.cpp:4940
float demand(sys::state &state, dcon::market_id s, dcon::commodity_id c)
Definition: economy.cpp:283
province_building_type
Definition: constants.hpp:578
float factory_type_input_cost(sys::state &state, dcon::nation_id n, dcon::market_id m, dcon::factory_type_id factory_type)
Definition: economy.cpp:7591
void execute(sys::state &state, dcon::effect_key key, int32_t primary, int32_t this_slot, int32_t from_slot, uint32_t r_lo, uint32_t r_hi)
Definition: effects.cpp:5413
constexpr uint32_t pop_open_factory_invest
Definition: culture.hpp:25
constexpr uint32_t pop_expand_factory_invest
Definition: culture.hpp:24
constexpr uint32_t pop_open_factory
Definition: culture.hpp:16
constexpr uint32_t build_railway
Definition: culture.hpp:38
constexpr uint32_t pop_build_factory_invest
Definition: culture.hpp:23
constexpr uint32_t build_factory
Definition: culture.hpp:8
constexpr uint32_t pop_expand_factory
Definition: culture.hpp:15
constexpr uint32_t build_bank
Definition: culture.hpp:41
constexpr uint32_t build_university
Definition: culture.hpp:42
constexpr uint32_t pop_build_factory
Definition: culture.hpp:14
constexpr uint32_t expand_factory
Definition: culture.hpp:9
float sin(float x) noexcept
Definition: math_fns.hpp:18
float sqrt(float x) noexcept
Definition: math_fns.hpp:70
constexpr uint32_t po_transfer_provinces
Definition: military.hpp:23
constexpr uint32_t po_demand_state
Definition: military.hpp:19
constexpr uint32_t all_allowed_states
Definition: military.hpp:14
constexpr uint32_t po_annex
Definition: military.hpp:18
constexpr uint32_t po_status_quo
Definition: military.hpp:27
constexpr uint32_t is_not_constructing_cb
Definition: military.hpp:12
constexpr uint32_t always
Definition: military.hpp:10
bool province_is_under_siege(sys::state const &state, dcon::province_id ids)
Definition: military.cpp:603
bool are_allied_in_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
Definition: military.cpp:662
bool cb_conditions_satisfied(sys::state &state, dcon::nation_id actor, dcon::nation_id target, dcon::cb_type_id cb)
Definition: military.cpp:404
float primary_warscore(sys::state &state, dcon::war_id w)
Definition: military.cpp:3997
bool is_artillery_better(sys::state &state, dcon::nation_id n, dcon::unit_type_id best, dcon::unit_type_id given)
Definition: military.cpp:57
float directed_warscore(sys::state &state, dcon::war_id w, dcon::nation_id primary, dcon::nation_id secondary)
Definition: military.cpp:4054
bool rebel_army_in_province(sys::state &state, dcon::province_id p)
Definition: military.cpp:7775
int32_t defender_peace_cost(sys::state &state, dcon::war_id war)
Definition: military.cpp:1952
bool can_use_cb_against(sys::state &state, dcon::nation_id from, dcon::nation_id target)
Definition: military.cpp:245
dcon::unit_type_id get_best_artillery(sys::state &state, dcon::nation_id n, bool primary_culture)
Definition: military.cpp:130
bool is_cavalry_better(sys::state &state, dcon::nation_id n, dcon::unit_type_id best, dcon::unit_type_id given)
Definition: military.cpp:74
bool has_truce_with(sys::state &state, dcon::nation_id attacker, dcon::nation_id target)
Definition: military.cpp:2296
war_role get_role(sys::state const &state, dcon::war_id w, dcon::nation_id n)
Definition: military.cpp:2501
int32_t attacker_peace_cost(sys::state &state, dcon::war_id war)
Definition: military.cpp:1941
bool is_attacker(sys::state &state, dcon::war_id w, dcon::nation_id n)
Definition: military.cpp:2493
bool is_infantry_better(sys::state &state, dcon::nation_id n, dcon::unit_type_id best, dcon::unit_type_id given)
Definition: military.cpp:37
dcon::unit_type_id get_best_cavalry(sys::state &state, dcon::nation_id n, bool primary_culture)
Definition: military.cpp:158
void add_wargoal(sys::state &state, dcon::war_id wfor, dcon::nation_id added_by, dcon::nation_id target, dcon::cb_type_id type, dcon::state_definition_id sd, dcon::national_identity_id tag, dcon::nation_id secondary_nation)
Definition: military.cpp:2668
bool cb_requires_selection_of_a_valid_nation(sys::state const &state, dcon::cb_type_id t)
Definition: military.cpp:2057
dcon::unit_type_id get_best_infantry(sys::state &state, dcon::nation_id n, bool primary_culture)
Definition: military.cpp:96
sys::date arrival_time_to(sys::state &state, dcon::army_id a, dcon::province_id p)
Definition: military.cpp:4284
bool cb_instance_conditions_satisfied(sys::state &state, dcon::nation_id actor, dcon::nation_id target, dcon::cb_type_id cb, dcon::state_definition_id st, dcon::national_identity_id tag, dcon::nation_id secondary)
Definition: military.cpp:471
float cb_infamy(sys::state &state, dcon::cb_type_id t, dcon::nation_id target, dcon::state_definition_id cb_state)
Definition: military.cpp:1519
bool are_at_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
Definition: military.cpp:649
bool attackers_have_status_quo_wargoal(sys::state const &state, dcon::war_id w)
Definition: military.cpp:775
int32_t transport_capacity(sys::state &state, dcon::navy_id n)
Definition: military.cpp:6843
float relative_attrition_amount(sys::state &state, dcon::navy_id a, dcon::province_id prov)
Definition: military.cpp:5389
bool cb_requires_selection_of_a_liberatable_tag(sys::state const &state, dcon::cb_type_id t)
Definition: military.cpp:2061
float peacetime_attrition_limit(sys::state &state, dcon::nation_id n, dcon::province_id prov)
Definition: military.cpp:5344
int32_t peace_cost(sys::state &state, dcon::war_id war, dcon::cb_type_id wargoal, dcon::nation_id from, dcon::nation_id target, dcon::nation_id secondary_nation, dcon::state_definition_id wargoal_state, dcon::national_identity_id wargoal_tag)
Definition: military.cpp:1799
bool defenders_have_status_quo_wargoal(sys::state const &state, dcon::war_id w)
Definition: military.cpp:768
bool war_goal_would_be_duplicate(sys::state &state, dcon::nation_id source, dcon::war_id w, dcon::nation_id target, dcon::cb_type_id cb_type, dcon::state_definition_id cb_state, dcon::national_identity_id cb_tag, dcon::nation_id cb_secondary_nation)
Definition: military.cpp:7693
bool cb_requires_selection_of_a_state(sys::state const &state, dcon::cb_type_id t)
Definition: military.cpp:2065
constexpr uint8_t is_banned
Definition: nations.hpp:183
constexpr uint8_t level_friendly
Definition: nations.hpp:172
constexpr uint8_t priority_three
Definition: nations.hpp:179
constexpr uint8_t priority_one
Definition: nations.hpp:177
constexpr uint8_t level_in_sphere
Definition: nations.hpp:173
constexpr uint8_t level_mask
Definition: nations.hpp:167
constexpr uint8_t priority_two
Definition: nations.hpp:178
bool is_great_power(sys::state const &state, dcon::nation_id id)
Definition: nations.cpp:1068
void enact_reform(sys::state &state, dcon::nation_id source, dcon::reform_option_id r)
Definition: nations.cpp:3350
int32_t free_colonial_points(sys::state &state, dcon::nation_id n)
Definition: nations.cpp:1304
bool are_allied(sys::state &state, dcon::nation_id a, dcon::nation_id b)
Definition: nations.cpp:685
void make_alliance(sys::state &state, dcon::nation_id a, dcon::nation_id b)
Definition: nations.cpp:2037
void enact_issue(sys::state &state, dcon::nation_id source, dcon::issue_option_id i)
Definition: nations.cpp:3395
bool is_landlocked(sys::state &state, dcon::nation_id n)
Definition: nations.cpp:690
void make_civilized(sys::state &state, dcon::nation_id n)
Definition: nations.cpp:3218
void adjust_relationship(sys::state &state, dcon::nation_id a, dcon::nation_id b, float delta)
Definition: nations.cpp:1660
int32_t max_national_focuses(sys::state &state, dcon::nation_id n)
Definition: nations.cpp:1183
void post(sys::state &state, message &&m)
float get_military_reform_multiplier(sys::state &state, dcon::nation_id n)
Definition: politics.cpp:293
float get_economic_reform_multiplier(sys::state &state, dcon::nation_id n)
Definition: politics.cpp:308
bool can_appoint_ruling_party(sys::state &state, dcon::nation_id nation)
Definition: politics.cpp:51
bool political_party_is_active(sys::state &state, dcon::nation_id n, dcon::political_party_id p)
Definition: politics.cpp:323
bool has_elections(sys::state &state, dcon::nation_id nation)
Definition: politics.cpp:60
void appoint_ruling_party(sys::state &state, dcon::nation_id n, dcon::political_party_id p)
Definition: politics.cpp:364
bool is_election_ongoing(sys::state &state, dcon::nation_id nation)
Definition: politics.cpp:55
std::vector< dcon::province_id > make_land_path(sys::state &state, dcon::province_id start, dcon::province_id end, dcon::nation_id nation_as, dcon::army_id a)
Definition: province.cpp:1863
float sorting_distance(sys::state &state, dcon::province_id a, dcon::province_id b)
Definition: province.cpp:1752
void upgrade_colonial_state(sys::state &state, dcon::nation_id source, dcon::state_instance_id si)
Definition: province.cpp:679
std::vector< dcon::province_id > make_safe_land_path(sys::state &state, dcon::province_id start, dcon::province_id end, dcon::nation_id nation_as)
Definition: province.cpp:1930
bool has_access_to_province(sys::state &state, dcon::nation_id nation_as, dcon::province_id prov)
Definition: province.cpp:1787
bool has_naval_base_being_built(sys::state &state, dcon::province_id id)
Definition: province.cpp:434
bool is_overseas(sys::state const &state, dcon::province_id ids)
Definition: province.cpp:19
bool has_safe_access_to_province(sys::state &state, dcon::nation_id nation_as, dcon::province_id prov)
Definition: province.cpp:1816
void for_each_province_in_state_instance(sys::state &state, dcon::state_instance_id s, F const &func)
std::vector< dcon::province_id > make_path_to_nearest_coast(sys::state &state, dcon::nation_id nation_as, dcon::province_id start)
Definition: province.cpp:2282
bool has_province_building_being_built(sys::state &state, dcon::province_id id, economy::province_building_type t)
Definition: province.cpp:460
bool can_integrate_colony(sys::state &state, dcon::state_instance_id id)
Definition: province.cpp:643
bool fast_can_start_colony(sys::state &state, dcon::nation_id n, dcon::state_definition_id d, int32_t free_points, dcon::province_id coastal_target, bool &adjacent)
Definition: province.cpp:1384
bool state_is_coastal(sys::state &state, dcon::state_instance_id s)
Definition: province.cpp:1666
void for_each_land_province(sys::state &state, F const &func)
std::vector< dcon::province_id > make_naval_path(sys::state &state, dcon::province_id start, dcon::province_id end)
Definition: province.cpp:2094
std::vector< dcon::province_id > make_unowned_land_path(sys::state &state, dcon::province_id start, dcon::province_id end)
Definition: province.cpp:2044
bool has_naval_access_to_province(sys::state &state, dcon::nation_id nation_as, dcon::province_id prov)
Definition: province.cpp:1760
bool has_fort_being_built(sys::state &state, dcon::province_id id)
Definition: province.cpp:413
std::vector< dcon::province_id > make_unowned_path_to_nearest_coast(sys::state &state, dcon::province_id start)
Definition: province.cpp:2334
bool state_borders_nation(sys::state &state, dcon::nation_id n, dcon::state_instance_id si)
Definition: province.cpp:1237
void increase_colonial_investment(sys::state &state, dcon::nation_id source, dcon::state_definition_id state_def)
Definition: province.cpp:1487
uint32_t reduce(uint32_t value_in, uint32_t upper_bound)
Definition: prng.cpp:46
uint64_t get_random(sys::state const &state, uint32_t value_in)
Definition: prng.cpp:8
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression, cppcoreguidelines-noexcept-swap, performance-noexcept-swap) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:24635
void add_line(sys::state &state, layout_base &dest, dcon::text_key txt, int32_t indent)
Definition: text.cpp:1923
void add_line_with_condition(sys::state &state, layout_base &dest, std::string_view key, bool condition_met, int32_t indent)
Definition: text.cpp:1979
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
Definition: text.cpp:617
int32_t to_generic(dcon::province_id v)
Definition: triggers.hpp:12
bool evaluate(sys::state &state, dcon::trigger_key key, int32_t primary, int32_t this_slot, int32_t from_slot)
Definition: triggers.cpp:5895
void effect_description(sys::state &state, text::layout_base &layout, dcon::effect_key k, int32_t primary_slot, int32_t this_slot, int32_t from_slot, uint32_t r_lo, uint32_t r_hi)
uint uint32_t
uchar uint8_t
province_class c
Definition: ai.cpp:4653
dcon::province_id id
Definition: ai.cpp:4652
dcon::cb_type_id cb
Definition: ai.cpp:2703
dcon::nation_id target
Definition: ai.cpp:2699
dcon::state_definition_id state_def
Definition: ai.cpp:2702
dcon::national_identity_id associated_tag
Definition: ai.cpp:2701
dcon::nation_id secondary_nation
Definition: ai.cpp:2700
Holds data regarding a diplomatic message between two specified nations at a certain date,...
static constexpr uint32_t set_size
dcon::nation_id target_nation
dcon::cb_type_id cb
static constexpr uint32_t modifier_definition_size
Definition: modifiers.hpp:231
Holds important data about the game world, state, and other data regarding windowing,...