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