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