Project Alice
Loading...
Searching...
No Matches
system_state.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <functional>
3#include <thread>
4#include "system_state.hpp"
5#include "dcon_generated.hpp"
6#include "map_modes.hpp"
7#include "opengl_wrapper.hpp"
8#include "window.hpp"
11#include "gui_console.hpp"
12#include "gui_minimap.hpp"
13#include "gui_unit_panel.hpp"
14#include "gui_topbar.hpp"
17#include "gui_event.hpp"
18#include "gui_map_icons.hpp"
21#include "gui_naval_combat.hpp"
22#include "gui_land_combat.hpp"
23#include "gui_chat_window.hpp"
24#include "gui_state_select.hpp"
25#include "gui_error_window.hpp"
27#include "map_tooltip.hpp"
28#include "unit_tooltip.hpp"
29#include "demographics.hpp"
30#include "rebels.hpp"
31#include "ai.hpp"
32#include "effects.hpp"
33#include "gui_leader_select.hpp"
34#include "gui_land_combat.hpp"
35#include "gui_nation_picker.hpp"
36#include "gui_end_window.hpp"
37#include "gui_map_legend.hpp"
38#include "gui_unit_grid_box.hpp"
39#include "blake2.h"
40#include "fif_common.hpp"
41#include "gui_deserialize.hpp"
42
43namespace ui {
44
46 state.ui_state.lazy_load_in_game = true;
47
48 state.ui_state.unit_details_box = ui::make_element_by_type<ui::grid_box>(state, state.ui_state.defs_by_name.find(state.lookup_key("alice_grid_panel"))->second.definition);
49 state.ui_state.unit_details_box->set_visible(state, false);
50
51 // dummy ui root for economy explorer
52 state.ui_state.economy_viewer_root = std::make_unique<ui::element_base>();
53
54 //
55 state.ui_state.select_states_legend = ui::make_element_by_type<ui::map_state_select_window>(state, state.ui_state.defs_by_name.find(state.lookup_key("alice_select_legend_window"))->second.definition);
56
57 // create ui for army selector
58 {
59 {
60 auto key = state.lookup_key("alice_armygroup_selection_control_panel");
61 auto def = state.ui_state.defs_by_name.find(key)->second.definition;
62 auto window = ui::make_element_by_type<ui::battleplanner_selection_control>(state, def);
63 state.ui_state.army_group_selector_root->add_child_to_front(std::move(window));
64 }
65 {
66 auto key = state.lookup_key("alice_armygroup_exit_units_selection");
67 auto def = state.ui_state.defs_by_name.find(key)->second.definition;
68 auto button = ui::make_element_by_type<ui::go_to_battleplanner_button>(state, def);
69 state.ui_state.army_group_selector_root->add_child_to_front(std::move(button));
70 }
71 }
72
73 // create ui for battleplanner
74 {
75 state.world.for_each_province([&](dcon::province_id id) {
76 auto ptr = ui::make_element_by_type<ui::army_group_counter_window>(state, "alice_army_group_on_map");
77 static_cast<ui::army_group_counter_window*>(ptr.get())->prov = id;
78 state.ui_state.military_root->add_child_to_front(std::move(ptr));
79 });
80
81 {
82 auto key = state.lookup_key("alice_army_group_regiments_list");
83 auto def = state.ui_state.defs_by_name.find(key)->second.definition;
84 auto new_elm_army_group = ui::make_element_by_type<ui::army_group_details_window>(state, def);
85 state.ui_state.army_group_window_land = static_cast<ui::army_group_details_window*>(new_elm_army_group.get());
86 new_elm_army_group->set_visible(state, true);
87 state.ui_state.military_root->add_child_to_front(std::move(new_elm_army_group));
88 }
89
90 {
91 auto key = state.lookup_key("alice_exit_battleplanner");
92 auto def = state.ui_state.defs_by_name.find(key)->second.definition;
93 auto button = ui::make_element_by_type<ui::go_to_base_game_button>(state, def);
94 state.ui_state.military_root->add_child_to_front(std::move(button));
95 }
96
97 {
98 auto key = state.lookup_key("alice_battleplanner_control_panel");
99 auto def = state.ui_state.defs_by_name.find(key)->second.definition;
100 auto window = ui::make_element_by_type<ui::battleplanner_control>(state, def);
101 state.ui_state.military_root->add_child_to_front(std::move(window));
102 }
103 }
104
105
106 state.ui_state.end_screen = std::make_unique<ui::container_base>();
107 {
108 auto ewin = ui::make_element_by_type<ui::end_window>(state, state.ui_state.defs_by_name.find(state.lookup_key("back_end"))->second.definition);
109 state.ui_state.end_screen->add_child_to_front(std::move(ewin));
110 }
111 {
112 auto window = ui::make_element_by_type<ui::console_window>(state, "alice_console_window");
113 state.ui_state.console_window = window.get();
114 state.ui_state.root->add_child_to_front(std::move(window));
115 }
116 state.world.for_each_province([&](dcon::province_id id) {
117 if(state.world.province_get_port_to(id)) {
118 auto ptr = ui::make_element_by_type<ui::port_window>(state, "alice_port_icon");
119 static_cast<ui::port_window*>(ptr.get())->set_province(state, id);
120 state.ui_state.units_root->add_child_to_front(std::move(ptr));
121 }
122 });
123
124 state.world.for_each_province([&](dcon::province_id id) {
125 auto ptr = ui::make_element_by_type<ui::unit_counter_window>(state, "alice_map_unit");
126 static_cast<ui::unit_counter_window*>(ptr.get())->prov = id;
127 state.ui_state.units_root->add_child_to_front(std::move(ptr));
128 });
129 state.world.for_each_province([&](dcon::province_id id) {
130 auto ptr = ui::make_element_by_type<ui::rgo_icon>(state, "alice_rgo_mapicon");
131 static_cast<ui::rgo_icon*>(ptr.get())->content = id;
132 state.ui_state.rgos_root->add_child_to_front(std::move(ptr));
133 });
134 province::for_each_land_province(state, [&](dcon::province_id id) {
135 auto ptr = ui::make_element_by_type<ui::province_details_container>(state, "alice_province_values");
136 static_cast<ui::province_details_container*>(ptr.get())->prov = id;
137 state.ui_state.province_details_root->add_child_to_front(std::move(ptr));
138 });
139 {
140 auto new_elm = ui::make_element_by_type<ui::chat_message_listbox<false>>(state, "chat_list");
141 new_elm->base_data.position.x += 156; // nudge
142 new_elm->base_data.position.y += 24; // nudge
143 new_elm->impl_on_update(state);
144 state.ui_state.tl_chat_list = new_elm.get();
145 state.ui_state.root->add_child_to_front(std::move(new_elm));
146 }
147 {
148 auto new_elm = ui::make_element_by_type<ui::outliner_window>(state, "outliner");
149 state.ui_state.outliner_window = new_elm.get();
150 new_elm->impl_on_update(state);
151 state.ui_state.root->add_child_to_front(std::move(new_elm));
152 // Has to be created AFTER the outliner window
153 // The topbar has this button within, however since the button isn't properly displayed, it is better to make
154 // it into an independent element of it's own, living freely on the UI root so it can be flexibly moved around when
155 // the window is resized for example.
156 for(size_t i = state.ui_defs.gui.size(); i-- > 0;) {
157 auto gdef = dcon::gui_def_id(dcon::gui_def_id::value_base_t(i));
158 if(state.to_string_view(state.ui_defs.gui[gdef].name) == "topbar_outlinerbutton_bg") {
159 auto new_bg = ui::make_element_by_type<ui::outliner_button>(state, gdef);
160 state.ui_state.root->add_child_to_front(std::move(new_bg));
161 break;
162 }
163 }
164 // Then create button atop
165 for(size_t i = state.ui_defs.gui.size(); i-- > 0;) {
166 auto gdef = dcon::gui_def_id(dcon::gui_def_id::value_base_t(i));
167 if(state.to_string_view(state.ui_defs.gui[gdef].name) == "topbar_outlinerbutton") {
168 auto new_btn = ui::make_element_by_type<ui::outliner_button>(state, gdef);
169 new_btn->impl_on_update(state);
170 state.ui_state.root->add_child_to_front(std::move(new_btn));
171 break;
172 }
173 }
174 }
175 {
176 auto new_elm = ui::make_element_by_type<ui::minimap_container_window>(state, "alice_menubar");
177 state.ui_state.menubar_window = new_elm.get();
178 state.ui_state.root->add_child_to_front(std::move(new_elm));
179 }
180 {
181 auto new_elm = ui::make_element_by_type<ui::minimap_picture_window>(state, "minimap_pic");
182 state.ui_state.root->add_child_to_front(std::move(new_elm));
183 }
184 {
185 auto new_elm = ui::make_element_by_type<ui::province_view_window>(state, "province_view");
186 state.ui_state.root->add_child_to_front(std::move(new_elm));
187 }
188 {
189 auto new_elm_army = ui::make_element_by_type<ui::unit_details_window<dcon::army_id>>(state, "sup_unit_status");
190 state.ui_state.army_status_window = static_cast<ui::unit_details_window<dcon::army_id>*>(new_elm_army.get());
191 new_elm_army->set_visible(state, false);
192 state.ui_state.root->add_child_to_front(std::move(new_elm_army));
193
194 auto new_elm_navy = ui::make_element_by_type<ui::unit_details_window<dcon::navy_id>>(state, "sup_unit_status");
195 state.ui_state.navy_status_window = static_cast<ui::unit_details_window<dcon::navy_id>*>(new_elm_navy.get());
196 new_elm_navy->set_visible(state, false);
197 state.ui_state.root->add_child_to_front(std::move(new_elm_navy));
198 }
199
200 {
201 auto mselection = ui::make_element_by_type<ui::mulit_unit_selection_panel>(state, "alice_multi_unitpanel");
202 state.ui_state.multi_unit_selection_window = mselection.get();
203 mselection->set_visible(state, false);
204 state.ui_state.root->add_child_to_front(std::move(mselection));
205 }
206 {
207 auto new_elm = ui::make_element_by_type<ui::diplomacy_request_window>(state, "defaultdialog");
208 state.ui_state.request_window = new_elm.get();
209 state.ui_state.root->add_child_to_front(std::move(new_elm));
210 }
211 {
212 auto new_elm = ui::make_element_by_type<ui::message_window>(state, "defaultpopup");
213 state.ui_state.msg_window = new_elm.get();
214 state.ui_state.root->add_child_to_front(std::move(new_elm));
215 }
216 {
217 auto new_elm = ui::make_element_by_type<ui::leader_selection_window>(state, "alice_leader_selection_panel");
218 state.ui_state.change_leader_window = new_elm.get();
219 state.ui_state.root->add_child_to_front(std::move(new_elm));
220 }
221 {
222 auto new_elm = ui::make_element_by_type<ui::naval_combat_end_popup>(state, "endofnavalcombatpopup");
223 new_elm->set_visible(state, false);
224 state.ui_state.root->add_child_to_front(std::move(new_elm));
225 }
226 {
227 auto new_elm = ui::make_element_by_type<ui::naval_combat_window>(state, "alice_naval_combat");
228 new_elm->set_visible(state, false);
229 state.ui_state.root->add_child_to_front(std::move(new_elm));
230 }
231 {
232 auto new_elm = ui::make_element_by_type<ui::land_combat_window>(state, "alice_land_combat");
233 new_elm->set_visible(state, false);
234 state.ui_state.root->add_child_to_front(std::move(new_elm));
235 }
236 {
237 auto new_elm = ui::make_element_by_type<ui::topbar_window>(state, "topbar");
238 new_elm->impl_on_update(state);
239 state.ui_state.root->add_child_to_front(std::move(new_elm));
240 }
241 {
242 auto legend_win = ui::make_element_by_type<ui::map_legend_gradient>(state, "alice_map_legend_gradient_window");
243 state.ui_state.map_gradient_legend = legend_win.get();
244 state.ui_state.root->add_child_to_front(std::move(legend_win));
245 }
246 {
247 auto legend_win = ui::make_element_by_type<ui::map_legend_civ_level>(state, "alice_map_legend_civ_level");
248 state.ui_state.map_civ_level_legend = legend_win.get();
249 state.ui_state.root->add_child_to_front(std::move(legend_win));
250 }
251 {
252 auto legend_win = ui::make_element_by_type<ui::map_legend_col>(state, "alice_map_legend_colonial");
253 state.ui_state.map_col_legend = legend_win.get();
254 state.ui_state.root->add_child_to_front(std::move(legend_win));
255 }
256 {
257 auto legend_win = ui::make_element_by_type<ui::map_legend_dip>(state, "alice_map_legend_diplomatic");
258 state.ui_state.map_dip_legend = legend_win.get();
259 state.ui_state.root->add_child_to_front(std::move(legend_win));
260 }
261 {
262 auto legend_win = ui::make_element_by_type<ui::map_legend_rr>(state, "alice_map_legend_infrastructure");
263 state.ui_state.map_rr_legend = legend_win.get();
264 state.ui_state.root->add_child_to_front(std::move(legend_win));
265 }
266 {
267 auto legend_win = ui::make_element_by_type<ui::map_legend_nav>(state, "alice_map_legend_naval");
268 state.ui_state.map_nav_legend = legend_win.get();
269 state.ui_state.root->add_child_to_front(std::move(legend_win));
270 }
271 {
272 auto legend_win = ui::make_element_by_type<ui::map_legend_rank>(state, "alice_map_legend_rank");
273 state.ui_state.map_rank_legend = legend_win.get();
274 state.ui_state.root->add_child_to_front(std::move(legend_win));
275 }
276 {
277 auto legend_win = ui::make_element_by_type<ui::map_legend_rec>(state, "alice_map_legend_rec");
278 state.ui_state.map_rec_legend = legend_win.get();
279 state.ui_state.root->add_child_to_front(std::move(legend_win));
280 }
281 { // And the other on the normal in game UI
282 auto new_elm = ui::make_element_by_type<ui::chat_window>(state, "ingame_lobby_window");
283 new_elm->set_visible(state, !(state.network_mode == sys::network_mode_type::single_player)); // Hidden in singleplayer by default
284 state.ui_state.chat_window = new_elm.get(); // Default for singleplayer is the in-game one, lobby one is useless in sp
285 state.ui_state.root->add_child_to_front(std::move(new_elm));
286 }
287 state.ui_state.rgos_root->impl_on_update(state);
288 state.ui_state.units_root->impl_on_update(state);
289}
290}
291
292namespace sys {
293
295 if(state_selection) {
296 state_selection->on_cancel(*this);
297 }
298 state_selection = data;
299
301
303 ui_state.select_states_legend->impl_on_update(*this);
304 }
305}
306
307void state::state_select(dcon::state_definition_id sdef) {
309 if(std::find(state_selection->selectable_states.begin(), state_selection->selectable_states.end(), sdef) != state_selection->selectable_states.end()) {
310 if(state_selection->single_state_select) {
311 state_selection->on_select(*this, sdef);
313 } else {
314 /*auto it = std::find(state.selected_states.begin(), state.selected_states.end(), sdef);
315 if(it == state.selected_states.end()) {
316 on_select(sdef);
317 } else {
318 state.selected_states.erase(std::remove(state.selected_states.begin(), state.selected_states.end(), sdef), state.selected_states.end());
319 }*/
320 std::abort();
321 }
322 }
323 map_state.update(*this);
324}
325
326//
327// window event functions
328//
329
330void state::on_rbutton_down(int32_t x, int32_t y, key_modifiers mod) {
331 game_scene::on_rbutton_down(*this, x, y, mod);
332}
333
334void state::on_mbutton_down(int32_t x, int32_t y, key_modifiers mod) {
335 // Lose focus on text
336 ui_state.edit_target = nullptr;
338}
339
340void state::on_lbutton_down(int32_t x, int32_t y, key_modifiers mod) {
343 else
344 game_scene::on_lbutton_down(*this, x, y, mod);
345}
346
347void state::on_rbutton_up(int32_t x, int32_t y, key_modifiers mod) { }
348void state::on_mbutton_up(int32_t x, int32_t y, key_modifiers mod) {
349 map_state.on_mbuttom_up(x, y, mod);
350}
351void state::on_lbutton_up(int32_t x, int32_t y, key_modifiers mod) {
352 game_scene::on_lbutton_up(*this, x, y, mod);
353}
354void state::on_mouse_move(int32_t x, int32_t y, key_modifiers mod) {
360 }
361 } else if(ui_state.under_mouse != nullptr) {
364 }
366 auto mx = int32_t(x / user_settings.ui_scale);
367 auto my = int32_t(y / user_settings.ui_scale);
368 if(mx < ui_state.target_ul_bounds.x || mx > ui_state.target_lr_bounds.x || my < ui_state.target_ul_bounds.y || my > ui_state.target_lr_bounds.y) {
369
372 }
373 }
374}
375void state::on_mouse_drag(int32_t x, int32_t y, key_modifiers mod) { // called when the left button is held down
376 is_dragging = true;
380 int32_t(y / user_settings.ui_scale), mod);
381 }
382}
383void state::on_drag_finished(int32_t x, int32_t y, key_modifiers mod) { // called when the left button is released after one or more drag events
386 ui_state.drag_target = nullptr;
387 }
388}
389void state::on_resize(int32_t x, int32_t y, window::window_state win_state) {
391 ogl::initialize_msaa(*this, x, y);
392
393 if(win_state != window::window_state::minimized) {
394 ui_state.root->base_data.size.x = int16_t(x / user_settings.ui_scale);
395 ui_state.root->base_data.size.y = int16_t(y / user_settings.ui_scale);
398 }
399 }
400}
401
402void state::on_mouse_wheel(int32_t x, int32_t y, key_modifiers mod, float amount) { // an amount of 1.0 is one "click" of the wheel
403 //update en demand
404 ui::element_base* root_elm = current_scene.get_root(*this);
405 ui_state.scroll_target = root_elm->impl_probe_mouse(*this,
409
410 auto belongs_on_map = [&](ui::element_base* b) {
411 while(b != nullptr) {
412 if(b == ui_state.units_root.get())
413 return true;
414 if(b == ui_state.unit_details_box.get())
415 return true;
416 b = b->parent;
417 }
418 return false;
419 };
420
421 if(ui_state.scroll_target != nullptr) {
423 } else if(ui_state.under_mouse == nullptr || belongs_on_map(ui_state.under_mouse)) {
424 map_state.on_mouse_wheel(x, y, x_size, y_size, mod, amount);
425
429 }
430 }
431}
433 if(keycode == virtual_key::CONTROL)
435 if(keycode == virtual_key::SHIFT || keycode == virtual_key::LSHIFT || keycode == virtual_key::RSHIFT)
437
438 game_scene::on_key_down(*this, keycode, mod);
439}
440
442 if(keycode == virtual_key::CONTROL)
443 ui_state.ctrl_held_down = false;
444 if(keycode == virtual_key::SHIFT || keycode == virtual_key::LSHIFT || keycode == virtual_key::RSHIFT)
446
447 //TODO: move to according scenes
449 if(keycode == sys::virtual_key::W)
450 keycode = sys::virtual_key::UP;
451 else if(keycode == sys::virtual_key::A)
452 keycode = sys::virtual_key::LEFT;
453 else if(keycode == sys::virtual_key::S)
454 keycode = sys::virtual_key::DOWN;
455 else if(keycode == sys::virtual_key::D)
456 keycode = sys::virtual_key::RIGHT;
457 }
458
459 map_state.on_key_up(keycode, mod);
460}
461void state::on_text(char32_t c) { // c is win1250 codepage value
463 ui_state.edit_target->on_text(*this, c);
464}
465
466inline constexpr int32_t tooltip_width = 400;
467
468void state::render() { // called to render the frame may (and should) delay returning until the frame is rendered, including
469 // waiting for vsync
471 return;
472
473 auto game_state_was_updated = game_state_updated.exchange(false, std::memory_order::acq_rel);
474 if(game_state_was_updated && !current_scene.starting_scene && !ui_state.lazy_load_in_game) {
478 }
479 auto ownership_update = province_ownership_changed.exchange(false, std::memory_order::acq_rel);
480 if(ownership_update) {
483 }
484 }
485 if(game_state_was_updated) {
487 }
488
489 std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
490 if(ui_state.last_render_time == std::chrono::time_point<std::chrono::steady_clock>{}) {
492 }
493 if(ui_state.fps_timer > 20) {
494 auto microseconds_since_last_render = std::chrono::duration_cast<std::chrono::microseconds>(now - ui_state.last_render_time);
495 auto frames_per_second = 1.f / float(microseconds_since_last_render.count() / 1e6);
496 ui_state.last_fps = frames_per_second;
499 }
500 ui_state.fps_timer += 1;
501
502 if(ui_state.scrollbar_timer > 500 * (ui_state.last_fps / 60)) {
504 if(ui_state.left_mouse_hold_target != nullptr) {
507 }
508 }
509
510 if(ui_state.left_mouse_hold_target != nullptr) {
512 }
513
514 current_scene.clean_up(*this);
515
516 ui::element_base* root_elm = current_scene.get_root(*this);
517
518 root_elm->base_data.size.x = ui_state.root->base_data.size.x;
519 root_elm->base_data.size.y = ui_state.root->base_data.size.y;
520
521 auto mouse_probe = root_elm->impl_probe_mouse(*this, int32_t(mouse_x_position / user_settings.ui_scale),
523 auto tooltip_probe = root_elm->impl_probe_mouse(*this, int32_t(mouse_x_position / user_settings.ui_scale),
525
526 if(!mouse_probe.under_mouse && map_state.get_zoom() > map::zoom_close) {
527 mouse_probe = current_scene.recalculate_mouse_probe(*this, mouse_probe, tooltip_probe);
528 tooltip_probe = current_scene.recalculate_tooltip_probe(*this, mouse_probe, tooltip_probe);
529 }
530 ui::urect tooltip_bounds;
531 int32_t tooltip_sub_index = -1;
532 if(tooltip_probe.under_mouse) {
533 tooltip_probe.under_mouse->tooltip_position(*this, int32_t(mouse_x_position / user_settings.ui_scale),
534 int32_t(mouse_y_position / user_settings.ui_scale), tooltip_sub_index, tooltip_bounds);
535 }
536
537 if(game_state_was_updated) {
538 if(!ui_state.tech_queue.empty()) {
539 if(!world.nation_get_current_research(local_player_nation)) {
540 for(auto it = ui_state.tech_queue.begin(); it != ui_state.tech_queue.end(); it++) {
541 if(world.nation_get_active_technologies(local_player_nation, *it)) {
542 ui_state.tech_queue.erase(it);
543 break;
544 }
546 // can research, so research it
548 ui_state.tech_queue.erase(it);
549 break;
550 }
551 }
552 }
553 }
554
555 world.for_each_automated_army_group([&](dcon::automated_army_group_id item) {
560 });
561
563
565 // Processing of (gamestate <=> ui) queues
567 // National events
568 auto* c1 = new_n_event.front();
569 while(c1) {
570 auto auto_choice = world.national_event_get_auto_choice(c1->e);
571 if(auto_choice == 0) {
572 ui::new_event_window(*this, *c1);
573 if(world.national_event_get_is_major(c1->e)) {
575 } else {
577 }
578 } else {
579 command::make_event_choice(*this, *c1, uint8_t(auto_choice - 1));
580 }
582 c1 = new_n_event.front();
583 }
584 // Free national events
585 auto* c2 = new_f_n_event.front();
586 while(c2) {
587 auto auto_choice = world.free_national_event_get_auto_choice(c2->e);
588 if(auto_choice == 0) {
589 ui::new_event_window(*this, *c2);
590 if(world.free_national_event_get_is_major(c2->e)) {
592 } else {
594 }
595 } else {
596 command::make_event_choice(*this, *c2, uint8_t(auto_choice - 1));
597 }
599 c2 = new_f_n_event.front();
600 }
601 // Provincial events
602 auto* c3 = new_p_event.front();
603 while(c3) {
604 auto auto_choice = world.provincial_event_get_auto_choice(c3->e);
605 if(auto_choice == 0) {
606 ui::new_event_window(*this, *c3);
608 } else {
609 command::make_event_choice(*this, *c3, uint8_t(auto_choice - 1));
610 }
612 c3 = new_p_event.front();
613 }
614 // Free provincial events
615 auto* c4 = new_f_p_event.front();
616 while(c4) {
617 auto auto_choice = world.free_provincial_event_get_auto_choice(c4->e);
618 if(auto_choice == 0) {
619 ui::new_event_window(*this, *c4);
621 } else {
622 command::make_event_choice(*this, *c4, uint8_t(auto_choice - 1));
623 }
625 c4 = new_f_p_event.front();
626 }
627 // land battle reports
628 {
629 auto* lr = land_battle_reports.front();
630 while(lr) {
632 if(lr->player_on_winning_side == true && (!lr->attacking_nation || !lr->defending_nation)) {
635 } else {
636 //do not pester user with defeat of rebels
637 }
638 } else {
640 }
641 }
644 }
645 }
646 // naval battle reports
647 {
648 auto* lr = naval_battle_reports.front();
649 while(lr) {
653 }
654 }
655 // Diplomatic messages
656 auto* c5 = new_requests.front();
657 bool had_diplo_msg = false;
658 while(c5) {
660 static_cast<ui::diplomacy_request_window*>(ui_state.request_window)->messages.push_back(*c5);
661 } else {
662 static_cast<ui::diplomatic_message_topbar_listbox*>(ui_state.request_topbar_listbox)->messages.push_back(*c5);
663 }
664 had_diplo_msg = true;
666 c5 = new_requests.front();
667 }
668 if(had_diplo_msg) {
670 }
671
672 // Log messages
673 auto* c6 = new_messages.front();
674 while(c6) {
675 auto base_type = c6->type;
676 auto setting_types = sys::message_setting_map[int32_t(base_type)];
677 uint8_t settings_bits = 0;
678 if(setting_types.source != sys::message_setting_type::count) {
679 if(c6->source == local_player_nation) {
680 settings_bits |= user_settings.self_message_settings[int32_t(setting_types.source)];
681 } else if(notification::nation_is_interesting(*this, c6->source)) {
682 settings_bits |= user_settings.interesting_message_settings[int32_t(setting_types.source)];
683 } else {
684 settings_bits |= user_settings.other_message_settings[int32_t(setting_types.source)];
685 }
686 }
687 if(setting_types.target != sys::message_setting_type::count) {
688 if(c6->target == local_player_nation) {
689 settings_bits |= user_settings.self_message_settings[int32_t(setting_types.target)];
690 } else if(notification::nation_is_interesting(*this, c6->target)) {
691 settings_bits |= user_settings.interesting_message_settings[int32_t(setting_types.target)];
692 } else {
693 settings_bits |= user_settings.other_message_settings[int32_t(setting_types.target)];
694 }
695 }
696 if(setting_types.third != sys::message_setting_type::count) {
697 if(c6->third == local_player_nation) {
698 settings_bits |= user_settings.self_message_settings[int32_t(setting_types.third)];
699 } else if(notification::nation_is_interesting(*this, c6->third)) {
700 settings_bits |= user_settings.interesting_message_settings[int32_t(setting_types.third)];
701 } else {
702 settings_bits |= user_settings.other_message_settings[int32_t(setting_types.third)];
703 }
704 }
705
706 if((settings_bits & message_response::log) && ui_state.msg_log_window) {
707 static_cast<ui::message_log_window*>(ui_state.msg_log_window)->messages.push_back(*c6);
708 }
709 if(settings_bits & message_response::popup) {
710 if(c6->source == local_player_nation && (base_type == message_base_type::major_event || base_type == message_base_type::national_event || base_type == message_base_type::province_event)) {
711 // do nothing -- covered by event window logic
712 } else {
713 if(ui_state.msg_window) {
714 static_cast<ui::message_window*>(ui_state.msg_window)->messages.push_back(*c6);
715 }
717 ui_pause.store(true, std::memory_order_release);
718 }
719 }
720 }
721
722
723 // Sound effects(tm)
724 if(settings_bits != 0 && local_player_nation && (c6->source == local_player_nation || c6->target == local_player_nation)) {
725 switch(base_type) {
728 break;
731 break;
734 break;
737 break;
740 break;
743 break;
746 break;
750 break;
753 break;
756 break;
759 break;
767 break;
774 break;
777 break;
781 //Sound effect is played on above logic (free/non-free loop events above)
782 break;
783 default:
784 break;
785 }
786 }
787
789 c6 = new_messages.front();
790 }
791 // Naval Combat Reports
792 auto* c7 = naval_battle_reports.front();
793 while(c7) {
794 if(ui_state.endof_navalcombat_windows.size() == 0) {
795 ui_state.endof_navalcombat_windows.push_back(ui::make_element_by_type<ui::naval_combat_end_popup>(*this,
796 ui_state.defs_by_name.find(lookup_key("endofnavalcombatpopup"))->second.definition));
797 }
798 //static_cast<ui::naval_combat_window*>(ui_state.navalcombat_windows.back().get())->messages.push_back(*c7);
799 static_cast<ui::naval_combat_end_popup*>(ui_state.endof_navalcombat_windows.back().get())->report = *c7;
800 ui_state.root->add_child_to_front(std::move(ui_state.endof_navalcombat_windows.back()));
804 }
805 if(!static_cast<ui::diplomacy_request_window*>(ui_state.request_window)->messages.empty()) {
806 ui_state.request_window->set_visible(*this, true);
807 ui_state.root->move_child_to_front(ui_state.request_window);
808 }
809 if(!static_cast<ui::message_window*>(ui_state.msg_window)->messages.empty()) {
810 ui_state.msg_window->set_visible(*this, true);
811 ui_state.root->move_child_to_front(ui_state.msg_window);
812 }
813 }
814 root_elm->impl_on_update(*this);
815
817
818 if(ui_state.last_tooltip == tooltip_probe.under_mouse && ui_state.last_tooltip_sub_index == tooltip_sub_index && ui_state.last_tooltip && ui_state.tooltip->is_visible()) {
819 auto type = ui_state.last_tooltip->has_tooltip(*this);
821 auto container = text::create_columnar_layout(*this, ui_state.tooltip->internal_layout,
822 text::layout_parameters{ 0, 0, tooltip_width, int16_t(root_elm->base_data.size.y - 20), ui_state.tooltip_font, 0,
823 text::alignment::left,
824 text::text_color::white, true },
825 10);
826 ui_state.last_tooltip->update_tooltip(*this, tooltip_probe.relative_location.x, tooltip_probe.relative_location.y,
827 container);
828 populate_shortcut_tooltip(*this, *ui_state.last_tooltip, container);
829 if(container.native_rtl == text::layout_base::rtl_status::rtl) {
830 container.used_width = -container.used_width;
831 for(auto& t : container.base_layout.contents) {
832 t.x += 16 + container.used_width;
833 t.y += 16;
834 }
835 } else {
836 for(auto& t : container.base_layout.contents) {
837 t.x += 16;
838 t.y += 16;
839 }
840 }
841 ui_state.tooltip->base_data.size.x = int16_t(container.used_width + 32);
842 ui_state.tooltip->base_data.size.y = int16_t(container.used_height + 32);
843 if(container.used_width > 0)
844 ui_state.tooltip->set_visible(*this, true);
845 else
846 ui_state.tooltip->set_visible(*this, false);
847 }
848 }
849 } // END game state was updated
850
851 if(ui_state.last_tooltip != tooltip_probe.under_mouse || ui_state.last_tooltip_sub_index != tooltip_sub_index) {
852 ui_state.last_tooltip = tooltip_probe.under_mouse;
853 ui_state.last_tooltip_sub_index = tooltip_sub_index;
854
855 if(tooltip_probe.under_mouse) {
856 auto type = ui_state.last_tooltip->has_tooltip(*this);
858 auto container = text::create_columnar_layout(*this, ui_state.tooltip->internal_layout,
859 text::layout_parameters{ 0, 0, tooltip_width,int16_t(root_elm->base_data.size.y - 20), ui_state.tooltip_font, 0,
860 text::alignment::left, text::text_color::white, true }, 10);
861 ui_state.last_tooltip->update_tooltip(*this, tooltip_probe.relative_location.x, tooltip_probe.relative_location.y,
862 container);
863 populate_shortcut_tooltip(*this, *ui_state.last_tooltip, container);
864 if(container.native_rtl == text::layout_base::rtl_status::rtl) {
865 container.used_width = -container.used_width;
866 for(auto& t : container.base_layout.contents) {
867 t.x += 16 + container.used_width;
868 t.y += 16;
869 }
870 } else {
871 for(auto& t : container.base_layout.contents) {
872 t.x += 16;
873 t.y += 16;
874 }
875 }
876 ui_state.tooltip->base_data.size.x = int16_t(container.used_width + 32);
877 ui_state.tooltip->base_data.size.y = int16_t(container.used_height + 32);
878 if(container.used_width > 0)
879 ui_state.tooltip->set_visible(*this, true);
880 else
881 ui_state.tooltip->set_visible(*this, false);
882 } else {
883 ui_state.tooltip->set_visible(*this, false);
884 }
885 } else {
886 ui_state.tooltip->set_visible(*this, false);
887 }
889 auto container = text::create_columnar_layout(*this, ui_state.tooltip->internal_layout,
890 text::layout_parameters{ 0, 0, tooltip_width, int16_t(root_elm->base_data.size.y - 20), ui_state.tooltip_font, 0,
891 text::alignment::left, text::text_color::white, true }, 10);
892 ui_state.last_tooltip->update_tooltip(*this, tooltip_probe.relative_location.x, tooltip_probe.relative_location.y, container);
893 populate_shortcut_tooltip(*this, *ui_state.last_tooltip, container);
894 if(container.native_rtl == text::layout_base::rtl_status::rtl) {
895 container.used_width = -container.used_width;
896 for(auto& t : container.base_layout.contents) {
897 t.x += 16 + container.used_width;
898 t.y += 16;
899 }
900 } else {
901 for(auto& t : container.base_layout.contents) {
902 t.x += 16;
903 t.y += 16;
904 }
905 }
906 ui_state.tooltip->base_data.size.x = int16_t(container.used_width + 32);
907 ui_state.tooltip->base_data.size.y = int16_t(container.used_height + 32);
908 if(container.used_width > 0)
909 ui_state.tooltip->set_visible(*this, true);
910 else
911 ui_state.tooltip->set_visible(*this, false);
912 }
913
914 if(ui_state.last_tooltip && ui_state.tooltip->is_visible()) {
915 // reposition tooltip
916 if(ui_state.tooltip->base_data.size.y <= root_elm->base_data.size.y - (tooltip_bounds.top_left.y + tooltip_bounds.size.y)) {
917 ui_state.tooltip->base_data.position.y = int16_t(tooltip_bounds.top_left.y + tooltip_bounds.size.y);
918 ui_state.tooltip->base_data.position.x = std::clamp(
919 int16_t(tooltip_bounds.top_left.x + (tooltip_bounds.size.x / 2) - (ui_state.tooltip->base_data.size.x / 2)),
920 int16_t(0), int16_t(std::max(root_elm->base_data.size.x - ui_state.tooltip->base_data.size.x, 0)));
921 } else if(ui_state.tooltip->base_data.size.x <= root_elm->base_data.size.x - (tooltip_bounds.top_left.x + tooltip_bounds.size.x)) {
922 ui_state.tooltip->base_data.position.x = int16_t(tooltip_bounds.top_left.x + tooltip_bounds.size.x);
923 ui_state.tooltip->base_data.position.y = std::clamp(
924 int16_t(tooltip_bounds.top_left.y + (tooltip_bounds.size.y / 2) - (ui_state.tooltip->base_data.size.y / 2)),
925 int16_t(0),
926 int16_t(std::max(root_elm->base_data.size.y - ui_state.tooltip->base_data.size.y, 0)));
927 } else if(ui_state.tooltip->base_data.size.x <= tooltip_bounds.top_left.x) {
928 ui_state.tooltip->base_data.position.x = int16_t(tooltip_bounds.top_left.x - ui_state.tooltip->base_data.size.x);
929 ui_state.tooltip->base_data.position.y = std::clamp(
930 int16_t(tooltip_bounds.top_left.y + (tooltip_bounds.size.y / 2) - (ui_state.tooltip->base_data.size.y / 2)),
931 int16_t(0), int16_t(std::max(root_elm->base_data.size.y - ui_state.tooltip->base_data.size.y, 0)));
932 } else if(ui_state.tooltip->base_data.size.y <= tooltip_bounds.top_left.y) {
933 ui_state.tooltip->base_data.position.y = int16_t(tooltip_bounds.top_left.y - ui_state.tooltip->base_data.size.y);
934 ui_state.tooltip->base_data.position.x = std::clamp(
935 int16_t(tooltip_bounds.top_left.x + (tooltip_bounds.size.x / 2) - (ui_state.tooltip->base_data.size.x / 2)),
936 int16_t(0), int16_t(std::max(root_elm->base_data.size.x - ui_state.tooltip->base_data.size.x, 0)));
937 } else {
938 ui_state.tooltip->base_data.position.x = std::clamp(
939 int16_t(tooltip_bounds.top_left.x + (tooltip_bounds.size.x / 2) - (ui_state.tooltip->base_data.size.x / 2)),
940 int16_t(0), int16_t(std::max(root_elm->base_data.size.x - ui_state.tooltip->base_data.size.x, 0)));
941 ui_state.tooltip->base_data.position.y = std::clamp(
942 int16_t(tooltip_bounds.top_left.y + (tooltip_bounds.size.y / 2) - (ui_state.tooltip->base_data.size.y / 2)),
943 int16_t(0), int16_t(std::max(root_elm->base_data.size.y - ui_state.tooltip->base_data.size.y, 0)));
944 }
945 }
946
947 if(current_scene.based_on_map && !mouse_probe.under_mouse && !tooltip_probe.under_mouse) {
948 dcon::province_id prov = map_state.get_province_under_mouse(*this, int32_t(mouse_x_position), int32_t(mouse_y_position), x_size, y_size);
950 prov = dcon::province_id{};
951 if(prov) {
952 if(!drag_selecting && (selected_armies.size() > 0 || selected_navies.size() > 0)) {
953 bool fail = false;
954 for(auto a : selected_armies) {
955 if(command::can_move_army(*this, local_player_nation, a, prov).empty()) {
956 fail = true;
957 }
958 }
959 for(auto a : selected_navies) {
960 if(command::can_move_navy(*this, local_player_nation, a, prov).empty()) {
961 fail = true;
962 }
963 }
964 if(!fail) {
965 auto c = world.province_get_nation_from_province_control(prov);
968 } else {
970 }
971 } else {
973 }
974 }
975 }
976 }
977
978 // Have to have the map tooltip down here, and we must check both of the probes
979 // Not doing this causes the map tooltip to override some of the regular tooltips (namely the score tooltips)
981 && !mouse_probe.under_mouse
982 && !tooltip_probe.under_mouse
983 ) {
984 dcon::province_id prov = map_state.get_province_under_mouse(*this, int32_t(mouse_x_position), int32_t(mouse_y_position), x_size, y_size);
985 if(
986 (
987 (
990 )
992 )
994 ) {
995 prov = dcon::province_id{};
996 }
997 if(prov) {
998 auto container = text::create_columnar_layout(*this, ui_state.tooltip->internal_layout,
999 text::layout_parameters{ 0, 0, tooltip_width, int16_t(ui_state.root->base_data.size.y - 20), ui_state.tooltip_font, 0, text::alignment::left, text::text_color::white, true },
1000 20);
1001 ui::populate_map_tooltip(*this, container, prov);
1002 if(container.native_rtl == text::layout_base::rtl_status::rtl) {
1003 container.used_width = -container.used_width;
1004 for(auto& t : container.base_layout.contents) {
1005 t.x += 16 + container.used_width;
1006 t.y += 16;
1007 }
1008 } else {
1009 for(auto& t : container.base_layout.contents) {
1010 t.x += 16;
1011 t.y += 16;
1012 }
1013 }
1014 ui_state.tooltip->base_data.size.x = int16_t(container.used_width + 32);
1015 ui_state.tooltip->base_data.size.y = int16_t(container.used_height + 32);
1016 if(container.used_width > 0) {
1017 // This block positions the tooltip somewhat under the province centroid
1018 auto mid_point = world.province_get_mid_point(prov);
1019 auto map_pos = map_state.normalize_map_coord(mid_point);
1020 auto screen_size =
1021 glm::vec2{ float(x_size / user_settings.ui_scale), float(y_size / user_settings.ui_scale) };
1022 glm::vec2 screen_pos;
1023 if(!map_state.map_to_screen(*this, map_pos, screen_size, screen_pos)) {
1024 ui_state.tooltip->set_visible(*this, false);
1025 } else {
1026 ui_state.tooltip->base_data.position =
1027 ui::xy_pair{ int16_t(screen_pos.x - container.used_width / 2 - 8), int16_t(screen_pos.y + 3.5f * map_state.get_zoom()) };
1028 ui_state.tooltip->set_visible(*this, true);
1029 }
1030 // Alternatively: just make it visible
1031 // ui_state.tooltip->set_visible(*this, true);
1032 } else {
1033 ui_state.tooltip->set_visible(*this, false);
1034 }
1035 } else {
1036 ui_state.tooltip->set_visible(*this, false);
1037 }
1038 }
1039
1040 if(ui_state.under_mouse != mouse_probe.under_mouse) {
1043 ui_state.under_mouse = mouse_probe.under_mouse;
1046 }
1047
1048 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1049 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1050 glEnable(GL_BLEND);
1051 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1052
1054 // Render default background
1055 glUseProgram(open_gl.ui_shader_program);
1058 glUniform1f(open_gl.ui_shader_screen_width_uniform, float(x_size));
1059 glUniform1f(open_gl.ui_shader_screen_height_uniform, float(y_size));
1061 glViewport(0, 0, x_size, y_size);
1062 glDepthRange(-1.0f, 1.0f);
1063 auto const& gfx_def = ui_defs.gfx[ui_state.bg_gfx_id];
1064 if(gfx_def.primary_texture_handle) {
1065 ogl::render_textured_rect(*this, ui::get_color_modification(false, false, false), 0.f, 0.f, float(x_size), float(y_size),
1066 ogl::get_texture_handle(*this, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
1067 ui::rotation::upright, gfx_def.is_vertically_flipped(),
1068 false);
1069 }
1070 }
1071
1073
1074 //UI rendering
1075 glEnable(GL_BLEND);
1076 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1077 glUseProgram(open_gl.ui_shader_program);
1083 glViewport(0, 0, x_size, y_size);
1084 glDepthRange(-1.0f, 1.0f);
1085
1086 ui_state.relative_mouse_location = mouse_probe.relative_location;
1087
1088 current_scene.render_ui(*this);
1089
1090 root_elm->impl_render(*this, 0, 0);
1091 ui_animation.render(*this);
1092
1093 if(ui_state.tooltip->is_visible()) {
1094 //floating by mouse
1096 int32_t aim_x = int32_t(mouse_x_position / user_settings.ui_scale) + ui_state.cursor_size;
1097 int32_t aim_y = int32_t(mouse_y_position / user_settings.ui_scale) + ui_state.cursor_size;
1098 int32_t wsize_x = int32_t(x_size / user_settings.ui_scale);
1099 int32_t wsize_y = int32_t(y_size / user_settings.ui_scale);
1100 //this only works if the tooltip isnt bigger than the entire window, wont crash though
1101 if(aim_x + ui_state.tooltip->base_data.size.x > wsize_x) {
1102 aim_x = wsize_x - ui_state.tooltip->base_data.size.x;
1103 }
1104 if(aim_y + ui_state.tooltip->base_data.size.y > wsize_y) {
1105 aim_y = wsize_y - ui_state.tooltip->base_data.size.y;
1106 }
1107 ui_state.tooltip->impl_render(*this, aim_x, aim_y);
1108 } else {//tooltip centered over ui element
1109 ui_state.tooltip->impl_render(*this, ui_state.tooltip->base_data.position.x, ui_state.tooltip->base_data.position.y);
1110 }
1111 }
1112}
1113
1115 ui_state.tooltip_font = text::name_into_font_id(*this, "ToolTip_Font");
1118
1119 // Load late ui defs
1120 auto root = get_root(common_fs);
1121 auto assets = simple_fs::open_directory(root, NATIVE("assets"));
1122 for(auto gui_file : list_files(assets, NATIVE(".aui"))) {
1123 auto file_name = simple_fs::get_file_name(gui_file);
1124 auto opened_file = open_file(gui_file);
1125 if(opened_file) {
1126 file_name.pop_back(); file_name.pop_back(); file_name.pop_back(); file_name.pop_back();
1127 auto afile_name = simple_fs::native_to_utf8(file_name);
1128 auto content = view_contents(*opened_file);
1129 bytes_to_windows(content.data, content.file_size, afile_name, ui_state.new_ui_windows);
1130 ui_state.held_open_ui_files.emplace_back(std::move(*opened_file));
1131 }
1132 }
1133
1134 // Clear "center" property so they don't look messed up!
1135 {
1136 static const std::string_view elem_names[] = {
1137 "state_info",
1138 "production_goods_name",
1139 "factory_info",
1140 "new_factory_option",
1141 "ledger_legend_entry",
1142 "project_info",
1143 };
1144 for(const auto& elem_name : elem_names) {
1145 auto it = ui_state.defs_by_name.find(lookup_key(elem_name));
1146 if(it != ui_state.defs_by_name.end()) {
1147 auto& gfx_def = ui_defs.gui[it->second.definition];
1148 gfx_def.flags &= ~ui::element_data::orientation_mask;
1149 }
1150 }
1151 }
1152 // Allow user to drag some windows, and only the ones that make sense
1153 {
1154 static const std::string_view elem_names[] = {
1155 "pop_details_win",
1156 "trade_flow",
1157 "event_election_window",
1158 "invest_project_window",
1159 "ledger",
1160 "province_view",
1161 "releaseconfirm",
1162 "build_factory",
1163 "defaultdiplomacydialog",
1164 "gpselectdiplomacydialog",
1165 "makecbdialog",
1166 "declarewardialog",
1167 "setuppeacedialog",
1168 "setupcrisisbackdowndialog",
1169 "endofnavalcombatpopup",
1170 "endoflandcombatpopup",
1171 "ingame_lobby_window",
1172 };
1173 for(const auto& elem_name : elem_names) {
1174 auto it = ui_state.defs_by_name.find(lookup_key(elem_name));
1175 if(it != ui_state.defs_by_name.end()) {
1176 auto& gfx_def = ui_defs.gui[it->second.definition];
1177 if(gfx_def.get_element_type() == ui::element_type::window) {
1178 gfx_def.data.window.flags |= ui::window_data::is_moveable_mask;
1179 }
1180 }
1181 }
1182 }
1183 // Nudge, overriden by V2 to be 0 always
1184 ui_defs.gui[ui_state.defs_by_name.find(lookup_key("decision_entry"))->second.definition].position = ui::xy_pair{ 0, 0 };
1185 // Find the object id for the main_bg displayed (so we display it before the map)
1186 ui_state.bg_gfx_id = ui_defs.gui[ui_state.defs_by_name.find(lookup_key("bg_main_menus"))->second.definition].data.image.gfx_object;
1187
1188 ui_state.nation_picker = ui::make_element_by_type<ui::nation_picker_container>(*this, ui_state.defs_by_name.find(lookup_key("lobby"))->second.definition);
1189 {
1190 auto window = ui::make_element_by_type<ui::console_window>(*this, "console_wnd");
1192 window->set_visible(*this, false);
1193 ui_state.nation_picker->add_child_to_front(std::move(window));
1194 }
1195 { // One on the lobby
1196 auto new_elm = ui::make_element_by_type<ui::chat_window>(*this, "ingame_lobby_window");
1197 new_elm->set_visible(*this, !(network_mode == sys::network_mode_type::single_player)); // Hidden in singleplayer by default
1198 ui_state.r_chat_window = new_elm.get();
1199 ui_state.nation_picker->add_child_to_front(std::move(new_elm));
1200 }
1202}
1203//
1204// string pool functions
1205//
1206
1207std::string_view state::to_string_view(dcon::text_key tag) const {
1208 if(!tag)
1209 return std::string_view();
1210 assert(size_t(tag.index()) < key_data.size());
1211 auto start_position = key_data.data() + tag.index();
1212 auto data_size = key_data.size();
1213 auto end_position = start_position;
1214 for(; end_position < key_data.data() + data_size; ++end_position) {
1215 if(*end_position == 0)
1216 break;
1217 }
1218 return std::string_view(key_data.data() + tag.index(), size_t(end_position - start_position));
1219}
1220
1221std::string_view state::locale_string_view(uint32_t tag) const {
1222 assert(size_t(tag) < locale_text_data.size());
1223 auto start_position = locale_text_data.data() + tag;
1224 auto data_size = locale_text_data.size();
1225 auto end_position = start_position;
1226 for(; end_position < locale_text_data.data() + data_size; ++end_position) {
1227 if(*end_position == 0)
1228 break;
1229 }
1230 return std::string_view(locale_text_data.data() + tag, size_t(end_position - start_position));
1231}
1232
1234 locale_text_data.clear();
1236 locale_text_data.push_back(0);
1237}
1238
1239void state::load_locale_strings(std::string_view locale_name) {
1240 auto root_dir = get_root(common_fs);
1241 auto assets_dir = open_directory(root_dir, NATIVE("assets/localisation"));
1242
1243 auto load_base_files = [&](int32_t column) {
1244 auto text_dir = open_directory(root_dir, NATIVE("localisation"));
1245 for(auto& file : list_files(text_dir, NATIVE(".csv"))) {
1246 if(auto ofile = open_file(file); ofile) {
1247 auto content = view_contents(*ofile);
1248 text::consume_csv_file(*this, content.data, content.file_size, column, false);
1249 }
1250 }
1251 for(auto& file : list_files(assets_dir, NATIVE(".csv"))) {
1252 if(auto ofile = open_file(file); ofile) {
1253 auto content = view_contents(*ofile);
1254 text::consume_csv_file(*this, content.data, content.file_size, column, false);
1255 }
1256 }
1257 };
1258
1259 if(locale_name.starts_with("en")) {
1260 load_base_files(1);
1261 } else if(locale_name.starts_with("fr")) {
1262 load_base_files(2);
1263 } else if(locale_name.starts_with("de")) {
1264 load_base_files(3);
1265 } else if(locale_name.starts_with("pl")) {
1266 load_base_files(4);
1267 } else if(locale_name.starts_with("es")) {
1268 load_base_files(5);
1269 } else if(locale_name.starts_with("it")) {
1270 load_base_files(6);
1271 } else if(locale_name.starts_with("sv")) {
1272 load_base_files(7);
1273 } else if(locale_name.starts_with("cs")) {
1274 load_base_files(8);
1275 } else if(locale_name.starts_with("hu")) {
1276 load_base_files(9);
1277 } else if(locale_name.starts_with("nl")) {
1278 load_base_files(10);
1279 } else if(locale_name.starts_with("pt")) {
1280 load_base_files(11);
1281 } else if(locale_name.starts_with("ru")) {
1282 load_base_files(12);
1283 } else if(locale_name.starts_with("fi")) {
1284 load_base_files(13);
1285 }
1286
1287 auto locale_dir = open_directory(assets_dir, simple_fs::utf8_to_native(locale_name));
1288 for(auto& file : list_files(locale_dir, NATIVE(".csv"))) {
1289 if(auto ofile = open_file(file); ofile) {
1290 auto content = view_contents(*ofile);
1291 text::consume_csv_file(*this, content.data, content.file_size, 1, true);
1292 }
1293 }
1294}
1295
1296bool state::key_is_localized(dcon::text_key tag) const {
1297 if(!tag)
1298 return false;
1299 assert(size_t(tag.index()) < key_data.size());
1301}
1302bool state::key_is_localized(std::string_view key) const {
1304}
1305dcon::text_key state::lookup_key(std::string_view text) const {
1306 if(auto it = untrans_key_to_text_sequence.find(text); it != untrans_key_to_text_sequence.end()) {
1307 return *it;
1308 }
1309 return dcon::text_key{};
1310}
1311
1312dcon::text_key state::add_key_win1252(std::string const& text) {
1313 return add_key_win1252(std::string_view(text));
1314}
1315dcon::text_key state::add_key_win1252(std::string_view text) {
1316 std::string temp;
1317 for(auto c : text) {
1318 auto unicode = text::win1250toUTF16(c);
1319 if(unicode == 0x00A7)
1320 unicode = uint16_t('?'); // convert section symbol to ?
1321 if(unicode <= 0x007F) {
1322 temp.push_back(char(unicode));
1323 } else if(unicode <= 0x7FF) {
1324 temp.push_back(char(0xC0 | uint8_t(0x1F & (unicode >> 6))));
1325 temp.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1326 } else { // if unicode <= 0xFFFF
1327 temp.push_back(char(0xE0 | uint8_t(0x0F & (unicode >> 12))));
1328 temp.push_back(char(0x80 | uint8_t(0x3F & (unicode >> 6))));
1329 temp.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1330 }
1331 }
1332 assert(temp[temp.size()] == '\0');
1333 return add_key_utf8(temp);
1334}
1335dcon::text_key state::add_key_utf8(std::string const& new_text) {
1336 return add_key_utf8(std::string_view(new_text.data()));
1337}
1338dcon::text_key state::add_key_utf8(std::string_view new_text) {
1339 auto ekey = lookup_key(new_text);
1340 if(ekey)
1341 return ekey;
1342
1343 auto start = key_data.size();
1344 auto length = new_text.length();
1345 if(length == 0)
1346 return dcon::text_key();
1347 key_data.resize(start + length + 1, char(0));
1348 std::copy_n(new_text.data(), length, key_data.data() + start);
1349 key_data.back() = 0;
1350
1351 auto ret = dcon::text_key(dcon::text_key::value_base_t(start));
1352 untrans_key_to_text_sequence.insert(ret);
1353 return ret;
1354}
1356 return add_locale_data_win1252(std::string_view(text));
1357}
1359 auto start = locale_text_data.size();
1360 for(auto c : text) {
1361 auto unicode = text::win1250toUTF16(c);
1362 if(unicode == 0x00A7)
1363 unicode = uint16_t('?'); // convert section symbol to ?
1364 if(unicode <= 0x007F) {
1365 locale_text_data.push_back(char(unicode));
1366 } else if(unicode <= 0x7FF) {
1367 locale_text_data.push_back(char(0xC0 | uint8_t(0x1F & (unicode >> 6))));
1368 locale_text_data.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1369 } else { // if unicode <= 0xFFFF
1370 locale_text_data.push_back(char(0xE0 | uint8_t(0x0F & (unicode >> 12))));
1371 locale_text_data.push_back(char(0x80 | uint8_t(0x3F & (unicode >> 6))));
1372 locale_text_data.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1373 }
1374 }
1375 locale_text_data.push_back(0);
1376 return uint32_t(start);
1377}
1378uint32_t state::add_locale_data_utf8(std::string const& new_text) {
1379 return add_locale_data_utf8(std::string_view(new_text));
1380}
1381uint32_t state::add_locale_data_utf8(std::string_view new_text) {
1382 auto start = locale_text_data.size();
1383 auto length = new_text.length();
1384 if(length == 0)
1385 return 0;
1386 locale_text_data.resize(start + length + 1, char(0));
1387 std::copy_n(new_text.data(), length, locale_text_data.data() + start);
1388 locale_text_data.back() = 0;
1389 return uint32_t(start);
1390}
1391
1392dcon::unit_name_id state::add_unit_name(std::string_view text) {
1393 if(text.empty())
1394 return dcon::unit_name_id();
1395
1396 std::string temp;
1397 for(auto c : text) {
1398 auto unicode = text::win1250toUTF16(c);
1399 if(unicode == 0x00A7)
1400 unicode = uint16_t('?'); // convert section symbol to ?
1401 if(unicode <= 0x007F) {
1402 temp.push_back(char(unicode));
1403 } else if(unicode <= 0x7FF) {
1404 temp.push_back(char(0xC0 | uint8_t(0x1F & (unicode >> 6))));
1405 temp.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1406 } else { // if unicode <= 0xFFFF
1407 temp.push_back(char(0xE0 | uint8_t(0x0F & (unicode >> 12))));
1408 temp.push_back(char(0x80 | uint8_t(0x3F & (unicode >> 6))));
1409 temp.push_back(char(0x80 | uint8_t(0x3F & unicode)));
1410 }
1411 }
1412 assert(temp.size() > 0);
1413 assert(temp[temp.size()] == '\0');
1414 auto start = unit_names.size();
1415 unit_names.resize(start + temp.length() + 1, char(0));
1416 std::copy_n(temp.data(), temp.length(), unit_names.data() + start);
1417 unit_names.back() = 0;
1418 unit_names_indices.push_back(int32_t(start));
1419 return dcon::unit_name_id(dcon::unit_name_id::value_base_t(unit_names_indices.size() - 1));
1420}
1421std::string_view state::to_string_view(dcon::unit_name_id tag) const {
1422 if(!tag)
1423 return std::string_view();
1424 assert(size_t(tag.index()) < unit_names_indices.size());
1425 auto start_position = unit_names.data() + unit_names_indices[tag.index()];
1426 auto data_size = unit_names.size();
1427 auto end_position = start_position;
1428 for(; end_position < unit_names.data() + data_size; ++end_position) {
1429 if(*end_position == 0)
1430 break;
1431 }
1432 return std::string_view(unit_names.data() + unit_names_indices[tag.index()], size_t(end_position - start_position));
1433}
1434
1435dcon::trigger_key state::commit_trigger_data(std::vector<uint16_t> data) {
1436 if(trigger_data_indices.empty()) { // Create placeholder for invalid triggers
1437 trigger_data_indices.push_back(0);
1438 trigger_data.push_back(uint16_t(trigger::always | trigger::no_payload | trigger::association_ne));
1439 }
1440
1441 if(data.empty()) {
1442 return dcon::trigger_key();
1443 }
1444
1445 auto search_result = std::search(trigger_data.data() + 1, trigger_data.data() + trigger_data.size(),
1446 std::boyer_moore_horspool_searcher(data.data(), data.data() + data.size()));
1447 if(search_result != trigger_data.data() + trigger_data.size()) {
1448 auto const start = search_result - trigger_data.data();
1449 auto it = std::find(trigger_data_indices.begin(), trigger_data_indices.end(), int32_t(start));
1450 if(it != trigger_data_indices.end()) {
1451 auto d = std::distance(trigger_data_indices.begin(), it);
1452 return dcon::trigger_key(dcon::trigger_key::value_base_t(d - 1));
1453 } else {
1454 trigger_data_indices.push_back(int32_t(start));
1455 assert(trigger_data_indices.size() <= std::numeric_limits<uint16_t>::max());
1456 return dcon::trigger_key(dcon::trigger_key::value_base_t(trigger_data_indices.size() - 1 - 1));
1457 }
1458 } else {
1459 auto start = trigger_data.size();
1460 auto size = data.size();
1461 trigger_data.resize(start + size, uint16_t(0));
1462 std::copy_n(data.data(), size, trigger_data.data() + start);
1463 trigger_data_indices.push_back(int32_t(start));
1464 assert(trigger_data_indices.size() <= std::numeric_limits<uint16_t>::max());
1465 return dcon::trigger_key(dcon::trigger_key::value_base_t(trigger_data_indices.size() - 1 - 1));
1466 }
1467}
1468
1469dcon::effect_key state::commit_effect_data(std::vector<uint16_t> data) {
1470 if(effect_data_indices.empty()) { // Create placeholder for invalid effects
1471 effect_data_indices.push_back(0);
1472 effect_data.push_back(uint16_t(effect::no_payload));
1473 }
1474
1475 if(data.empty()) {
1476 return dcon::effect_key();
1477 }
1478
1479 auto search_result = std::search(effect_data.data() + 1, effect_data.data() + effect_data.size(),
1480 std::boyer_moore_horspool_searcher(data.data(), data.data() + data.size()));
1481 if(search_result != effect_data.data() + effect_data.size()) {
1482 auto const start = search_result - effect_data.data();
1483 auto it = std::find(effect_data_indices.begin(), effect_data_indices.end(), int32_t(start));
1484 if(it != effect_data_indices.end()) {
1485 auto d = std::distance(effect_data_indices.begin(), it);
1486 return dcon::effect_key(dcon::effect_key::value_base_t(d - 1));
1487 } else {
1488 effect_data_indices.push_back(int32_t(start));
1489 assert(effect_data_indices.size() <= std::numeric_limits<uint16_t>::max());
1490 return dcon::effect_key(dcon::effect_key::value_base_t(effect_data_indices.size() - 1 - 1));
1491 }
1492 } else {
1493 auto start = effect_data.size();
1494 auto size = data.size();
1495 effect_data.resize(start + size, uint16_t(0));
1496 std::copy_n(data.data(), size, effect_data.data() + start);
1497 effect_data_indices.push_back(int32_t(start));
1498 assert(effect_data_indices.size() <= std::numeric_limits<uint16_t>::max());
1499 return dcon::effect_key(dcon::effect_key::value_base_t(effect_data_indices.size() - 1 - 1));
1500 }
1501}
1502
1504 auto settings_location = simple_fs::get_or_create_settings_directory();
1505
1506 char buffer[sizeof(user_settings_s)];
1507 char* ptr = &buffer[0];
1508
1509#define US_SAVE(x) \
1510 std::memcpy(ptr, &user_settings.x, sizeof(user_settings.x)); \
1511 ptr += sizeof(user_settings.x);
1512 US_SAVE(ui_scale);
1513 US_SAVE(master_volume);
1514 US_SAVE(music_volume);
1515 US_SAVE(effects_volume);
1516 US_SAVE(interface_volume);
1517 US_SAVE(prefer_fullscreen);
1518 US_SAVE(map_is_globe);
1519 US_SAVE(autosaves);
1520 US_SAVE(bind_tooltip_mouse);
1521 US_SAVE(use_classic_fonts);
1522 US_SAVE(outliner_views);
1523 constexpr size_t lower_half_count = 98;
1524 std::memcpy(ptr, user_settings.self_message_settings, lower_half_count);
1525 ptr += 98;
1526 std::memcpy(ptr, user_settings.interesting_message_settings, lower_half_count);
1527 ptr += 98;
1528 std::memcpy(ptr, user_settings.other_message_settings, lower_half_count);
1529 ptr += 98;
1530 US_SAVE(fow_enabled);
1531 constexpr size_t upper_half_count = 128 - 98;
1532 std::memcpy(ptr, &user_settings.self_message_settings[98], upper_half_count);
1533 ptr += upper_half_count;
1534 std::memcpy(ptr, &user_settings.interesting_message_settings[98], upper_half_count);
1535 ptr += upper_half_count;
1536 std::memcpy(ptr, &user_settings.other_message_settings[98], upper_half_count);
1537 ptr += upper_half_count;
1538 US_SAVE(map_label);
1539 US_SAVE(antialias_level);
1540 US_SAVE(gaussianblur_level);
1541 US_SAVE(gamma);
1542 US_SAVE(railroads_enabled);
1543 US_SAVE(rivers_enabled);
1544 US_SAVE(zoom_mode);
1545 US_SAVE(vassal_color);
1546 US_SAVE(left_mouse_click_hold_and_release);
1547 US_SAVE(render_models);
1548 US_SAVE(mouse_edge_scrolling);
1549 US_SAVE(black_map_font);
1550 US_SAVE(spoilers);
1551 US_SAVE(zoom_speed);
1552 US_SAVE(mute_on_focus_lost);
1553 US_SAVE(diplomatic_message_popup);
1554 US_SAVE(wasd_for_map_movement);
1555 US_SAVE(notify_rebels_defeat);
1557 US_SAVE(UNUSED_UINT32_T);
1558 US_SAVE(locale);
1559#undef US_SAVE
1560
1561 simple_fs::write_file(settings_location, NATIVE("user_settings.dat"), &buffer[0], uint32_t(ptr - buffer));
1562}
1564 auto settings_location = simple_fs::get_or_create_settings_directory();
1565 auto settings_file = open_file(settings_location, NATIVE("user_settings.dat"));
1566 if(settings_file) {
1567 auto content = view_contents(*settings_file);
1568 auto ptr = content.data;
1569
1570#define US_LOAD(x) \
1571 if(ptr > content.data + content.file_size - sizeof(user_settings.x)) break; \
1572 std::memcpy(&user_settings.x, ptr, sizeof(user_settings.x)); \
1573 ptr += sizeof(user_settings.x);
1574
1575 do {
1576 US_LOAD(ui_scale);
1577 US_LOAD(master_volume);
1578 US_LOAD(music_volume);
1579 US_LOAD(effects_volume);
1580 US_LOAD(interface_volume);
1581 US_LOAD(prefer_fullscreen);
1582 US_LOAD(map_is_globe);
1583 US_LOAD(autosaves);
1584 US_LOAD(bind_tooltip_mouse);
1585 US_LOAD(use_classic_fonts);
1586 US_LOAD(outliner_views);
1587 constexpr size_t lower_half_count = 98;
1588
1589 std::memcpy(&user_settings.self_message_settings, ptr, std::min(lower_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1590 ptr += 98;
1591
1592 std::memcpy(&user_settings.interesting_message_settings, ptr, std::min(lower_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1593 ptr += 98;
1594
1595 std::memcpy(&user_settings.other_message_settings, ptr, std::min(lower_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1596 ptr += 98;
1597
1598 US_LOAD(fow_enabled);
1599 constexpr size_t upper_half_count = 128 - 98;
1600 std::memcpy(&user_settings.self_message_settings[98], ptr, std::min(upper_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1601 ptr += upper_half_count;
1602 std::memcpy(&user_settings.interesting_message_settings[98], ptr, std::min(upper_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1603 ptr += upper_half_count;
1604 std::memcpy(&user_settings.other_message_settings[98], ptr, std::min(upper_half_count, size_t(std::max(ptrdiff_t(0), (content.data + content.file_size) - ptr))));
1605 ptr += upper_half_count;
1606 US_LOAD(map_label);
1607 US_LOAD(antialias_level);
1608 US_LOAD(gaussianblur_level);
1609 US_LOAD(gamma);
1610 US_LOAD(railroads_enabled);
1611 US_LOAD(rivers_enabled);
1612 US_LOAD(zoom_mode);
1613 US_LOAD(vassal_color);
1614 US_LOAD(left_mouse_click_hold_and_release);
1615 US_LOAD(render_models);
1616 US_LOAD(mouse_edge_scrolling);
1617 US_LOAD(black_map_font);
1618 US_LOAD(spoilers);
1619 US_LOAD(zoom_speed);
1620 US_LOAD(mute_on_focus_lost);
1621 US_LOAD(diplomatic_message_popup);
1622 US_LOAD(wasd_for_map_movement);
1623 US_LOAD(notify_rebels_defeat);
1625 US_LOAD(UNUSED_UINT32_T);
1626 US_LOAD(locale);
1627#undef US_LOAD
1628 } while(false);
1629
1630 //NaN will not get clamped, so use special std::isfinite test to set to reasonable values
1633
1634 if(!std::isfinite(user_settings.music_volume)) user_settings.music_volume = 0.0f;
1635 user_settings.music_volume = std::clamp(user_settings.music_volume, 0.0f, 1.0f);
1636
1637 if(!std::isfinite(user_settings.effects_volume)) user_settings.effects_volume = 0.0f;
1638 user_settings.effects_volume = std::clamp(user_settings.effects_volume, 0.0f, 1.0f);
1639
1640 if(!std::isfinite(user_settings.master_volume)) user_settings.master_volume = 0.0f;
1641 user_settings.master_volume = std::clamp(user_settings.master_volume, 0.0f, 1.0f);
1642
1644
1647
1648 if(!std::isfinite(user_settings.gamma)) user_settings.gamma = 0.5f;
1649 user_settings.gamma = std::clamp(user_settings.gamma, 0.5f, 2.5f);
1650
1651 if(!std::isfinite(user_settings.zoom_speed)) user_settings.zoom_speed = 15.0f;
1652 user_settings.zoom_speed = std::clamp(user_settings.zoom_speed, 15.f, 25.f);
1653 }
1654
1655 // find most recent autosave
1656
1658 uint64_t max_timestamp = 0;
1659 for(int32_t i = 0; i < sys::max_autosaves; ++i) {
1660 auto asfile = simple_fs::open_file(saves, native_string(NATIVE("autosave_")) + simple_fs::utf8_to_native(std::to_string(i)) + native_string(NATIVE(".bin")));
1661 if(asfile) {
1662 auto content = simple_fs::view_contents(*asfile);
1664 if(content.file_size > sizeof_save_header(header)) {
1665 read_save_header((uint8_t const*)(content.data), header);
1666 if(header.timestamp > max_timestamp) {
1667 max_timestamp = header.timestamp;
1669 }
1670 }
1671 }
1672 }
1673
1674 user_settings.locale[15] = 0;
1675 std::string lname(user_settings.locale);
1676 bool locale_loaded = false;
1677
1678 auto rt = get_root(common_fs);
1679 auto assets = simple_fs::open_directory(rt, NATIVE("assets"));
1680 auto loc = simple_fs::open_directory(assets, NATIVE("localisation"));
1681 for(auto& ld : simple_fs::list_subdirectories(loc)) {
1682 auto def_file = simple_fs::open_file(ld, NATIVE("locale.txt"));
1683 if(def_file) {
1684 auto contents = simple_fs::view_contents(*def_file);
1685 auto ld_name = simple_fs::get_full_name(ld);
1686 auto dir_lname = ld_name.substr(ld_name.find_last_of(NATIVE_DIR_SEPARATOR) + 1);
1687 parsers::add_locale(*this, simple_fs::native_to_utf8(dir_lname), contents.data, contents.data + contents.file_size);
1688 }
1689 }
1690
1691 for(auto l : world.in_locale) {
1692 auto ln = l.get_locale_name();
1693 auto ln_sv = std::string_view{ (char const*)ln.begin(), ln.size() };
1694 if(ln_sv == lname) {
1696 locale_loaded = true;
1697 break;
1698 }
1699 }
1700
1701 if(!locale_loaded) {
1702 for(auto l : world.in_locale) {
1703 auto ln = l.get_locale_name();
1704 auto ln_sv = std::string_view{ (char const*)ln.begin(), ln.size() };
1705 if(ln_sv == "en-US") {
1707 locale_loaded = true;
1708 break;
1709 }
1710 }
1711 }
1712
1713 if(!locale_loaded) {
1714 font_collection.change_locale(*this, dcon::locale_id{ 0 });
1715 }
1716}
1717
1718void state::update_ui_scale(float new_scale) {
1719 user_settings.ui_scale = new_scale;
1720 ui_state.root->base_data.size.x = int16_t(x_size / user_settings.ui_scale);
1721 ui_state.root->base_data.size.y = int16_t(y_size / user_settings.ui_scale);
1724 // TODO move windows
1725}
1726
1728 auto root = get_root(state.common_fs);
1729 auto poptypes = open_directory(root, NATIVE("poptypes"));
1730
1731 for(auto& file : simple_fs::list_files(poptypes, NATIVE(".txt"))) {
1732 auto full_name = get_full_name(file);
1733 auto last = full_name.c_str() + full_name.length();
1734 auto first = full_name.c_str();
1735 for(; last > first; --last) {
1736 if(*last == NATIVE('.'))
1737 break;
1738 }
1739 auto start_of_name = last;
1740 for(; start_of_name >= first; --start_of_name) {
1741 if(*start_of_name == NATIVE('\\') || *start_of_name == NATIVE('/')) {
1742 ++start_of_name;
1743 break;
1744 }
1745 }
1746 auto utf8typename = simple_fs::native_to_utf8(native_string_view(start_of_name, last - start_of_name));
1747
1748 auto name_id = text::find_or_add_key(context.state, utf8typename, true);
1749 auto type_id = state.world.create_pop_type();
1750 state.world.pop_type_set_name(type_id, name_id);
1751 context.map_of_poptypes.insert_or_assign(std::string(utf8typename), type_id);
1752 }
1753}
1754
1755void state::open_diplomacy(dcon::nation_id target) {
1756 if(ui_state.diplomacy_subwindow != nullptr) {
1757 if(ui_state.topbar_subwindow != nullptr) {
1758 ui_state.topbar_subwindow->set_visible(*this, false);
1759 }
1762 ui_state.root->move_child_to_front(ui_state.diplomacy_subwindow);
1764 }
1765}
1766
1768 auto root = get_root(common_fs);
1769 auto common = open_directory(root, NATIVE("common"));
1770
1772
1773 //text::name_into_font_id(*this, "garamond_14");
1774 ui::load_text_gui_definitions(*this, context.gfx_context, err);
1775
1776 auto map = open_directory(root, NATIVE("map"));
1777 // parse default.map
1778 {
1779 auto def_map_file = open_file(map, NATIVE("default.map"));
1780 if(def_map_file) {
1781 auto content = view_contents(*def_map_file);
1782 err.file_name = "default.map";
1783 parsers::token_generator gen(content.data, content.data + content.file_size);
1784 parsers::parse_default_map_file(gen, err, context);
1785 } else {
1786 err.fatal = true;
1787 err.accumulated_errors += "File map/default.map could not be opened\n";
1788 }
1789 }
1790 // parse definition.csv
1791 {
1792 auto def_csv_file = open_file(map, NATIVE("definition.csv"));
1793 if(def_csv_file) {
1794 auto content = view_contents(*def_csv_file);
1795 err.file_name = "definition.csv";
1796 parsers::read_map_colors(content.data, content.data + content.file_size, err, context);
1797 } else {
1798 err.fatal = true;
1799 err.accumulated_errors += "File map/definition.csv could not be opened\n";
1800 }
1801 }
1802
1803 {
1804 err.file_name = "adjacencies.csv";
1805 auto adj_csv_file = open_file(map, NATIVE("adjacencies.csv"));
1806 if(adj_csv_file) {
1807 auto adj_content = view_contents(*adj_csv_file);
1808 parsers::read_map_adjacency(adj_content.data, adj_content.data + adj_content.file_size, err, context);
1809 }
1810 }
1811
1812 /*
1813 240,208,1 Tsushima --> 240,208,0 Nagasaki
1814 128,65,97 Fehmarn--> 128,65,96 Kiel
1815 */
1816
1817 if(auto it = context.map_color_to_province_id.find(sys::pack_color(240, 208, 0));
1818 it != context.map_color_to_province_id.end() &&
1819 context.map_color_to_province_id.find(sys::pack_color(240, 208, 1)) == context.map_color_to_province_id.end()) {
1820 context.map_color_to_province_id.insert_or_assign(sys::pack_color(240, 208, 1), it->second);
1821 }
1822 if(auto it = context.map_color_to_province_id.find(sys::pack_color(128, 65, 96));
1823 it != context.map_color_to_province_id.end() &&
1824 context.map_color_to_province_id.find(sys::pack_color(128, 65, 97)) == context.map_color_to_province_id.end()) {
1825 context.map_color_to_province_id.insert_or_assign(sys::pack_color(128, 65, 97), it->second);
1826 }
1827
1828 /*
1829 // DO NOT RESTORE
1830 // 1, 222, 200 --> 51, 221, 251 -- randomly misplaced sea
1831 // Apparently this color is not just found in the corner of the map, but scattered around it as well
1832 // thus substituting a valid province color for it causes the sea tile to connect to all sorts of place
1833 // (a very undesirable behavior
1834 if(auto it = context.map_color_to_province_id.find(sys::pack_color(51, 221, 251));
1835 it != context.map_color_to_province_id.end() &&
1836 context.map_color_to_province_id.find(sys::pack_color(1, 222, 200)) == context.map_color_to_province_id.end()) {
1837 context.map_color_to_province_id.insert_or_assign(sys::pack_color(1, 222, 200), it->second);
1838 }
1839 */
1840
1841 // 94, 53, 41 --> 89, 202, 202 -- random dots in the sea tiles
1842 // 247, 248, 245 -- > 89, 202, 202
1843
1844
1845 if(auto it = context.map_color_to_province_id.find(sys::pack_color(89, 202, 202));
1846 it != context.map_color_to_province_id.end() &&
1847 context.map_color_to_province_id.find(sys::pack_color(94, 53, 41)) == context.map_color_to_province_id.end()) {
1848 context.map_color_to_province_id.insert_or_assign(sys::pack_color(94, 53, 41), it->second);
1849 }
1850 if(auto it = context.map_color_to_province_id.find(sys::pack_color(89, 202, 202));
1851 it != context.map_color_to_province_id.end() &&
1852 context.map_color_to_province_id.find(sys::pack_color(247, 248, 245)) == context.map_color_to_province_id.end()) {
1853 context.map_color_to_province_id.insert_or_assign(sys::pack_color(247, 248, 245), it->second);
1854 }
1855
1856
1857 std::thread map_loader([&]() { map_state.load_map_data(context); });
1858
1860
1861 // Read national tags from countries.txt
1862 {
1863 auto countries = open_file(common, NATIVE("countries.txt"));
1864 if(countries) {
1865 auto content = view_contents(*countries);
1866 err.file_name = "countries.txt";
1867 parsers::token_generator gen(content.data, content.data + content.file_size);
1868 parsers::parse_national_identity_file(gen, err, context);
1869 } else {
1870 err.fatal = true;
1871 err.accumulated_errors += "File common/countries.txt could not be opened\n";
1872 }
1873 }
1874 // read religions from religion.txt
1875 {
1876 auto religion = open_file(common, NATIVE("religion.txt"));
1877 if(religion) {
1878 auto content = view_contents(*religion);
1879 err.file_name = "religion.txt";
1880 parsers::token_generator gen(content.data, content.data + content.file_size);
1881 parsers::parse_religion_file(gen, err, context);
1882 } else {
1883 err.fatal = true;
1884 err.accumulated_errors += "File common/religion.txt could not be opened\n";
1885 }
1886 }
1887 // read cultures from cultures.txt
1888 {
1889 auto cultures = open_file(common, NATIVE("cultures.txt"));
1890 if(cultures) {
1891 auto content = view_contents(*cultures);
1892 err.file_name = "cultures.txt";
1893 parsers::token_generator gen(content.data, content.data + content.file_size);
1894 parsers::parse_culture_file(gen, err, context);
1895 } else {
1896 err.fatal = true;
1897 err.accumulated_errors += "File common/cultures.txt could not be opened\n";
1898 }
1899 }
1900 // read commodities from goods.txt
1901 {
1902 // FIRST: make sure that we have a money good
1903 if(world.commodity_size() == 0) {
1904 // create money
1905 auto money_id = world.create_commodity();
1906 assert(money_id.index() == 0);
1907 }
1908 auto goods = open_file(common, NATIVE("goods.txt"));
1909 if(goods) {
1910 auto content = view_contents(*goods);
1911 err.file_name = "goods.txt";
1912 parsers::token_generator gen(content.data, content.data + content.file_size);
1913 parsers::parse_goods_file(gen, err, context);
1914 } else {
1915 err.fatal = true;
1916 err.accumulated_errors += "File common/goods.txt could not be opened\n";
1917 }
1918 }
1919 // read buildings.text
1920 // world.factory_type_resize_construction_costs(world.commodity_size());
1921 {
1922 auto buildings = open_file(common, NATIVE("buildings.txt"));
1923 if(buildings) {
1924 auto content = view_contents(*buildings);
1925 err.file_name = "buildings.txt";
1926 parsers::token_generator gen(content.data, content.data + content.file_size);
1927 parsers::parse_building_file(gen, err, context);
1928 } else {
1929 err.fatal = true;
1930 err.accumulated_errors += "File common/buildings.txt could not be opened\n";
1931 }
1932 }
1933 // pre parse ideologies.txt
1934 {
1935 context.ideologies_file = open_file(common, NATIVE("ideologies.txt"));
1936 if(context.ideologies_file) {
1937 auto content = view_contents(*context.ideologies_file);
1938 err.file_name = "ideologies.txt";
1939 parsers::token_generator gen(content.data, content.data + content.file_size);
1940 parsers::parse_ideology_file(gen, err, context);
1941 } else {
1942 err.fatal = true;
1943 err.accumulated_errors += "File common/ideologies.txt could not be opened\n";
1944 }
1945 }
1946 // pre parse issues.txt
1947 {
1948 context.issues_file = open_file(common, NATIVE("issues.txt"));
1949 if(context.issues_file) {
1950 auto content = view_contents(*context.issues_file);
1951 err.file_name = "issues.txt";
1952 parsers::token_generator gen(content.data, content.data + content.file_size);
1953 parsers::parse_issues_file(gen, err, context);
1954 } else {
1955 err.fatal = true;
1956 err.accumulated_errors += "File common/ideologies.txt could not be opened\n";
1957 }
1958 }
1959 for(auto i : culture_definitions.party_issues) {
1960 world.issue_set_issue_type(i, uint8_t(culture::issue_type::party));
1961 }
1962 for(auto i : culture_definitions.military_issues) {
1963 world.reform_set_reform_type(i, uint8_t(culture::issue_type::military));
1964 }
1965 for(auto i : culture_definitions.economic_issues) {
1966 world.reform_set_reform_type(i, uint8_t(culture::issue_type::economic));
1967 }
1968 for(auto i : culture_definitions.social_issues) {
1969 world.issue_set_issue_type(i, uint8_t(culture::issue_type::social));
1970 }
1972 world.issue_set_issue_type(i, uint8_t(culture::issue_type::political));
1973 }
1974 // parse governments.txt
1975 {
1976 auto governments = open_file(common, NATIVE("governments.txt"));
1977 if(governments) {
1978 auto content = view_contents(*governments);
1979 err.file_name = "governments.txt";
1980 parsers::token_generator gen(content.data, content.data + content.file_size);
1981 parsers::parse_governments_file(gen, err, context);
1982 } else {
1983 err.fatal = true;
1984 err.accumulated_errors += "File common/governments.txt could not be opened\n";
1985 }
1986 }
1987 // pre parse cb_types.txt
1988 {
1989 context.cb_types_file = open_file(common, NATIVE("cb_types.txt"));
1990 if(context.cb_types_file) {
1991 auto content = view_contents(*context.cb_types_file);
1992 err.file_name = "cb_types.txt";
1993 parsers::token_generator gen(content.data, content.data + content.file_size);
1994 parsers::parse_cb_types_file(gen, err, context);
1995 } else {
1996 err.fatal = true;
1997 err.accumulated_errors += "File common/cb_types.txt could not be opened\n";
1998 }
1999 }
2000 // parse traits.txt
2001 {
2002 auto traits = open_file(common, NATIVE("traits.txt"));
2003 if(traits) {
2004 auto content = view_contents(*traits);
2005 err.file_name = "traits.txt";
2006 parsers::token_generator gen(content.data, content.data + content.file_size);
2007 parsers::parse_traits_file(gen, err, context);
2008 } else {
2009 err.fatal = true;
2010 err.accumulated_errors += "File common/traits.txt could not be opened\n";
2011 }
2012 }
2013 // pre parse crimes.txt
2014 {
2015 context.crimes_file = open_file(common, NATIVE("crime.txt"));
2016 if(context.crimes_file) {
2017 auto content = view_contents(*context.crimes_file);
2018 err.file_name = "crime.txt";
2019 parsers::token_generator gen(content.data, content.data + content.file_size);
2020 parsers::parse_crimes_file(gen, err, context);
2021 } else {
2022 err.fatal = true;
2023 err.accumulated_errors += "File common/crime.txt could not be opened\n";
2024 }
2025 }
2026 // pre parse triggered_modifiers.txt
2027 {
2028 context.triggered_modifiers_file = open_file(common, NATIVE("triggered_modifiers.txt"));
2029 if(context.triggered_modifiers_file) {
2030 auto content = view_contents(*context.triggered_modifiers_file);
2031 err.file_name = "triggered_modifiers.txt";
2032 parsers::token_generator gen(content.data, content.data + content.file_size);
2033 parsers::parse_triggered_modifiers_file(gen, err, context);
2034 } else {
2035 err.fatal = true;
2036 err.accumulated_errors += "File common/triggered_modifiers.txt could not be opened\n";
2037 }
2038 }
2039 // parse nationalvalues.txt
2040 {
2041 auto nv_file = open_file(common, NATIVE("nationalvalues.txt"));
2042 if(nv_file) {
2043 auto content = view_contents(*nv_file);
2044 err.file_name = "nationalvalues.txt";
2045 parsers::token_generator gen(content.data, content.data + content.file_size);
2046 parsers::parse_national_values_file(gen, err, context);
2047 } else {
2048 err.fatal = true;
2049 err.accumulated_errors += "File common/nationalvalues.txt could not be opened\n";
2050 }
2051 }
2052 // parse static_modifiers.txt
2053 {
2054 auto sm_file = open_file(common, NATIVE("static_modifiers.txt"));
2055 if(sm_file) {
2056 auto content = view_contents(*sm_file);
2057 err.file_name = "static_modifiers.txt";
2058 parsers::token_generator gen(content.data, content.data + content.file_size);
2059 parsers::parse_static_modifiers_file(gen, err, context);
2060 } else {
2061 err.fatal = true;
2062 err.accumulated_errors += "File common/static_modifiers.txt could not be opened\n";
2063 }
2064 }
2065 // parse event_modifiers.txt
2066 {
2067 auto em_file = open_file(common, NATIVE("event_modifiers.txt"));
2068 if(em_file) {
2069 auto content = view_contents(*em_file);
2070 err.file_name = "event_modifiers.txt";
2071 parsers::token_generator gen(content.data, content.data + content.file_size);
2072 parsers::parse_event_modifiers_file(gen, err, context);
2073 } else {
2074 err.fatal = true;
2075 err.accumulated_errors += "File common/event_modifiers.txt could not be opened\n";
2076 }
2077 }
2078 // read *.lua, not being able to read the defines isn't fatal per se
2079 {
2080 // Default vanilla dates used if ones are not defined
2083 for(auto defines_file : simple_fs::list_files(common, NATIVE(".lua"))) {
2084 auto opened_file = open_file(defines_file);
2085 if(opened_file) {
2086 auto content = view_contents(*opened_file);
2087 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2088 defines.parse_file(*this, std::string_view(content.data, content.data + content.file_size), err);
2089 }
2090 }
2091 current_date = sys::date(bookmark_date, start_date); //relative to start date
2092 }
2093 // gather names of poptypes
2094 list_pop_types(*this, context);
2095 // pre parse rebel_types.txt
2096 {
2097 context.rebel_types_file = open_file(common, NATIVE("rebel_types.txt"));
2098 if(context.rebel_types_file) {
2099 auto content = view_contents(*context.rebel_types_file);
2100 err.file_name = "rebel_types.txt";
2101 parsers::token_generator gen(content.data, content.data + content.file_size);
2102 parsers::parse_rebel_types_file(gen, err, context);
2103 } else {
2104 err.fatal = true;
2105 err.accumulated_errors += "File common/rebel_types.txt could not be opened\n";
2106 }
2107 }
2108
2109 // parse terrain.txt
2110 {
2111 auto terrain_file = open_file(map, NATIVE("terrain.txt"));
2112 if(terrain_file) {
2113 auto content = view_contents(*terrain_file);
2114 err.file_name = "terrain.txt";
2115 parsers::token_generator gen(content.data, content.data + content.file_size);
2116 parsers::parse_terrain_file(gen, err, context);
2117 } else {
2118 err.fatal = true;
2119 err.accumulated_errors += "File map/terrain.txt could not be opened\n";
2120 }
2121 }
2122 // parse region.txt
2123 {
2124 auto region_file = open_file(map, NATIVE("region.txt"));
2125 if(region_file) {
2126 auto content = view_contents(*region_file);
2127 err.file_name = "region.txt";
2128 parsers::token_generator gen(content.data, content.data + content.file_size);
2129 parsers::parse_region_file(gen, err, context);
2130 } else {
2131 err.fatal = true;
2132 err.accumulated_errors += "File map/region.txt could not be opened\n";
2133 }
2134 }
2135 // parse super_region.txt
2136 {
2137 auto super_region_file = open_file(map, NATIVE("super_region.txt"));
2138 if(super_region_file) {
2139 auto content = view_contents(*super_region_file);
2140 err.file_name = "super_region.txt";
2141 parsers::token_generator gen(content.data, content.data + content.file_size);
2142 parsers::parse_region_file(gen, err, context);
2143 } else {
2144 // OK for this file to be missing
2145 }
2146 }
2147 // parse continent.txt
2148 {
2149 auto continent_file = open_file(map, NATIVE("continent.txt"));
2150 if(continent_file) {
2151 auto content = view_contents(*continent_file);
2152 err.file_name = "continent.txt";
2153 parsers::token_generator gen(content.data, content.data + content.file_size);
2154 parsers::parse_continent_file(gen, err, context);
2155 } else {
2156 err.fatal = true;
2157 err.accumulated_errors += "File map/continent.txt could not be opened\n";
2158 }
2159 }
2160 // parse climate.txt
2161 {
2162 auto climate_file = open_file(map, NATIVE("climate.txt"));
2163 if(climate_file) {
2164 auto content = view_contents(*climate_file);
2165 err.file_name = "climate.txt";
2166 parsers::token_generator gen(content.data, content.data + content.file_size);
2167 parsers::parse_climate_file(gen, err, context);
2168 } else {
2169 err.fatal = true;
2170 err.accumulated_errors += "File map/climate.txt could not be opened\n";
2171 }
2172 }
2173 // parse technology.txt
2174 {
2175 auto tech_file = open_file(common, NATIVE("technology.txt"));
2176 if(tech_file) {
2177 auto content = view_contents(*tech_file);
2178 err.file_name = "technology.txt";
2179 parsers::token_generator gen(content.data, content.data + content.file_size);
2180 parsers::parse_technology_main_file(gen, err, context);
2181 } else {
2182 err.fatal = true;
2183 err.accumulated_errors += "File common/technology.txt could not be opened\n";
2184 }
2185 }
2186 // pre parse inventions
2187 {
2188 auto inventions = open_directory(root, NATIVE("inventions"));
2189 for(auto& invf : simple_fs::list_files(inventions, NATIVE(".txt"))) {
2191 if(simple_fs::get_file_name(invf) == NATIVE("army_inventions.txt")) {
2193 } else if(simple_fs::get_file_name(invf) == NATIVE("navy_inventions.txt")) {
2195 } else if(simple_fs::get_file_name(invf) == NATIVE("commerce_inventions.txt")) {
2197 } else if(simple_fs::get_file_name(invf) == NATIVE("culture_inventions.txt")) {
2199 } else if(simple_fs::get_file_name(invf) == NATIVE("industry_inventions.txt")) {
2201 //non vanilla
2202 } else if(simple_fs::get_file_name(invf) == NATIVE("military_theory_inventions.txt")) {
2204 } else if(simple_fs::get_file_name(invf) == NATIVE("diplomacy_inventions.txt")) {
2206 } else if(simple_fs::get_file_name(invf) == NATIVE("population_inventions.txt")) {
2208 } else if(simple_fs::get_file_name(invf) == NATIVE("flavor_inventions.txt")) {
2210 }
2211
2212 parsers::tech_group_context invention_context{ context, cat };
2213 auto i_file = open_file(invf);
2214 if(i_file) {
2215 auto content = view_contents(*i_file);
2217 parsers::token_generator gen(content.data, content.data + content.file_size);
2218 parsers::parse_inventions_file(gen, err, invention_context);
2219 context.tech_and_invention_files.emplace_back(std::move(*i_file));
2220 }
2221 }
2222 }
2223 // load unit type definitions
2224 {
2225 parsers::make_base_units(context);
2226
2227 auto units = open_directory(root, NATIVE("units"));
2228 for(auto unit_file : simple_fs::list_files(units, NATIVE(".txt"))) {
2229 auto opened_file = open_file(unit_file);
2230 if(opened_file) {
2231 auto content = view_contents(*opened_file);
2232 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2233 parsers::token_generator gen(content.data, content.data + content.file_size);
2234 parsers::parse_unit_file(gen, err, context);
2235 }
2236 }
2237
2238 if(!bool(military_definitions.infantry)) {
2239 err.accumulated_errors += "No infantry (or equivalent unit type) found\n";
2240 }
2241 if(!bool(military_definitions.irregular)) {
2242 err.accumulated_errors += "No irregular (or equivalent unit type) found\n";
2243 }
2244 if(!bool(military_definitions.artillery)) {
2245 err.accumulated_errors += "No artillery (or equivalent unit type) found\n";
2246 }
2247 }
2248 // make space in arrays
2249
2250 world.national_identity_resize_unit_names_count(uint32_t(military_definitions.unit_base_definitions.size()));
2251 world.national_identity_resize_unit_names_first(uint32_t(military_definitions.unit_base_definitions.size()));
2252
2253 world.political_party_resize_party_issues(world.issue_size());
2254
2255 world.province_resize_party_loyalty(world.ideology_size());
2256 world.province_resize_building_level(economy::max_building_types);
2257
2258 world.pop_type_resize_everyday_needs(world.commodity_size());
2259 world.pop_type_resize_luxury_needs(world.commodity_size());
2260 world.pop_type_resize_life_needs(world.commodity_size());
2261 world.pop_type_resize_ideology(world.ideology_size());
2262 world.pop_type_resize_issues(world.issue_option_size());
2263 world.pop_type_resize_promotion(world.pop_type_size());
2264
2265 world.national_focus_resize_production_focus(world.commodity_size());
2266
2267 world.technology_resize_activate_building(world.factory_type_size());
2268 world.technology_resize_activate_unit(uint32_t(military_definitions.unit_base_definitions.size()));
2269 world.technology_resize_increase_building(uint32_t(economy::max_building_types));
2270 world.invention_resize_increase_building(uint32_t(economy::max_building_types));
2271
2272 world.invention_resize_activate_building(world.factory_type_size());
2273 world.invention_resize_activate_unit(uint32_t(military_definitions.unit_base_definitions.size()));
2274 world.invention_resize_activate_crime(uint32_t(culture_definitions.crimes.size()));
2275
2276 world.rebel_type_resize_government_change(world.government_type_size());
2277
2278 world.nation_resize_max_building_level(economy::max_building_types);
2279 world.nation_resize_active_inventions(world.invention_size());
2280 world.nation_resize_active_technologies(world.technology_size());
2281 world.nation_resize_upper_house(world.ideology_size());
2282
2283 world.national_identity_resize_government_flag_type(world.government_type_size());
2284 world.national_identity_resize_government_name(world.government_type_size());
2285 world.national_identity_resize_government_adjective(world.government_type_size());
2286 world.national_identity_resize_government_ruler_name(world.government_type_size());
2287 world.national_identity_resize_government_color(world.government_type_size());
2288
2289 // add special names
2290 for(auto ident : world.in_national_identity) {
2291 auto const tag = nations::int_to_tag(ident.get_identifying_int());
2292 for(auto const& named_gov : context.map_of_governments) {
2293 auto const name = tag + "_" + named_gov.first;
2294 auto name_k = add_key_win1252(name);
2295 ident.set_government_name(named_gov.second, name_k);
2296 auto const adj = tag + "_" + named_gov.first + "_ADJ";
2297 auto adj_k = add_key_win1252(adj);
2298 ident.set_government_adjective(named_gov.second, adj_k);
2299 auto const ruler = tag + "_" + named_gov.first + "_ruler";
2300 auto ruler_k = add_key_win1252(ruler);
2301 ident.set_government_ruler_name(named_gov.second, ruler_k);
2302 }
2303 }
2304
2305 // load scripted triggers
2306 auto stdir = open_directory(root, NATIVE("scripted triggers"));
2307 for(auto st_file : simple_fs::list_files(stdir, NATIVE(".txt"))) {
2308 auto opened_file = open_file(st_file);
2309 if(opened_file) {
2310 auto content = view_contents(*opened_file);
2311 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2312 parsers::token_generator gen(content.data, content.data + content.file_size);
2313 parsers::parse_scripted_trigger_file(gen, err, context);
2314 }
2315 }
2316
2317 // load country files
2318 world.for_each_national_identity([&](dcon::national_identity_id i) {
2319 auto country_file = open_file(common, simple_fs::win1250_to_native(context.file_names_for_idents[i]));
2320 if(country_file) {
2321 parsers::country_file_context c_context{ context, i };
2322 auto content = view_contents(*country_file);
2323 err.file_name = context.file_names_for_idents[i];
2324 parsers::token_generator gen(content.data, content.data + content.file_size);
2325 parsers::parse_country_file(gen, err, c_context);
2326 }
2327 });
2328
2329 world.province_resize_rgo_max_size_per_good(world.commodity_size());
2330
2331 // load province history files
2332 auto history = open_directory(root, NATIVE("history"));
2333 {
2334 auto prov_history = open_directory(history, NATIVE("provinces"));
2335 auto const load_from_dir = [&](auto const subdir) {
2336 // Modding extension:
2337 for(auto province_file : list_files(subdir, NATIVE(".csv"))) {
2338 auto opened_file = open_file(province_file);
2339 if(opened_file) {
2340 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2341 auto content = view_contents(*opened_file);
2342 parsers::parse_csv_province_history_file(*this, content.data, content.data + content.file_size, err, context);
2343 }
2344 }
2345 for(auto prov_file : list_files(subdir, NATIVE(".txt"))) {
2346 auto file_name = simple_fs::native_to_utf8(get_file_name(prov_file));
2347 auto name_start = file_name.c_str();
2348 auto name_end = name_start + file_name.length();
2349 // exclude files starting with "~" for example
2350 if(name_start < name_end && !isdigit(*name_start))
2351 continue;
2352
2353 auto value_start = name_start;
2354 for(; value_start < name_end; ++value_start) {
2355 if(isdigit(*value_start))
2356 break;
2357 }
2358 auto value_end = value_start;
2359 for(; value_end < name_end; ++value_end) {
2360 if(!isdigit(*value_end))
2361 break;
2362 }
2363
2364 err.file_name = simple_fs::native_to_utf8(get_full_name(prov_file));
2365 auto province_id = parsers::parse_int(std::string_view(value_start, value_end), 0, err);
2366 if(province_id > 0 && uint32_t(province_id) < context.original_id_to_prov_id_map.size()) {
2367 auto opened_file = open_file(prov_file);
2368 if(opened_file) {
2369 auto pid = context.original_id_to_prov_id_map[province_id];
2370 parsers::province_file_context pf_context{ context, pid };
2371 auto content = view_contents(*opened_file);
2372 parsers::token_generator gen(content.data, content.data + content.file_size);
2373 parsers::parse_province_history_file(gen, err, pf_context);
2374 }
2375 }
2376 }
2377 };
2378 load_from_dir(prov_history);
2379 for(auto const& subdir : list_subdirectories(prov_history)) {
2380 load_from_dir(subdir);
2381 }
2382 }
2384 // load pop history files
2385 {
2386 auto pop_history = open_directory(history, NATIVE("pops"));
2387 auto startdate = current_date.to_ymd(start_date);
2388 auto start_dir_name = std::to_string(startdate.year) + "." + std::to_string(startdate.month) + "." + std::to_string(startdate.day);
2389 auto date_directory = open_directory(pop_history, simple_fs::utf8_to_native(start_dir_name));
2390 // NICK:
2391 // Attempts to look through the start date as defined by the mod.
2392 // If it does not find any pop files there, it defaults to looking through 1836.1.1
2393 // This is to deal with mods that have their start date defined as something else, but have pop history within 1836.1.1 (converters).
2394 auto directory_file_count = list_files(date_directory, NATIVE(".txt")).size();
2395 if(directory_file_count == 0)
2396 date_directory = open_directory(pop_history, simple_fs::utf8_to_native("1836.1.1"));
2397 for(auto pop_file : list_files(date_directory, NATIVE(".txt"))) {
2398 auto opened_file = open_file(pop_file);
2399 if(opened_file) {
2400 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2401 auto content = view_contents(*opened_file);
2402 parsers::token_generator gen(content.data, content.data + content.file_size);
2403 parsers::parse_pop_history_file(gen, err, context);
2404 }
2405 }
2406 // Modding extension:
2407 // Support loading pops from a CSV file, this to condense them better and allow
2408 // for them to load faster and better ordered, editable with a spreadsheet program
2409 for(auto pop_file : list_files(date_directory, NATIVE(".csv"))) {
2410 auto opened_file = open_file(pop_file);
2411 if(opened_file) {
2412 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2413 auto content = view_contents(*opened_file);
2414 parsers::parse_csv_pop_history_file(*this, content.data, content.data + content.file_size, err, context);
2415 }
2416 }
2417 }
2418
2419 // load poptype definitions
2420 {
2421 auto poptypes = open_directory(root, NATIVE("poptypes"));
2422 for(auto pr : context.map_of_poptypes) {
2423 auto opened_file = open_file(poptypes, simple_fs::utf8_to_native(pr.first + ".txt"));
2424 if(opened_file) {
2425 err.file_name = pr.first + ".txt";
2426 auto content = view_contents(*opened_file);
2427 parsers::poptype_context inner_context{ context, pr.second };
2428 parsers::token_generator gen(content.data, content.data + content.file_size);
2429 parsers::parse_poptype_file(gen, err, inner_context);
2430 }
2431 }
2432 }
2433
2434 // load ideology contents
2435 {
2436 err.file_name = "ideologies.txt";
2437 for(auto& pr : context.map_of_ideologies) {
2438 parsers::individual_ideology_context new_context{ context, pr.second.id };
2439 parsers::parse_individual_ideology(pr.second.generator_state, err, new_context);
2440 }
2441 }
2443 if(auto it = context.map_of_ideologies.find("conservative"); it != context.map_of_ideologies.end()) {
2444 culture_definitions.conservative = it->second.id;
2445 err.accumulated_warnings += "conservative ideology lacks \"can_reduce_militancy = 1\" key\n";
2446 }
2448 err.accumulated_errors += "NO CONSERVATIVE IDEOLOGY (fatal error)\n";
2449 err.fatal = true;
2450 }
2451 }
2452 // triggered modifier contents
2453 {
2454 err.file_name = "triggered_modifiers.txt";
2455 for(auto& r : context.set_of_triggered_modifiers) {
2456 national_definitions.triggered_modifiers[r.index].trigger_condition =
2457 parsers::read_triggered_modifier_condition(r.generator_state, err, context);
2458 }
2459 }
2460 // cb contents
2461 {
2462 err.file_name = "cb_types.txt";
2463 for(auto& r : context.map_of_cb_types) {
2464 parsers::individual_cb_context new_context{ context, r.second.id };
2465 parsers::parse_cb_body(r.second.generator_state, err, new_context);
2466 }
2467 }
2468 // pending crimes
2469 {
2470 err.file_name = "crime.txt";
2471 for(auto& r : context.map_of_crimes) {
2472 parsers::read_pending_crime(r.second.id, r.second.generator_state, err, context);
2473 }
2474 }
2475 world.issue_option_resize_support_modifiers(world.issue_option_size());
2476 // pending issue options
2477 {
2478 err.file_name = "issues.txt";
2479 for(auto& r : context.map_of_ioptions) {
2480 parsers::read_pending_option(r.second.id, r.second.generator_state, err, context);
2481 }
2482 }
2483 // pending reform options
2484 {
2485 err.file_name = "issues.txt";
2486 for(auto& r : context.map_of_roptions) {
2487 parsers::read_pending_reform(r.second.id, r.second.generator_state, err, context);
2488 }
2489 }
2490 // parse national_focus.txt
2491 {
2492 auto nat_focus = open_file(common, NATIVE("national_focus.txt"));
2493 if(nat_focus) {
2494 auto content = view_contents(*nat_focus);
2495 err.file_name = "national_focus.txt";
2496 parsers::token_generator gen(content.data, content.data + content.file_size);
2497 parsers::parse_national_focus_file(gen, err, context);
2498 } else {
2499 err.fatal = true;
2500 err.accumulated_errors += "File common/national_focus.txt could not be opened\n";
2501 }
2502 }
2503 // load pop_types.txt
2504 {
2505 auto pop_types_file = open_file(common, NATIVE("pop_types.txt"));
2506 if(pop_types_file) {
2507 auto content = view_contents(*pop_types_file);
2508 err.file_name = "pop_types.txt";
2509 parsers::token_generator gen(content.data, content.data + content.file_size);
2510 parsers::parse_main_pop_type_file(gen, err, context);
2511 } else {
2512 err.fatal = true;
2513 err.accumulated_errors += "File common/pop_types.txt could not be opened\n";
2514 }
2515 }
2516 // read pending techs
2517 {
2518 err.file_name = "technology file";
2519 for(auto& r : context.map_of_technologies) {
2520 parsers::read_pending_technology(r.second.id, r.second.generator_state, err, context);
2521 }
2522 }
2523 // read pending inventions
2524 {
2525 err.file_name = "inventions file";
2526 for(auto& r : context.map_of_inventions) {
2527 parsers::read_pending_invention(r.second.id, r.second.generator_state, err, context);
2528 }
2529
2530 // fix invention tech category
2531 for(auto inv : world.in_invention) {
2532 if(inv.get_technology_type() == uint8_t(culture::tech_category::unknown)) {
2533 auto lim_trigger = inv.get_limit();
2534 if(lim_trigger) {
2535 trigger::recurse_over_triggers(trigger_data.data() + trigger_data_indices[lim_trigger.index() + 1],
2536 [&](uint16_t* tval) {
2537 if((tval[0] & trigger::code_mask) == trigger::technology) {
2538 auto findex = this->world.technology_get_folder_index(trigger::payload(tval[1]).tech_id);
2539 inv.set_technology_type(uint8_t(this->culture_definitions.tech_folders[findex].category));
2540 }
2541 });
2542 }
2543 }
2544 if(inv.get_technology_type() == uint8_t(culture::tech_category::unknown)) {
2545 err.accumulated_warnings += "failed to find a technology category for invention ";
2546 err.accumulated_warnings += text::produce_simple_string(*this, inv.get_name()) + "\n";
2547 }
2548 }
2549 }
2550 // parse on_actions.txt
2551 {
2552 auto on_action = open_file(common, NATIVE("on_actions.txt"));
2553 if(on_action) {
2554 auto content = view_contents(*on_action);
2555 err.file_name = "on_actions.txt";
2556 parsers::token_generator gen(content.data, content.data + content.file_size);
2557 parsers::parse_on_action_file(gen, err, context);
2558 } else {
2559 err.fatal = true;
2560 err.accumulated_errors += "File common/on_actions.txt could not be opened\n";
2561 }
2562 }
2563 // parse production_types.txt
2564 {
2565 auto prod_types = open_file(common, NATIVE("production_types.txt"));
2566 if(prod_types) {
2567 auto content = view_contents(*prod_types);
2568 err.file_name = "production_types.txt";
2569 parsers::token_generator gen(content.data, content.data + content.file_size);
2570
2571 parsers::production_context new_context{ context };
2572 parsers::parse_production_types_file(gen, err, new_context);
2573
2574 for(const auto ft : world.in_factory_type) {
2575 if(!bool(world.factory_type_get_output(ft))) {
2576 err.accumulated_errors += "No output defined for factory " + std::string(text::produce_simple_string(*this, world.factory_type_get_name(ft))) + " (" + err.file_name + ")\n";
2577 }
2578 }
2579 if(!new_context.found_worker_types) {
2580 err.fatal = true;
2581 err.accumulated_errors += "Unable to identify factory worker types from production_types.txt\n";
2582 }
2583 } else {
2584 err.fatal = true;
2585 err.accumulated_errors += "File common/production_types.txt could not be opened\n";
2586 }
2587 }
2588 // read pending rebel types
2589 {
2590 err.file_name = "rebel_types.txt";
2591 for(auto& r : context.map_of_rebeltypes) {
2592 parsers::read_pending_rebel_type(r.second.id, r.second.generator_state, err, context);
2593 }
2594 }
2595 // load decisions
2596 {
2597 auto decisions = open_directory(root, NATIVE("decisions"));
2598 for(auto decision_file : list_files(decisions, NATIVE(".txt"))) {
2599 auto opened_file = open_file(decision_file);
2600 if(opened_file) {
2601 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2602 auto content = view_contents(*opened_file);
2603 parsers::token_generator gen(content.data, content.data + content.file_size);
2604 parsers::parse_decision_file(gen, err, context);
2605 }
2606 }
2607 }
2608 // load events
2609 {
2610 auto events = open_directory(root, NATIVE("events"));
2611 std::vector<simple_fs::file> held_open_files;
2612 for(auto event_file : list_files(events, NATIVE(".txt"))) {
2613 auto opened_file = open_file(event_file);
2614 if(opened_file) {
2615 err.file_name = simple_fs::native_to_utf8(get_full_name(*opened_file));
2616 auto content = view_contents(*opened_file);
2617 parsers::token_generator gen(content.data, content.data + content.file_size);
2618 parsers::parse_event_file(gen, err, context);
2619 held_open_files.emplace_back(std::move(*opened_file));
2620 }
2621 }
2622 err.file_name = "pending events";
2623 parsers::commit_pending_events(err, context);
2624 }
2625 // load news
2626 {
2627 auto news_dir = open_directory(root, NATIVE("news"));
2628 for(auto news_file : list_files(news_dir, NATIVE(".txt"))) {
2629 auto opened_file = open_file(news_file);
2630 if(opened_file) {
2631 err.file_name = simple_fs::native_to_utf8(simple_fs::get_full_name(*opened_file));
2632 auto content = view_contents(*opened_file);
2633 parsers::token_generator gen(content.data, content.data + content.file_size);
2634 parsers::parse_news_file(gen, err, parsers::news_context{ context });
2635 }
2636 }
2637 }
2638 // load tutorial
2639 {
2640 auto tutorial_dir = open_directory(root, NATIVE("tutorial"));
2641 for(auto tutorial_file : list_files(tutorial_dir, NATIVE(".txt"))) {
2642 auto opened_file = open_file(tutorial_file);
2643 if(opened_file) {
2644 err.file_name = simple_fs::native_to_utf8(simple_fs::get_full_name(*opened_file));
2645 auto content = view_contents(*opened_file);
2646 parsers::token_generator gen(content.data, content.data + content.file_size);
2647 parsers::parse_tutorial_file(gen, err, context);
2648 }
2649 }
2650 }
2651 // load battleplan settings
2652 {
2653 auto bp_dir = open_directory(root, NATIVE("battleplans"));
2654 for(auto file : list_files(bp_dir, NATIVE(".txt"))) {
2655 if(auto f = open_file(file); f) {
2657 auto content = view_contents(*f);
2658 parsers::token_generator gen(content.data, content.data + content.file_size);
2659 parsers::parse_battleplan_settings_file(gen, err, context);
2660 }
2661 }
2662 }
2663
2664 // load oob
2665 {
2666 auto oob_dir = open_directory(history, NATIVE("units"));
2667
2668 for(auto oob_file : list_files(oob_dir, NATIVE(".txt"))) {
2669 auto file_name = get_full_name(oob_file);
2670 if(file_name == NATIVE("v2dd2.txt")) // discard junk file
2671 continue;
2672 auto last = file_name.c_str() + file_name.length();
2673 auto first = file_name.c_str();
2674 auto start_of_name = last;
2675 for(; start_of_name >= first; --start_of_name) {
2676 if(*start_of_name == NATIVE('\\') || *start_of_name == NATIVE('/')) {
2677 ++start_of_name;
2678 break;
2679 }
2680 }
2681 if(last - start_of_name >= 3) {
2682 auto utf8name = simple_fs::native_to_utf8(native_string_view(start_of_name, last - start_of_name));
2683 if(utf8name[0] == 'R' && utf8name[1] == 'E' && utf8name[2] == 'B') {
2684 // ignore REB
2685 } else if(auto it = context.map_of_ident_names.find(nations::tag_to_int(utf8name[0], utf8name[1], utf8name[2])); it != context.map_of_ident_names.end()) {
2686 auto holder = context.state.world.national_identity_get_nation_from_identity_holder(it->second);
2687 if(holder) {
2688 parsers::oob_file_context new_context{ context, holder };
2689 auto opened_file = open_file(oob_file);
2690 if(opened_file) {
2691 err.file_name = utf8name;
2692 auto content = view_contents(*opened_file);
2693 parsers::token_generator gen(content.data, content.data + content.file_size);
2694 parsers::parse_oob_file(gen, err, new_context);
2695 }
2696 } else {
2697 err.accumulated_warnings += "dead tag " + utf8name.substr(0, 3) + " encountered while scanning oob files\n";
2698 }
2699 } else {
2700 err.accumulated_warnings += "invalid tag " + utf8name.substr(0, 3) + " encountered while scanning oob files\n";
2701 }
2702 }
2703 }
2704
2705 auto startdate = current_date.to_ymd(start_date);
2706 auto start_dir_name = std::to_string(startdate.year);
2707 auto date_directory = open_directory(oob_dir, simple_fs::utf8_to_native(start_dir_name));
2708 for(auto oob_file : list_files(date_directory, NATIVE(".txt"))) {
2709 auto file_name = get_full_name(oob_file);
2710 auto last = file_name.c_str() + file_name.length();
2711 auto first = file_name.c_str();
2712 auto start_of_name = last;
2713 for(; start_of_name >= first; --start_of_name) {
2714 if(*start_of_name == NATIVE('\\') || *start_of_name == NATIVE('/')) {
2715 ++start_of_name;
2716 break;
2717 }
2718 }
2719 if(last - start_of_name >= 3) {
2720 auto utf8name = simple_fs::native_to_utf8(native_string_view(start_of_name, last - start_of_name));
2721 if(utf8name[0] == 'R' && utf8name[1] == 'E' && utf8name[2] == 'B') {
2722 // ignore REB
2723 } else if(auto it = context.map_of_ident_names.find(nations::tag_to_int(utf8name[0], utf8name[1], utf8name[2])); it != context.map_of_ident_names.end()) {
2724 auto holder = context.state.world.national_identity_get_nation_from_identity_holder(it->second);
2725 if(holder) {
2726 parsers::oob_file_context new_context{ context, holder };
2727 auto opened_file = open_file(oob_file);
2728 if(opened_file) {
2729 err.file_name = utf8name;
2730 auto content = view_contents(*opened_file);
2731 parsers::token_generator gen(content.data, content.data + content.file_size);
2732 parsers::parse_oob_file(gen, err, new_context);
2733 }
2734 } else {
2735 err.accumulated_warnings += "dead tag " + utf8name.substr(0, 3) + " encountered while scanning oob files\n";
2736 }
2737 } else {
2738 err.accumulated_warnings += "invalid tag " + utf8name.substr(0, 3) + " encountered while scanning oob files\n";
2739 }
2740 }
2741 }
2742 }
2743 // parse diplomacy history
2744 {
2745 auto diplomacy_dir = open_directory(history, NATIVE("diplomacy"));
2746 for(auto dip_file : list_files(diplomacy_dir, NATIVE(".txt"))) {
2747 auto opened_file = open_file(dip_file);
2748 if(opened_file) {
2749 auto content = view_contents(*opened_file);
2750 err.file_name = simple_fs::native_to_utf8(simple_fs::get_full_name(*opened_file));
2751 parsers::token_generator gen(content.data, content.data + content.file_size);
2752 parsers::parse_diplomacy_file(gen, err, context);
2753 }
2754 }
2755 }
2756
2757 // !!!! yes, I know
2758 world.nation_resize_flag_variables(uint32_t(national_definitions.num_allocated_national_flags));
2759 national_definitions.global_flag_variables.resize((national_definitions.num_allocated_global_flags + 7) / 8, dcon::bitfield_type{ 0 });
2760 world.nation_resize_accepted_cultures(world.culture_size());
2761
2762 std::vector<std::pair<dcon::nation_id, dcon::decision_id>> pending_decisions;
2763 // load country history
2764 {
2765 auto country_dir = open_directory(history, NATIVE("countries"));
2766 for(auto country_file : list_files(country_dir, NATIVE(".txt"))) {
2767 auto file_name = get_full_name(country_file);
2768
2769 auto last = file_name.c_str() + file_name.length();
2770 auto first = file_name.c_str();
2771 auto start_of_name = last;
2772 for(; start_of_name >= first; --start_of_name) {
2773 if(*start_of_name == NATIVE('\\') || *start_of_name == NATIVE('/')) {
2774 ++start_of_name;
2775 break;
2776 }
2777 }
2778 if(last - start_of_name >= 6) {
2779 auto utf8name = simple_fs::native_to_utf8(native_string_view(start_of_name, last - start_of_name));
2780
2781 if(auto it = context.map_of_ident_names.find(nations::tag_to_int(utf8name[0], utf8name[1], utf8name[2]));
2782 it != context.map_of_ident_names.end()) {
2783 auto holder = context.state.world.national_identity_get_nation_from_identity_holder(it->second);
2784
2785 if(!holder) {
2786 holder = world.create_nation();
2787 world.nation_set_diplomatic_points(holder, 1.0f);
2788 world.try_create_identity_holder(holder, it->second);
2789 }
2790
2791 parsers::country_history_context new_context{ context, it->second, holder, pending_decisions };
2792
2793 auto opened_file = open_file(country_file);
2794 if(opened_file) {
2795 err.file_name = utf8name;
2796 auto content = view_contents(*opened_file);
2797 parsers::token_generator gen(content.data, content.data + content.file_size);
2798 parsers::parse_country_history_file(gen, err, new_context);
2799 }
2800
2801 } else {
2802 err.accumulated_warnings += "invalid tag " + utf8name.substr(0, 3) + " encountered while scanning country history files\n";
2803 }
2804 }
2805 }
2806 }
2807
2808 // load war history
2809 {
2810 auto country_dir = open_directory(history, NATIVE("wars"));
2811 for(auto war_file : list_files(country_dir, NATIVE(".txt"))) {
2812 auto opened_file = open_file(war_file);
2813 if(opened_file) {
2814 parsers::war_history_context new_context{ context };
2815
2816 err.file_name = simple_fs::native_to_utf8(simple_fs::get_full_name(*opened_file));
2817 auto content = view_contents(*opened_file);
2818 parsers::token_generator gen(content.data, content.data + content.file_size);
2819 parsers::parse_war_history_file(gen, err, new_context);
2820 }
2821 }
2822 }
2823
2824 // misc touch ups
2826 world.nation_resize_stockpiles(world.commodity_size());
2827 world.nation_resize_variables(uint32_t(national_definitions.num_allocated_national_variables));
2828 world.pop_resize_udemographics(pop_demographics::size(*this));
2829 national_definitions.global_flag_variables.resize((national_definitions.num_allocated_global_flags + 7) / 8, dcon::bitfield_type{ 0 });
2830
2831 // add dummy nations for unheld tags
2832 world.for_each_national_identity([&](dcon::national_identity_id id) {
2833 if(!world.national_identity_get_nation_from_identity_holder(id)) {
2834 auto new_nation = world.create_nation();
2835 world.try_create_identity_holder(new_nation, id);
2836 }
2837 });
2838
2839 world.nation_resize_modifier_values(sys::national_mod_offsets::count);
2840 world.nation_resize_rgo_goods_output(world.commodity_size());
2841 world.nation_resize_factory_goods_output(world.commodity_size());
2842 world.nation_resize_factory_goods_throughput(world.commodity_size());
2843 world.nation_resize_rgo_size(world.commodity_size());
2844 world.nation_resize_unlocked_commodities(world.commodity_size());
2845 world.nation_resize_rebel_org_modifier(world.rebel_type_size());
2846 world.nation_resize_active_unit(uint32_t(military_definitions.unit_base_definitions.size()));
2847 world.nation_resize_active_crime(uint32_t(culture_definitions.crimes.size()));
2848 world.nation_resize_active_building(world.factory_type_size());
2849 world.nation_resize_unit_stats(uint32_t(military_definitions.unit_base_definitions.size()));
2850 world.nation_resize_max_building_level(economy::max_building_types);
2851 world.province_resize_modifier_values(provincial_mod_offsets::count);
2852 world.nation_resize_demographics(demographics::size(*this));
2853 world.state_instance_resize_demographics(demographics::size(*this));
2854 world.province_resize_demographics(demographics::size(*this));
2855 world.province_resize_rgo_profit_per_good(world.commodity_size());
2856 world.province_resize_rgo_actual_production_per_good(world.commodity_size());
2857 world.province_resize_rgo_employment_per_good(world.commodity_size());
2858 world.province_resize_rgo_target_employment_per_good(world.commodity_size());
2859
2860 world.trade_route_resize_volume(world.commodity_size());
2861 world.nation_resize_factory_type_experience(world.factory_type_size());
2862 world.nation_resize_factory_type_experience_priority_national(world.factory_type_size());
2863 world.nation_resize_factory_type_experience_priority_private(world.factory_type_size());
2864
2865 world.market_resize_price(world.commodity_size());
2866 world.market_resize_supply(world.commodity_size());
2867 world.market_resize_demand(world.commodity_size());
2868 world.market_resize_stockpile(world.commodity_size());
2869 world.market_resize_consumption(world.commodity_size());
2870 world.market_resize_intermediate_demand(world.commodity_size());
2871
2872 world.market_resize_life_needs_costs(world.pop_type_size());
2873 world.market_resize_everyday_needs_costs(world.pop_type_size());
2874 world.market_resize_luxury_needs_costs(world.pop_type_size());
2875 world.market_resize_life_needs_scale(world.pop_type_size());
2876 world.market_resize_everyday_needs_scale(world.pop_type_size());
2877 world.market_resize_luxury_needs_scale(world.pop_type_size());
2878 world.market_resize_max_life_needs_satisfaction(world.pop_type_size());
2879 world.market_resize_max_everyday_needs_satisfaction(world.pop_type_size());
2880 world.market_resize_max_luxury_needs_satisfaction(world.pop_type_size());
2881
2882 world.market_resize_import(world.commodity_size());
2883 world.market_resize_export(world.commodity_size());
2884 world.market_resize_army_demand(world.commodity_size());
2885 world.market_resize_navy_demand(world.commodity_size());
2886 world.market_resize_construction_demand(world.commodity_size());
2887 world.market_resize_private_construction_demand(world.commodity_size());
2888 world.market_resize_demand_satisfaction(world.commodity_size());
2889 world.market_resize_direct_demand_satisfaction(world.commodity_size());
2890 world.market_resize_supply_sold_ratio(world.commodity_size());
2891 world.market_resize_life_needs_weights(world.commodity_size());
2892 world.market_resize_everyday_needs_weights(world.commodity_size());
2893 world.market_resize_luxury_needs_weights(world.commodity_size());
2894
2895 world.nation_resize_stockpile_targets(world.commodity_size());
2896 world.nation_resize_drawing_on_stockpiles(world.commodity_size());
2897 world.commodity_resize_price_record(economy::price_history_length);
2898 world.nation_resize_gdp_record(economy::gdp_history_length);
2899
2900 nations_by_rank.resize(2000); // TODO: take this value directly from the data container: max number of nations
2901 nations_by_industrial_score.resize(2000);
2902 nations_by_military_score.resize(2000);
2903 nations_by_prestige_score.resize(2000);
2904 crisis_participants.resize(2000);
2905 crisis_attacker_wargoals.resize(2000);
2906 crisis_defender_wargoals.resize(2000);
2907
2908 selected_regiments.resize(const_max_selected_units);
2909 selected_ships.resize(const_max_selected_units);
2910
2911
2912 for(auto t : world.in_technology) {
2913 for(auto n : world.in_nation) {
2914 if(n.get_active_technologies(t))
2915 culture::apply_technology(*this, n, t);
2916 }
2917 }
2918 for(auto t : world.in_invention) {
2919 for(auto n : world.in_nation) {
2920 if(trigger::evaluate(*this, t.get_limit(), trigger::to_generic(n), trigger::to_generic(n), -1)
2921 && trigger::evaluate_additive_modifier(*this, t.get_chance(), trigger::to_generic(n), trigger::to_generic(n), -1) > 0.f) {
2922 n.set_active_inventions(t, true);
2923 }
2924 if(n.get_active_inventions(t)) {
2925 culture::apply_invention(*this, n, t);
2926 }
2927 }
2928 }
2929
2930 map_loader.join();
2931
2932 // touch up adjacencies
2933 world.for_each_province_adjacency([&](dcon::province_adjacency_id id) {
2934 auto frel = fatten(world, id);
2935 auto prov_a = frel.get_connected_provinces(0);
2936 auto prov_b = frel.get_connected_provinces(1);
2937 if(prov_a.id.index() < province_definitions.first_sea_province.index() && prov_b.id.index() >= province_definitions.first_sea_province.index()) {
2938 frel.get_type() |= province::border::coastal_bit;
2939 } else if(prov_a.id.index() >= province_definitions.first_sea_province.index() && prov_b.id.index() < province_definitions.first_sea_province.index()) {
2940 frel.get_type() |= province::border::coastal_bit;
2941 }
2942 if(prov_a.get_state_from_abstract_state_membership() != prov_b.get_state_from_abstract_state_membership()) {
2943 frel.get_type() |= province::border::state_bit;
2944 }
2945 if(prov_a.get_nation_from_province_ownership() != prov_b.get_nation_from_province_ownership()) {
2946 frel.get_type() |= province::border::national_bit;
2947 }
2948 });
2949
2950 // fill in the terrain type
2951
2952 for(int32_t i = 0; i < province_definitions.first_sea_province.index(); ++i) {
2953 dcon::province_id id{ dcon::province_id::value_base_t(i) };
2954 if(!world.province_get_terrain(id)) { // don't overwrite if set by the history file
2955 auto terrain_type = map_state.map_data.median_terrain_type[province::to_map_id(id)];
2956 if(terrain_type < 64) {
2957 auto modifier = context.modifier_by_terrain_index[terrain_type];
2958 world.province_set_terrain(id, modifier);
2959 }
2960 }
2961 }
2962 for(int32_t i = province_definitions.first_sea_province.index(); i < int32_t(world.province_size()); ++i) {
2963 dcon::province_id id{ dcon::province_id::value_base_t(i) };
2964 world.province_set_terrain(id, context.ocean_terrain);
2965 }
2966
2967 /*
2968 Lake removal
2969 -- this is basically using the connected region algorithm on the water provinces
2970 */
2971 {
2972 world.for_each_province([&](dcon::province_id id) { world.province_set_connected_region_id(id, 0); });
2973
2974 std::vector<dcon::province_id> to_fill_list;
2975 std::vector<int32_t> region_sizes;
2976
2977 uint16_t current_fill_id = 0;
2978 province_definitions.connected_region_is_coastal.clear();
2979
2980 to_fill_list.reserve(world.province_size());
2981
2982 for(int32_t i = int32_t(world.province_size()); i-- > province_definitions.first_sea_province.index();) {
2983 dcon::province_id id{ dcon::province_id::value_base_t(i) };
2984
2985 if(world.province_get_connected_region_id(id) == 0) {
2986 ++current_fill_id;
2987
2988 region_sizes.push_back(0);
2989 to_fill_list.push_back(id);
2990
2991 while(!to_fill_list.empty()) {
2992 auto current_id = to_fill_list.back();
2993 to_fill_list.pop_back();
2994 region_sizes.back() += 1;
2995
2996 world.province_set_connected_region_id(current_id, current_fill_id);
2997 for(auto rel : world.province_get_province_adjacency(current_id)) {
2998 if((rel.get_type() & (province::border::coastal_bit | province::border::impassible_bit)) == 0) { // not leaving sea, not impassible
2999 if(rel.get_connected_provinces(0).get_connected_region_id() == 0)
3000 to_fill_list.push_back(rel.get_connected_provinces(0));
3001 if(rel.get_connected_provinces(1).get_connected_region_id() == 0)
3002 to_fill_list.push_back(rel.get_connected_provinces(1));
3003 }
3004 }
3005 }
3006
3007 to_fill_list.clear();
3008 }
3009 }
3010
3011 int32_t max = 0;
3012 for(int32_t i = 0; i < int32_t(region_sizes.size()); ++i) {
3013 if(region_sizes[max] < region_sizes[i])
3014 max = i;
3015 }
3016
3017 if(!region_sizes.empty()) {
3018 for(auto k = uint32_t(context.state.province_definitions.first_sea_province.index()); k < context.state.world.province_size(); ++k) {
3019 dcon::province_id p{ dcon::province_id::value_base_t(k) };
3020 if(world.province_get_connected_region_id(p) != int16_t(max + 1)) {
3021 world.province_set_is_coast(p, false);
3022 world.province_set_port_to(p, dcon::province_id{});
3023 for(auto adj : context.state.world.province_get_province_adjacency(p)) {
3024 auto other = adj.get_connected_provinces(0) != p ? adj.get_connected_provinces(0) : adj.get_connected_provinces(1);
3025 other.set_is_coast(false);
3026 other.set_port_to(dcon::province_id{});
3028 }
3029 }
3030 }
3031 }
3032 }
3033
3034 for(auto ip : context.special_impassible) {
3035 for(auto adj : world.province_get_province_adjacency(ip)) {
3037 }
3038 }
3039
3040 // make ports
3041 province::for_each_land_province(*this, [&](dcon::province_id p) {
3042 for(auto adj : world.province_get_province_adjacency(p)) {
3043 auto other = adj.get_connected_provinces(0) != p ? adj.get_connected_provinces(0) : adj.get_connected_provinces(1);
3044 auto bits = adj.get_type();
3045 if(other && (bits & province::border::coastal_bit) != 0 && (bits & province::border::impassible_bit) == 0) {
3046 world.province_set_port_to(p, other.id);
3047 world.province_set_is_coast(p, true);
3048 return;
3049 }
3050 }
3051 });
3052
3053 // fix worker types
3054 province::for_each_land_province(*this, [&](dcon::province_id p) {
3055 bool is_mine = world.commodity_get_is_mine(world.province_get_rgo(p));
3056 // fix pop types
3057 for(auto pop : world.province_get_pop_location(p)) {
3058 if(is_mine && pop.get_pop().get_poptype() == culture_definitions.farmers) {
3059 pop.get_pop().set_poptype(culture_definitions.laborers);
3060 }
3061 if(!is_mine && pop.get_pop().get_poptype() == culture_definitions.laborers) {
3062 pop.get_pop().set_poptype(culture_definitions.farmers);
3063 }
3064 }
3065 });
3066
3067 bool gov_error = false;
3068 for(auto n : world.in_nation) {
3069 auto g = n.get_government_type();
3070 if(!g && n.get_owned_province_count() != 0) {
3071 auto name = nations::int_to_tag(n.get_identity_from_identity_holder().get_identifying_int());
3072 err.accumulated_errors += name + " exists but has no governmentnt (This will result in a crash)\n";
3073 gov_error = true;
3074 }
3075 }
3076 if(gov_error)
3077 return;
3078
3079 //
3080 // make ui scripts
3081 //
3082 for(auto& s : context.gfx_context.nation_buttons_allow) {
3083 if(s.button_element) {
3084 err.file_name = s.original_file;
3086 ui_defs.gui[s.button_element].data.button.scriptable_enable = make_trigger(s.generator_state, err, t_context);
3087 ui_defs.gui[s.button_element].data.button.flags |= uint16_t(ui::button_scripting::nation);
3088 }
3089 }
3090 for(auto& s : context.gfx_context.nation_buttons_effect) {
3091 if(s.button_element) {
3092 err.file_name = s.original_file;
3094 ui_defs.gui[s.button_element].data.button.scriptable_effect = make_effect(s.generator_state, err, t_context);
3095 ui_defs.gui[s.button_element].data.button.flags |= uint16_t(ui::button_scripting::nation);
3096 }
3097 }
3098 for(auto& s : context.gfx_context.province_buttons_allow) {
3099 if(s.button_element) {
3100 err.file_name = s.original_file;
3101 auto existing_scripting = ui_defs.gui[s.button_element].data.button.get_button_scripting();
3102 if(existing_scripting == ui::button_scripting::nation) {
3103 err.accumulated_errors += std::string("Button ") + std::string(to_string_view(ui_defs.gui[s.button_element].name)) + "in " + err.file_name + " has both province and nation scripting set\n";
3104 } else {
3106 ui_defs.gui[s.button_element].data.button.scriptable_enable = make_trigger(s.generator_state, err, t_context);
3107 ui_defs.gui[s.button_element].data.button.flags |= uint16_t(ui::button_scripting::province);
3108 }
3109 }
3110 }
3111 for(auto& s : context.gfx_context.province_buttons_effect) {
3112 if(s.button_element) {
3113 err.file_name = s.original_file;
3114 auto existing_scripting = ui_defs.gui[s.button_element].data.button.get_button_scripting();
3115 if(existing_scripting == ui::button_scripting::nation) {
3116 err.accumulated_errors += std::string("Button ") + std::string(to_string_view(ui_defs.gui[s.button_element].name)) + "in " + err.file_name + " has both province and nation scripting set\n";
3117 } else {
3119 ui_defs.gui[s.button_element].data.button.scriptable_effect = make_effect(s.generator_state, err, t_context);
3120 ui_defs.gui[s.button_element].data.button.flags |= uint16_t(ui::button_scripting::province);
3121 }
3122 }
3123 }
3124
3125 // Sanity checking navies & armies
3126 for(auto n : world.in_navy) {
3127 auto p = n.get_navy_location().get_location();
3128 if(p.id.index() >= province_definitions.first_sea_province.index()) {
3129 //...
3130 } else { //land province
3131 auto pp = world.province_get_port_to(p);
3132 auto adj = world.get_province_adjacency_by_province_pair(p, pp);
3133 if(!pp || !adj) {
3134 err.accumulated_errors += "Navy defined in " + text::produce_simple_string(*this, p.get_name()) + "; but said province isn't connected to a sea province\n";
3135 }
3136 }
3137 }
3138 for(auto a : world.in_army) {
3139 auto p = a.get_army_location().get_location();
3140 if(p.id.index() >= province_definitions.first_sea_province.index()) {
3141 err.accumulated_errors += "Army defined in " + text::produce_simple_string(*this, p.get_name()) + " which is a sea province\n";
3142 }
3143 }
3144
3145 //sanity flags, but only as a warning
3146 {
3147 auto gfx_dir = open_directory(root, NATIVE("gfx"));
3148 auto flags_dir = open_directory(gfx_dir, NATIVE("flags"));
3149 for(auto nid : world.in_national_identity) {
3150 native_string tag_native = simple_fs::win1250_to_native(nations::int_to_tag(nid.get_identifying_int()));
3151 if(auto f = simple_fs::peek_file(flags_dir, tag_native + NATIVE(".tga")); !f) {
3152 err.accumulated_warnings += "Flag missing " + simple_fs::native_to_utf8(tag_native) + ".tga\n";
3153 }
3154 std::array<bool, size_t(culture::flag_type::count)> has_reported;
3155 std::fill(has_reported.begin(), has_reported.end(), false);
3156 for(auto g : world.in_government_type) {
3157 if(!has_reported[g.get_flag()]) {
3158 native_string file_str = tag_native;
3159 file_str += ogl::flag_type_to_name(*this, culture::flag_type(g.get_flag()));
3160 file_str += NATIVE(".tga");
3161 if(auto f = simple_fs::peek_file(flags_dir, file_str); !f) {
3162 err.accumulated_warnings += "Flag missing " + simple_fs::native_to_utf8(file_str) + "\n";
3163 }
3164 has_reported[g.get_flag()] = true;
3165 }
3166 }
3167 }
3168 }
3169
3170 //Fixup armies defined on a different place
3171 for(auto p : world.in_pop_location) {
3172 for(const auto src : p.get_pop().get_regiment_source()) {
3173 if(src.get_regiment().get_army_from_army_membership().get_controller_from_army_control() == p.get_province().get_nation_from_province_ownership())
3174 continue;
3175 err.accumulated_warnings += "Army defined in " + text::produce_simple_string(*this, p.get_province().get_name()) + "; but regiment comes from a province owned by someone else\n";
3176 if(!src.get_regiment().get_army_from_army_membership().get_is_retreating()
3177 && !src.get_regiment().get_army_from_army_membership().get_navy_from_army_transport()
3178 && !src.get_regiment().get_army_from_army_membership().get_battle_from_army_battle_participation()
3179 && !src.get_regiment().get_army_from_army_membership().get_controller_from_army_rebel_control()) {
3180 auto new_u = world.create_army();
3181 world.army_set_controller_from_army_control(new_u, p.get_province().get_nation_from_province_ownership());
3182 src.get_regiment().set_army_from_army_membership(new_u);
3184 } else {
3185 src.get_regiment().set_strength(0.f);
3186 }
3187 }
3188 }
3189
3191 fill_unsaved_data(); // we need this to run triggers
3192
3193 for(auto n : world.in_nation) {
3194 auto g = n.get_government_type();
3195 auto name = nations::int_to_tag(n.get_identity_from_identity_holder().get_identifying_int());
3196 if(!(n.get_owned_province_count() == 0 || world.government_type_is_valid(g))) {
3197 err.accumulated_errors += "Government for '" + text::produce_simple_string(*this, text::get_name(*this, n)) + "' (" + name + ") is not valid\n";
3198 }
3199 }
3200 for(auto g : world.in_government_type) {
3201 for(auto rt : world.in_rebel_type) {
3202 auto ng = rt.get_government_change(g);
3203 if(!(!ng || uint32_t(ng.id.index()) < world.government_type_size())) {
3204 err.accumulated_errors += "Government change for rebel type '" + text::produce_simple_string(*this, rt.get_name()) + "' with government '" + text::produce_simple_string(*this, g.get_name()) + "' is not valid\n";
3205 }
3206 }
3207 }
3208
3209 // run pending triggers and effects
3210 for(auto pending_decision : pending_decisions) {
3211 dcon::nation_id n = pending_decision.first;
3212 dcon::decision_id d = pending_decision.second;
3213 if(auto e = world.decision_get_effect(d); e)
3214 effect::execute(*this, e, trigger::to_generic(n), trigger::to_generic(n), 0, uint32_t(current_date.value), uint32_t(n.index() << 4 ^ d.index()));
3215 }
3216
3218 economy::initialize(*this);
3219 economy::sanity_check(*this);
3220
3223
3224 economy::sanity_check(*this);
3225
3228
3236 nations::update_military_scores(*this); // depends on ship score, land unit average
3237 nations::update_rankings(*this); // depends on industrial score, military scores
3238
3239 economy::sanity_check(*this);
3240
3241 assert(great_nations.size() == 0);
3242 uint32_t greatpowersfound = 0;
3243 uint32_t i = 0;
3244 while(greatpowersfound < uint32_t(defines.great_nations_count)) {
3245 if(i >= nations_by_rank.size()) {
3246 break;
3247 }
3248 if(nations_by_rank[i] && world.overlord_get_ruler(world.nation_get_overlord_as_subject(nations_by_rank[i])) == dcon::nation_id()) {
3249 great_nations.push_back(great_nation{ sys::date{0}, nations_by_rank[i] });
3250 world.nation_set_is_great_power(nations_by_rank[i], true);
3251 greatpowersfound++;
3252 }
3253 i++;
3254 }
3255
3256 // fix slaves in non-slave owning nations
3257 for(auto p : world.in_province) {
3258 culture::fix_slaves_in_province(*this, p.get_nation_from_province_ownership(), p);
3259 }
3260
3261 economy::sanity_check(*this);
3262
3263 province::for_each_land_province(*this, [&](dcon::province_id p) {
3264 if(auto rgo = world.province_get_rgo(p); !rgo) {
3265 auto name = world.province_get_name(p);
3266 err.accumulated_errors += std::string("province ") + text::produce_simple_string(*this, name) + " is missing an rgo\n";
3267 world.province_set_rgo(p, economy::money);
3268 }
3269 });
3270
3271 economy::sanity_check(*this);
3272
3274
3275 economy::sanity_check(*this);
3276
3277 economy::presimulate(*this);
3278
3279 ai::identify_focuses(*this);
3281 // ai::update_ai_research(*this);
3283 ai::update_focuses(*this);
3284
3285 military::recover_org(*this);
3286
3288}
3289
3290void state::preload() {
3291 adjacency_data_out_of_date = true;
3292 for(auto si : world.in_state_instance) {
3293 si.set_naval_base_is_taken(false);
3294 si.set_capital(dcon::province_id{});
3295 }
3296 for(auto n : world.in_nation) {
3297 n.set_combined_issue_rules(0);
3298 n.set_is_at_war(false);
3299 n.set_allies_count(0);
3300 n.set_vassals_count(0);
3301 n.set_substates_count(0);
3302 n.set_administrative_efficiency(0.0f);
3303 n.set_is_target_of_some_cb(false);
3304 n.set_in_sphere_of(dcon::nation_id{});
3305 n.set_is_player_controlled(false);
3306 n.set_is_great_power(false);
3307 n.set_is_colonial_nation(false);
3308 n.set_has_flash_point_state(false);
3309 n.set_ai_is_threatened(false);
3310 n.set_ai_home_port(dcon::province_id{});
3311 }
3312 for(auto p : world.in_pop) {
3315 p.set_is_primary_or_accepted_culture(false);
3316 }
3317 for(auto p : world.in_province) {
3318 p.set_state_membership(dcon::state_instance_id{});
3319 p.set_is_owner_core(false);
3320 p.set_is_blockaded(false);
3321 }
3322 for(auto m : world.in_movement) {
3323 m.set_pop_support(0.0f);
3324 m.set_radicalism(0.0f);
3325 }
3326 for(auto s : world.in_ship) {
3327 s.set_pending_split(false);
3328 }
3329 for(auto r : world.in_regiment) {
3330 r.set_pending_split(false);
3331 }
3332}
3333
3334void state::on_scenario_load() {
3335 world.pop_type_resize_issues_fns(world.issue_option_size());
3336 world.pop_type_resize_ideology_fns(world.ideology_size());
3337 world.pop_type_resize_promotion_fns(world.pop_type_size());
3338
3339 ui_state.rebel_flags.resize(world.ideology_size(), 0);
3340
3341 if(network_mode != network_mode_type::single_player)
3342 return;
3343
3344 //
3345 // compile functions using llvm when available
3346 //
3347#ifdef USE_LLVM
3348
3349 std::thread dispatch{ [&]() {
3350 jit_environment = std::make_unique<fif::environment>();
3351
3352 int32_t error_count = 0;
3353 std::string error_list;
3354 jit_environment->report_error = [&](std::string_view s) {
3355 console_command_error += std::string("?R ERROR: ") + std::string(s) + "?W\\n";
3356 };
3357 fif::common_fif_environment(*this, *jit_environment);
3358
3359
3360 fif::interpreter_stack values{ };
3361
3362 //AllocConsole();
3363 //freopen("CONOUT$", "w", stdout);
3364 //freopen("CONOUT$", "w", stderr);
3365
3366 for(auto p : world.in_pop_type) {
3367 for(auto i : world.in_issue_option) {
3368 auto mkey = world.pop_type_get_issues(p, i);
3369 std::string base_name = "pi" + std::to_string(p.id.index()) + "_" + std::to_string(i.id.index());
3370 if(mkey) {
3371 std::string fn_str = ": " + base_name + "internal >pop_id dup " + fif_trigger::multiplicative_modifier(*this, mkey) + " drop drop r> ; ";
3372 fn_str += ":export " + base_name + "ext" + " i32 " + base_name + "internal ; ";
3373 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3374 } else {
3375 std::string fn_str = ": " + base_name + "internal" + " drop 0.0 ; ";
3376 fn_str += ":export " + base_name + "ext" + " i32 " + base_name + "internal ; ";
3377 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3378 }
3379 }
3380
3381 for(auto id : world.in_ideology) {
3382 auto mkey = world.pop_type_get_ideology(p, id);
3383 std::string base_name = "pid" + std::to_string(p.id.index()) + "_" + std::to_string(id.id.index());
3384 if(mkey) {
3385 std::string fn_str = ": " + base_name + "internal >pop_id dup " + fif_trigger::multiplicative_modifier(*this, mkey) + " drop drop r> ; ";
3386 fn_str += ":export " + base_name + "ext" + " i32 " + base_name + "internal ; ";
3387 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3388 } else {
3389 std::string fn_str = ": " + base_name + "internal" + " drop 0.0 ; ";
3390 fn_str += ":export " + base_name + "ext" + " i32 " + base_name + "internal ; ";
3391 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3392 }
3393 }
3394
3395 for(auto t : world.in_pop_type) {
3396 auto mkey = world.pop_type_get_promotion(p, t);
3397 std::string base_name = "pp" + std::to_string(p.id.index()) + "_" + std::to_string(t.id.index());
3398 if(mkey) {
3399 std::string fn_str = ": " + base_name + "internal >pop_id dup " + fif_trigger::additive_modifier(*this, mkey) + " drop drop r> ; ";
3400 fn_str += ":export " + base_name + "ext" + " i32 " + base_name + "internal ; ";
3401 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3402 }
3403 }
3404
3405 {
3406 auto mkey = world.pop_type_get_migration_target(p);
3407 std::string base_name = "pmt" + std::to_string(p.id.index());
3408 if(mkey) {
3409 std::string fn_str = ": " + base_name + "internal swap >pop_id swap >province_id " + fif_trigger::multiplicative_modifier(*this, mkey) + " drop drop r> ; ";
3410 fn_str += ":export " + base_name + "ext" + " i32 i32 " + base_name + "internal ; ";
3411 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3412 }
3413 }
3414 {
3415 auto mkey = world.pop_type_get_country_migration_target(p);
3416 std::string base_name = "pcmt" + std::to_string(p.id.index());
3417 if(mkey) {
3418 std::string fn_str = ": " + base_name + "internal swap >pop_id swap >nation_id " + fif_trigger::multiplicative_modifier(*this, mkey) + " drop drop r> ; ";
3419 fn_str += ":export " + base_name + "ext" + " i32 i32 " + base_name + "internal ; ";
3420 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3421 }
3422 }
3423 }
3424 {
3425 std::string fn_str = ": promote_internal >pop_id dup " + fif_trigger::additive_modifier(*this, culture_definitions.promotion_chance) + " drop drop r> ; ";
3426 fn_str += ":export promote_ext i32 promote_internal ; ";
3427 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3428 }
3429 {
3430 std::string fn_str = ": demote_internal >pop_id dup " + fif_trigger::additive_modifier(*this, culture_definitions.demotion_chance) + " drop drop r> ; ";
3431 fn_str += ":export demote_ext i32 demote_internal ; ";
3432 fif::run_fif_interpreter(*jit_environment, fn_str, values);
3433 }
3434
3435 fif::perform_jit(*jit_environment);
3436
3437 //
3438 // load exported fns
3439 //
3440 for(auto p : world.in_pop_type) {
3441 for(auto i : world.in_issue_option) {
3442 std::string name = "pi" + std::to_string(p.id.index()) + "_" + std::to_string(i.id.index()) + "ext";
3443
3444 LLVMOrcExecutorAddress bare_address = 0;
3445 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, name.c_str());
3446
3447 if(error) {
3448 auto msg = LLVMGetErrorMessage(error);
3449#ifdef _WIN32
3450 OutputDebugStringA(msg);
3451 OutputDebugStringA("\n");
3452#endif
3454 } else {
3455 assert(bare_address != 0);
3456 world.pop_type_set_issues_fns(p, i, bare_address);
3457 }
3458 }
3459 for(auto id : world.in_ideology) {
3460 std::string name = "pid" + std::to_string(p.id.index()) + "_" + std::to_string(id.id.index()) + "ext";
3461
3462 LLVMOrcExecutorAddress bare_address = 0;
3463 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, name.c_str());
3464
3465 if(error) {
3466 auto msg = LLVMGetErrorMessage(error);
3467#ifdef _WIN32
3468 OutputDebugStringA(msg);
3469 OutputDebugStringA("\n");
3470#endif
3472 } else {
3473 assert(bare_address != 0);
3474 world.pop_type_set_ideology_fns(p, id, bare_address);
3475 }
3476 }
3477 for(auto t : world.in_pop_type) {
3478 if(world.pop_type_get_promotion(p, t)) {
3479 std::string name = "pp" + std::to_string(p.id.index()) + "_" + std::to_string(t.id.index()) + "ext";
3480
3481 LLVMOrcExecutorAddress bare_address = 0;
3482 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, name.c_str());
3483
3484 if(error) {
3485 auto msg = LLVMGetErrorMessage(error);
3486#ifdef _WIN32
3487 OutputDebugStringA(msg);
3488 OutputDebugStringA("\n");
3489#endif
3491 } else {
3492 assert(bare_address != 0);
3493 world.pop_type_set_promotion_fns(p, t, bare_address);
3494 }
3495 }
3496 }
3497
3498 {
3499 auto mkey = world.pop_type_get_migration_target(p);
3500 if(mkey) {
3501 std::string base_name = "pmt" + std::to_string(p.id.index()) + "ext";
3502 LLVMOrcExecutorAddress bare_address = 0;
3503 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, base_name.c_str());
3504
3505 if(error) {
3506 auto msg = LLVMGetErrorMessage(error);
3507#ifdef _WIN32
3508 OutputDebugStringA(msg);
3509 OutputDebugStringA("\n");
3510#endif
3512 } else {
3513 assert(bare_address != 0);
3514 world.pop_type_set_migration_target_fn(p, bare_address);
3515 }
3516 }
3517 }
3518 {
3519 auto mkey = world.pop_type_get_country_migration_target(p);
3520 if(mkey) {
3521 std::string base_name = "pcmt" + std::to_string(p.id.index()) + "ext";
3522 LLVMOrcExecutorAddress bare_address = 0;
3523 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, base_name.c_str());
3524
3525 if(error) {
3526 auto msg = LLVMGetErrorMessage(error);
3527#ifdef _WIN32
3528 OutputDebugStringA(msg);
3529 OutputDebugStringA("\n");
3530#endif
3532 } else {
3533 assert(bare_address != 0);
3534 world.pop_type_set_country_migration_target_fn(p, bare_address);
3535 }
3536 }
3537 }
3538 }
3539
3540 {
3541 LLVMOrcExecutorAddress bare_address = 0;
3542 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, "promote_ext");
3543
3544 if(error) {
3545 auto msg = LLVMGetErrorMessage(error);
3546#ifdef _WIN32
3547 OutputDebugStringA(msg);
3548 OutputDebugStringA("\n");
3549#endif
3551 } else {
3552 assert(bare_address != 0);
3553 culture_definitions.promotion_chance_fn = bare_address;
3554 }
3555 }
3556 {
3557 LLVMOrcExecutorAddress bare_address = 0;
3558 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, "demote_ext");
3559
3560 if(error) {
3561 auto msg = LLVMGetErrorMessage(error);
3562#ifdef _WIN32
3563 OutputDebugStringA(msg);
3564 OutputDebugStringA("\n");
3565#endif
3567 } else {
3568 assert(bare_address != 0);
3569 culture_definitions.demotion_chance_fn = bare_address;
3570 }
3571 }
3572
3573 //
3574 // set global values
3575 //
3576 {
3577 LLVMOrcExecutorAddress bare_address = 0;
3578 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, "set_container");
3579
3580 if(error) {
3581 auto msg = LLVMGetErrorMessage(error);
3582#ifdef _WIN32
3583 OutputDebugStringA(msg);
3584 OutputDebugStringA("\n");
3585#endif
3587 } else {
3588 assert(bare_address != 0);
3589 using ftype = void(*)(void*);
3590 ftype fn = (ftype)bare_address;
3591 fn(&world);
3592 }
3593 }
3594 {
3595 LLVMOrcExecutorAddress bare_address = 0;
3596 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, "set_vector_storage");
3597
3598 if(error) {
3599 auto msg = LLVMGetErrorMessage(error);
3600#ifdef _WIN32
3601 OutputDebugStringA(msg);
3602 OutputDebugStringA("\n");
3603#endif
3605 } else {
3606 assert(bare_address != 0);
3607 using ftype = void(*)(void*);
3608 ftype fn = (ftype)bare_address;
3609 fn(dcon::shared_backing_storage.allocation);
3610 }
3611 }
3612 {
3613 LLVMOrcExecutorAddress bare_address = 0;
3614 auto error = LLVMOrcLLJITLookup(jit_environment->llvm_jit, &bare_address, "set_state");
3615
3616 if(error) {
3617 auto msg = LLVMGetErrorMessage(error);
3618#ifdef _WIN32
3619 OutputDebugStringA(msg);
3620 OutputDebugStringA("\n");
3621#endif
3623 } else {
3624 assert(bare_address != 0);
3625 using ftype = void(*)(void*);
3626 ftype fn = (ftype)bare_address;
3627 fn(this);
3628 }
3629 }
3630 //
3631 // END set global values
3632 //
3633 } };
3634
3635 dispatch.detach();
3636#endif
3637
3638}
3639
3640void state::fill_unsaved_data() { // reconstructs derived values that are not directly saved after a save has been loaded
3641 great_nations.reserve(int32_t(defines.great_nations_count));
3642
3643 world.nation_resize_modifier_values(sys::national_mod_offsets::count);
3644 world.nation_resize_rgo_goods_output(world.commodity_size());
3645 world.nation_resize_factory_goods_output(world.commodity_size());
3646 world.nation_resize_factory_goods_throughput(world.commodity_size());
3647 world.nation_resize_rgo_size(world.commodity_size());
3648 world.nation_resize_unlocked_commodities(world.commodity_size());
3649 world.nation_resize_rebel_org_modifier(world.rebel_type_size());
3650 world.nation_resize_active_unit(uint32_t(military_definitions.unit_base_definitions.size()));
3651 world.nation_resize_active_crime(uint32_t(culture_definitions.crimes.size()));
3652 world.nation_resize_active_building(world.factory_type_size());
3653 world.nation_resize_unit_stats(uint32_t(military_definitions.unit_base_definitions.size()));
3654 world.nation_resize_max_building_level(economy::max_building_types);
3655
3656 world.province_resize_modifier_values(provincial_mod_offsets::count);
3657
3658 world.nation_resize_demographics(demographics::size(*this));
3659 world.state_instance_resize_demographics(demographics::size(*this));
3660 world.province_resize_demographics(demographics::size(*this));
3661 world.nation_resize_demographics_alt(demographics::size(*this));
3662 world.state_instance_resize_demographics_alt(demographics::size(*this));
3663 world.province_resize_demographics_alt(demographics::size(*this));
3664
3666
3667 world.for_each_nation([&](dcon::nation_id id) { politics::update_displayed_identity(*this, id); });
3668
3669 nations_by_rank.resize(2000); // TODO: take this value directly from the data container: max number of nations
3670 nations_by_industrial_score.resize(2000);
3671 nations_by_military_score.resize(2000);
3672 crisis_attacker_wargoals.resize(2000);
3673 crisis_defender_wargoals.resize(2000);
3674 nations_by_prestige_score.resize(2000);
3675 crisis_participants.resize(2000);
3676
3677 selected_regiments.resize(const_max_selected_units);
3678 selected_ships.resize(const_max_selected_units);
3679
3680 world.for_each_issue([&](dcon::issue_id id) {
3681 for(auto& opt : world.issue_get_options(id)) {
3682 if(opt) {
3683 world.issue_option_set_parent_issue(opt, id);
3684 }
3685 }
3686 });
3687 world.for_each_reform([&](dcon::reform_id id) {
3688 for(auto& opt : world.reform_get_options(id)) {
3689 if(opt) {
3690 world.reform_option_set_parent_reform(opt, id);
3691 }
3692 }
3693 });
3694 for(auto i : culture_definitions.party_issues) {
3695 world.issue_set_issue_type(i, uint8_t(culture::issue_type::party));
3696 }
3697 for(auto i : culture_definitions.military_issues) {
3698 world.reform_set_reform_type(i, uint8_t(culture::issue_type::military));
3699 }
3700 for(auto i : culture_definitions.economic_issues) {
3701 world.reform_set_reform_type(i, uint8_t(culture::issue_type::economic));
3702 }
3703 for(auto i : culture_definitions.social_issues) {
3704 world.issue_set_issue_type(i, uint8_t(culture::issue_type::social));
3705 }
3706 for(auto i : culture_definitions.political_issues) {
3707 world.issue_set_issue_type(i, uint8_t(culture::issue_type::political));
3708 }
3709
3715
3718
3724
3728
3730
3733
3735
3742
3744
3745 //
3746 // clear any pending messages from previously loaded saves
3747 //
3748
3749 new_n_event.~SPSCQueue();
3750 new (&new_n_event) rigtorp::SPSCQueue<event::pending_human_n_event>(1024);
3751 new_f_n_event.~SPSCQueue();
3752 new (&new_f_n_event) rigtorp::SPSCQueue<event::pending_human_f_n_event>(1024);
3753 new_p_event.~SPSCQueue();
3754 new (&new_p_event) rigtorp::SPSCQueue<event::pending_human_p_event>(1024);
3755 new_f_p_event.~SPSCQueue();
3756 new (&new_f_p_event) rigtorp::SPSCQueue<event::pending_human_f_p_event>(1024);
3757
3758 new_requests.~SPSCQueue();
3759 new (&new_requests) rigtorp::SPSCQueue<diplomatic_message::message>(256);
3760
3761
3762 if(local_player_nation) {
3763 world.nation_set_is_player_controlled(local_player_nation, true);
3764 // reshow pending events, messages, etc
3765 for(auto const& e : pending_n_event) {
3766 if(e.n == local_player_nation) {
3767 auto auto_choice = world.national_event_get_auto_choice(e.e);
3768 if(auto_choice == 0)
3769 auto b = new_n_event.try_push(e);
3770 else
3771 command::make_event_choice(*this, e, uint8_t(auto_choice - 1));
3772 }
3773 }
3774 for(auto const& e : pending_f_n_event) {
3775 if(e.n == local_player_nation) {
3776 auto auto_choice = world.free_national_event_get_auto_choice(e.e);
3777 if(auto_choice == 0)
3778 auto b = new_f_n_event.try_push(e);
3779 else
3780 command::make_event_choice(*this, e, uint8_t(auto_choice - 1));
3781 }
3782 }
3783 for(auto const& e : pending_p_event) {
3784 if(world.province_get_nation_from_province_ownership(e.p) == local_player_nation) {
3785 auto auto_choice = world.provincial_event_get_auto_choice(e.e);
3786 if(auto_choice == 0)
3787 auto b = new_p_event.try_push(e);
3788 else
3789 command::make_event_choice(*this, e, uint8_t(auto_choice - 1));
3790 }
3791 }
3792 for(auto const& e : pending_f_p_event) {
3793 if(world.province_get_nation_from_province_ownership(e.p) == local_player_nation) {
3794 auto auto_choice = world.free_provincial_event_get_auto_choice(e.e);
3795 if(auto_choice == 0)
3796 auto b = new_f_p_event.try_push(e);
3797 else
3798 command::make_event_choice(*this, e, uint8_t(auto_choice - 1));
3799 }
3800 }
3801 for(auto const& m : pending_messages) {
3802 if(m.to == local_player_nation && m.type != diplomatic_message::type::none) {
3803 auto b = new_requests.try_push(m);
3804 }
3805 }
3806 }
3807 ui_date = current_date;
3808
3809 //copy current day's data to the alt store
3810
3811
3814
3815 ai::identify_focuses(*this);
3819
3820 military_definitions.pending_blackflag_update = true;
3822
3823
3824
3825
3826#ifndef NDEBUG
3827 for(auto p : world.in_pop) {
3828 float total = 0.0f;
3829 for(auto i : world.in_ideology) {
3830 auto val = pop_demographics::get_demo(*this, p, pop_demographics::to_key(*this, i));
3831 if(0.0 <= val && val <= 1.0f) {
3832 total += val;
3833 } else {
3834 pop_demographics::set_demo(*this, p.id, pop_demographics::to_key(*this, i), 0.0f);
3835 }
3836 }
3837 if(total > 0.0f) {
3838 for(auto i : world.in_ideology) {
3839 auto val = pop_demographics::get_demo(*this, p, pop_demographics::to_key(*this, i));
3840 pop_demographics::set_demo(*this, p.id, pop_demographics::to_key(*this, i), val / total);
3841 }
3842 }
3843 }
3844 military::run_gc(*this);
3845 for(auto a : world.in_army) {
3846 if(a.get_arrival_time() && a.get_arrival_time() <= current_date) {
3847 a.set_arrival_time(current_date + 1);
3848 }
3849 }
3850 for(auto a : world.in_navy) {
3851 if(a.get_arrival_time() && a.get_arrival_time() <= current_date) {
3852 a.set_arrival_time(current_date + 1);
3853 }
3854 }
3855 for(auto shp : world.in_ship) {
3856 assert(shp.get_navy_from_navy_membership());
3857 assert(shp.get_type());
3858 }
3859 std::vector<dcon::ship_id> sin_battle;
3860 for(auto b : world.in_naval_battle) {
3861 for(auto slot : b.get_slots()) {
3864
3865 assert(world.ship_is_valid(slot.ship));
3866 auto it = std::find(sin_battle.begin(), sin_battle.end(), slot.ship);
3867 assert(it == sin_battle.end());
3868 sin_battle.push_back(slot.ship);
3869 }
3870 }
3871 }
3872
3873#endif // ! NDEBUG
3874
3875 game_state_updated.store(true, std::memory_order::release);
3876}
3877
3878void state::single_game_tick() {
3879 // do update logic
3880
3881 current_date += 1;
3882
3883 if(!is_playable_date(current_date, start_date, end_date)) {
3885 game_state_updated.store(true, std::memory_order::release);
3886 return;
3887 }
3888
3889 auto ymd_date = current_date.to_ymd(start_date);
3890
3892
3893 auto month_start = sys::year_month_day{ ymd_date.year, ymd_date.month, uint16_t(1) };
3894 auto next_month_start = ymd_date.month != 12 ? sys::year_month_day{ ymd_date.year, uint16_t(ymd_date.month + 1), uint16_t(1) } : sys::year_month_day{ ymd_date.year + 1, uint16_t(1), uint16_t(1) };
3895 auto const days_in_month = uint32_t(sys::days_difference(month_start, next_month_start));
3896
3897 // pop update:
3898 static demographics::ideology_buffer idbuf(*this);
3899 static demographics::issues_buffer isbuf(*this);
3903 static demographics::migration_buffer cmbuf;
3904 static demographics::migration_buffer imbuf;
3905
3906 // calculate complex changes in parallel where we can, but don't actually apply the results
3907 // instead, the changes are saved to be applied only after all triggers have been evaluated
3908 concurrency::parallel_for(0, 7, [&](int32_t index) {
3909 switch(index) {
3910 case 0:
3911 {
3912 auto o = uint32_t(ymd_date.day);
3913 if(o >= days_in_month)
3914 o -= days_in_month;
3915 demographics::update_ideologies(*this, o, days_in_month, idbuf);
3916 break;
3917 }
3918 case 1:
3919 {
3920 auto o = uint32_t(ymd_date.day + 1);
3921 if(o >= days_in_month)
3922 o -= days_in_month;
3923 demographics::update_issues(*this, o, days_in_month, isbuf);
3924 break;
3925 }
3926 case 2:
3927 {
3928 auto o = uint32_t(ymd_date.day + 6);
3929 if(o >= days_in_month)
3930 o -= days_in_month;
3931 demographics::update_type_changes(*this, o, days_in_month, pbuf);
3932 break;
3933 }
3934 case 3:
3935 {
3936 auto o = uint32_t(ymd_date.day + 7);
3937 if(o >= days_in_month)
3938 o -= days_in_month;
3939 demographics::update_assimilation(*this, o, days_in_month, abuf);
3940 break;
3941 }
3942 case 4:
3943 {
3944 auto o = uint32_t(ymd_date.day + 8);
3945 if(o >= days_in_month)
3946 o -= days_in_month;
3947 demographics::update_internal_migration(*this, o, days_in_month, mbuf);
3948 break;
3949 }
3950 case 5:
3951 {
3952 auto o = uint32_t(ymd_date.day + 9);
3953 if(o >= days_in_month)
3954 o -= days_in_month;
3955 demographics::update_colonial_migration(*this, o, days_in_month, cmbuf);
3956 break;
3957 }
3958 case 6:
3959 {
3960 auto o = uint32_t(ymd_date.day + 10);
3961 if(o >= days_in_month)
3962 o -= days_in_month;
3963 demographics::update_immigration(*this, o, days_in_month, imbuf);
3964 break;
3965 }
3966 default:
3967 break;
3968 }
3969 });
3970
3971 // apply in parallel where we can
3972 concurrency::parallel_for(0, 8, [&](int32_t index) {
3973 switch(index) {
3974 case 0:
3975 {
3976 auto o = uint32_t(ymd_date.day + 0);
3977 if(o >= days_in_month)
3978 o -= days_in_month;
3979 demographics::apply_ideologies(*this, o, days_in_month, idbuf);
3980 break;
3981 }
3982 case 1:
3983 {
3984 auto o = uint32_t(ymd_date.day + 1);
3985 if(o >= days_in_month)
3986 o -= days_in_month;
3987 demographics::apply_issues(*this, o, days_in_month, isbuf);
3988 break;
3989 }
3990 case 2:
3991 {
3992 auto o = uint32_t(ymd_date.day + 2);
3993 if(o >= days_in_month)
3994 o -= days_in_month;
3995 demographics::update_militancy(*this, o, days_in_month);
3996 break;
3997 }
3998 case 3:
3999 {
4000 auto o = uint32_t(ymd_date.day + 3);
4001 if(o >= days_in_month)
4002 o -= days_in_month;
4003 demographics::update_consciousness(*this, o, days_in_month);
4004 break;
4005 }
4006 case 4:
4007 {
4008 auto o = uint32_t(ymd_date.day + 4);
4009 if(o >= days_in_month)
4010 o -= days_in_month;
4011 demographics::update_literacy(*this, o, days_in_month);
4012 break;
4013 }
4014 case 5:
4015 {
4016 auto o = uint32_t(ymd_date.day + 5);
4017 if(o >= days_in_month)
4018 o -= days_in_month;
4019 demographics::update_growth(*this, o, days_in_month);
4020 break;
4021 }
4022 case 6:
4024 [&](auto ids) { world.province_set_daily_net_migration(ids, ve::fp_vector{}); });
4025 break;
4026 case 7:
4028 [&](auto ids) { world.province_set_daily_net_immigration(ids, ve::fp_vector{}); });
4029 break;
4030 default:
4031 break;
4032 }
4033 });
4034
4035 // because they may add pops, these changes must be applied sequentially
4036 {
4037 auto o = uint32_t(ymd_date.day + 6);
4038 if(o >= days_in_month)
4039 o -= days_in_month;
4040 demographics::apply_type_changes(*this, o, days_in_month, pbuf);
4041 }
4042 {
4043 auto o = uint32_t(ymd_date.day + 7);
4044 if(o >= days_in_month)
4045 o -= days_in_month;
4046 demographics::apply_assimilation(*this, o, days_in_month, abuf);
4047 }
4048 {
4049 auto o = uint32_t(ymd_date.day + 8);
4050 if(o >= days_in_month)
4051 o -= days_in_month;
4052 demographics::apply_internal_migration(*this, o, days_in_month, mbuf);
4053 }
4054 {
4055 auto o = uint32_t(ymd_date.day + 9);
4056 if(o >= days_in_month)
4057 o -= days_in_month;
4058 demographics::apply_colonial_migration(*this, o, days_in_month, cmbuf);
4059 }
4060 {
4061 auto o = uint32_t(ymd_date.day + 10);
4062 if(o >= days_in_month)
4063 o -= days_in_month;
4064 demographics::apply_immigration(*this, o, days_in_month, imbuf);
4065 }
4066
4068
4069 // basic repopulation of demographics derived values
4070
4071 int64_t pc_difference = 0;
4072
4073 if(network_mode != network_mode_type::single_player)
4075
4076 //
4077 // ALTERNATE PAR DEMO START POINT A
4078 //
4079
4080 concurrency::parallel_invoke([&]() {
4081 // values updates pass 1 (mostly trivial things, can be done in parallel)
4082 concurrency::parallel_for(0, 17, [&](int32_t index) {
4083 switch(index) {
4084 case 0:
4086 break;
4087 case 1:
4088 // Instant research cheat
4089 for(auto n : this->cheat_data.instant_research_nations) {
4090 auto tech = this->world.nation_get_current_research(n);
4091 if(tech.is_valid()) {
4092 float points = culture::effective_technology_cost(*this, this->current_date.to_ymd(this->start_date).year, n, tech);
4093 this->world.nation_set_research_points(n, points);
4094 }
4095 }
4097 break;
4098 case 2:
4100 break;
4101 case 3:
4103 break;
4104 case 4:
4106 break;
4107 case 5:
4109 break;
4110 case 6:
4112 break;
4113 case 7:
4115 break;
4116 case 8:
4118 break;
4119 case 9:
4121 break;
4122 case 10:
4125 break;
4126 case 11:
4128 break;
4129 case 12:
4131 break;
4132 case 13:
4134 break;
4135 case 14:
4137 break;
4138 case 15:
4140 break;
4141 case 16:
4143 break;
4144 }
4145 });
4146
4147 economy::daily_update(*this, false, 1.f);
4148
4149 //
4150 // ALTERNATE PAR DEMO START POINT B
4151 //
4152
4153 military::recover_org(*this);
4158
4160
4162 military::update_cbs(*this); // may add/remove cbs to a nation
4163
4164 event::update_events(*this);
4165
4166 culture::update_research(*this, uint32_t(ymd_date.year));
4167
4168 nations::update_military_scores(*this); // depends on ship score, land unit average
4169 nations::update_rankings(*this); // depends on industrial score, military scores
4170 nations::update_great_powers(*this); // depends on rankings
4171 nations::update_influence(*this); // depends on rankings, great powers
4172
4175
4176 if(current_date.value % 4 == 0) {
4178 }
4179
4180 if(defines.alice_eval_ai_mil_everyday != 0.0f) {
4181 ai::make_defense(*this);
4182 ai::make_attacks(*this);
4183 ai::update_ships(*this);
4184 }
4185 ai::take_ai_decisions(*this);
4186
4187 // Once per month updates, spread out over the month
4188 switch(ymd_date.day) {
4189 case 1:
4192 break;
4193 case 2:
4196 break;
4197 case 3:
4199 ai::add_gw_goals(*this);
4200 break;
4201 case 4:
4203 if(!bool(defines.alice_eval_ai_mil_everyday)) {
4204 ai::make_defense(*this);
4205 }
4206 break;
4207 case 5:
4210 break;
4211 case 6:
4212 ai::form_alliances(*this);
4213 if(!bool(defines.alice_eval_ai_mil_everyday)) {
4214 ai::make_attacks(*this);
4215 }
4216 break;
4217 case 7:
4219 break;
4220 case 8:
4222 break;
4223 case 9:
4225 break;
4226 case 10:
4228 break;
4229 case 11:
4231 break;
4232 case 12:
4234 rebel::update_armies(*this);
4236 break;
4237 case 13:
4239 break;
4240 case 14:
4241 ai::update_focuses(*this);
4242 break;
4243 case 15:
4245 break;
4246 case 16:
4247 ai::build_ships(*this);
4248 break;
4249 case 17:
4251 break;
4252 case 18:
4254 break;
4255 case 19:
4256 ai::update_budget(*this);
4257 break;
4258 case 20:
4260 if(!bool(defines.alice_eval_ai_mil_everyday)) {
4261 ai::make_defense(*this);
4262 }
4263 break;
4264 case 21:
4266 break;
4267 case 22:
4268 ai::take_reforms(*this);
4269 break;
4270 case 23:
4271 ai::civilize(*this);
4272 ai::make_war_decs(*this);
4273 break;
4274 case 24:
4276 if(!bool(defines.alice_eval_ai_mil_everyday)) {
4277 ai::make_attacks(*this);
4278 }
4279 rebel::update_armies(*this);
4281 break;
4282 case 25:
4284 break;
4285 case 26:
4286 ai::make_peace_offers(*this);
4287 break;
4288 case 27:
4290 break;
4291 case 28:
4293 break;
4294 case 29:
4296 break;
4297 case 30:
4298 if(!bool(defines.alice_eval_ai_mil_everyday)) {
4299 ai::update_ships(*this);
4300 }
4301 rebel::update_armies(*this);
4303 break;
4304 case 31:
4307 break;
4308 default:
4309 break;
4310 }
4311
4313
4314 if(ymd_date.day == 1) {
4315 if(ymd_date.month == 1) {
4316 // yearly update : redo the upper house
4317 for(auto n : world.in_nation) {
4318 if(n.get_owned_province_count() != 0)
4320 }
4321
4325 }
4326 if(ymd_date.month == 2) {
4327 ai::upgrade_colonies(*this);
4328 }
4329 if(ymd_date.month == 3 && !national_definitions.on_quarterly_pulse.empty()) {
4330 for(auto n : world.in_nation) {
4331 if(n.get_owned_province_count() > 0) {
4332 event::fire_fixed_event(*this, national_definitions.on_quarterly_pulse, trigger::to_generic(n.id), event::slot_type::nation, n.id, -1, event::slot_type::none);
4333 }
4334 }
4335 }
4336 if(ymd_date.month == 4 && ymd_date.year % 2 == 0) { // the purge
4338 }
4339 if(ymd_date.month == 5) {
4340 ai::prune_alliances(*this);
4342 }
4343 if(ymd_date.month == 6 && !national_definitions.on_quarterly_pulse.empty()) {
4344 for(auto n : world.in_nation) {
4345 if(n.get_owned_province_count() > 0) {
4346 event::fire_fixed_event(*this, national_definitions.on_quarterly_pulse, trigger::to_generic(n.id), event::slot_type::nation, n.id, -1, event::slot_type::none);
4347 }
4348 }
4349 }
4350 if(ymd_date.month == 7) {
4353 }
4354 if(ymd_date.month == 9 && !national_definitions.on_quarterly_pulse.empty()) {
4355 for(auto n : world.in_nation) {
4356 if(n.get_owned_province_count() > 0) {
4357 event::fire_fixed_event(*this, national_definitions.on_quarterly_pulse, trigger::to_generic(n.id), event::slot_type::nation, n.id, -1, event::slot_type::none);
4358 }
4359 }
4360 }
4361 if(ymd_date.month == 10 && !national_definitions.on_yearly_pulse.empty()) {
4362 for(auto n : world.in_nation) {
4363 if(n.get_owned_province_count() > 0) {
4364 event::fire_fixed_event(*this, national_definitions.on_yearly_pulse, trigger::to_generic(n.id), event::slot_type::nation, n.id, -1, event::slot_type::none);
4365 }
4366 }
4367 }
4368 if(ymd_date.month == 11) {
4369 ai::prune_alliances(*this);
4370 }
4371 if(ymd_date.month == 12 && !national_definitions.on_quarterly_pulse.empty()) {
4372 for(auto n : world.in_nation) {
4373 if(n.get_owned_province_count() > 0) {
4374 event::fire_fixed_event(*this, national_definitions.on_quarterly_pulse, trigger::to_generic(n.id), event::slot_type::nation, n.id, -1, event::slot_type::none);
4375 }
4376 }
4377 }
4378 }
4379
4381
4382 military::run_gc(*this);
4383 nations::run_gc(*this);
4385 ai::daily_cleanup(*this);
4386
4390
4391 },
4392 [&]() {
4393 if(network_mode == network_mode_type::single_player)
4395 }
4396 );
4397
4398 if(network_mode == network_mode_type::single_player) {
4399 world.nation_swap_demographics_demographics_alt();
4400 world.state_instance_swap_demographics_demographics_alt();
4401 world.province_swap_demographics_demographics_alt();
4402
4404 }
4405
4406 /*
4407 * END OF DAY: update cached data
4408 */
4409
4410 player_data_cache.treasury_record[current_date.value % 32] = nations::get_treasury(*this, local_player_nation);
4411 player_data_cache.population_record[current_date.value % 32] = world.nation_get_demographics(local_player_nation, demographics::total);
4412 if((current_date.value % 16) == 0) {
4413 auto index = economy::most_recent_price_record_index(*this);
4414 for(auto c : world.in_commodity) {
4415 c.set_price_record(index, economy::price(*this, c));
4416 }
4417 }
4418
4419 if(((ymd_date.month % 3) == 0) && (ymd_date.day == 1)) {
4420 auto index = economy::most_recent_gdp_record_index(*this);
4421 for(auto n : world.in_nation) {
4422 n.set_gdp_record(index, economy::gdp_adjusted(*this, n));
4423 }
4424 }
4425
4426 ui_date = current_date;
4427
4428 game_state_updated.store(true, std::memory_order::release);
4429
4430 switch(user_settings.autosaves) {
4431 case autosave_frequency::none:
4432 break;
4433 case autosave_frequency::daily:
4435 break;
4436 case autosave_frequency::monthly:
4437 if(ymd_date.day == 1)
4439 break;
4440 case autosave_frequency::yearly:
4441 if(ymd_date.month == 1 && ymd_date.day == 1)
4443 break;
4444 default:
4445 break;
4446 }
4447}
4448
4449sys::checksum_key state::get_save_checksum() {
4450 dcon::load_record loaded = world.make_serialize_record_store_save();
4451 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[world.serialize_size(loaded)]);
4452 std::byte* start = reinterpret_cast<std::byte*>(buffer.get());
4453 world.serialize(start, loaded);
4454
4455 auto buffer_position = reinterpret_cast<uint8_t*>(start);
4456 int32_t total_size_used = static_cast<int32_t>(buffer_position - buffer.get());
4457
4458 checksum_key key;
4459 blake2b(&key, sizeof(key), buffer.get(), total_size_used, nullptr, 0);
4460 return key;
4461}
4462
4463void state::debug_save_oos_dump() {
4465 {
4466 // save for further inspection
4467 dcon::load_record loaded = world.make_serialize_record_store_save();
4468 auto save_buffer = std::unique_ptr<uint8_t[]>(new uint8_t[world.serialize_size(loaded)]);
4469 auto buffer_position = reinterpret_cast<std::byte*>(save_buffer.get());
4470 world.serialize(buffer_position, loaded);
4471 size_t total_size_used = reinterpret_cast<uint8_t*>(buffer_position) - save_buffer.get();
4472 simple_fs::write_file(sdir, NATIVE("save.bin"), reinterpret_cast<const char*>(save_buffer.get()), uint32_t(total_size_used));
4473 }
4474 {
4475 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[sys::sizeof_save_section(*this)]);
4476 auto buffer_position = sys::write_save_section(buffer.get(), *this);
4477 size_t total_size_used = reinterpret_cast<uint8_t*>(buffer_position) - buffer.get();
4478 simple_fs::write_file(sdir, NATIVE("all_save.bin"), reinterpret_cast<const char*>(buffer.get()), uint32_t(total_size_used));
4479 }
4480}
4481
4482void state::debug_scenario_oos_dump() {
4484 {
4485 // save for further inspection
4486 dcon::load_record loaded = world.make_serialize_record_store_scenario();
4487 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[world.serialize_size(loaded)]);
4488 auto buffer_position = reinterpret_cast<std::byte*>(buffer.get());
4489 world.serialize(buffer_position, loaded);
4490 size_t total_size_used = reinterpret_cast<uint8_t*>(buffer_position) - buffer.get();
4491 simple_fs::write_file(sdir, NATIVE("scen.bin"), reinterpret_cast<char*>(buffer.get()), uint32_t(total_size_used));
4492 }
4493 {
4494 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[sys::sizeof_scenario_section(*this).total_size]);
4495 auto buffer_position = sys::write_scenario_section(buffer.get(), *this);
4496 size_t total_size_used = reinterpret_cast<uint8_t*>(buffer_position) - buffer.get();
4497 simple_fs::write_file(sdir, NATIVE("all_scen.bin"), reinterpret_cast<char*>(buffer.get()), uint32_t(total_size_used));
4498 }
4499}
4500
4501void state::game_loop() {
4502 static int32_t game_speed[] = {
4503 0, // speed 0
4504 2000, // speed 1 -- 2 seconds
4505 750, // speed 2 -- 0.75 seconds
4506 250, // speed 3 -- 0.25 seconds
4507 125, // speed 4 -- 0.125 seconds
4508 };
4509 game_speed[1] = int32_t(defines.alice_speed_1);
4510 game_speed[2] = int32_t(defines.alice_speed_2);
4511 game_speed[3] = int32_t(defines.alice_speed_3);
4512 game_speed[4] = int32_t(defines.alice_speed_4);
4513
4514 while(quit_signaled.load(std::memory_order::acquire) == false) {
4516 {
4517 std::lock_guard l{ ugly_ui_game_interaction_hack };
4519 }
4520 if(network_mode == sys::network_mode_type::client) {
4521 std::this_thread::sleep_for(std::chrono::milliseconds(15));
4522 } else {
4523 auto speed = actual_game_speed.load(std::memory_order::acquire);
4524 auto upause = ui_pause.load(std::memory_order::acquire);
4525 if(network_mode != sys::network_mode_type::host) { // prevent host from pausing the game with open event windows
4526 upause = upause || ui::events_pause_test(*this);
4527 }
4528
4529 if(speed <= 0 || upause || internally_paused || current_scene.enforced_pause) {
4530 std::this_thread::sleep_for(std::chrono::milliseconds(15));
4531 } else {
4532 auto entry_time = std::chrono::steady_clock::now();
4533 auto ms_count = std::chrono::duration_cast<std::chrono::milliseconds>(entry_time - last_update).count();
4534 if(speed >= 5 || ms_count >= game_speed[speed]) { /*enough time has passed*/
4535 last_update = entry_time;
4536 if(network_mode == sys::network_mode_type::host) {
4537 command::advance_tick(*this, local_player_nation);
4538 } else {
4539 std::lock_guard l{ ugly_ui_game_interaction_hack };
4540 single_game_tick();
4541 }
4542 } else {
4543 std::this_thread::sleep_for(std::chrono::milliseconds(1));
4544 }
4545 }
4546 }
4547 }
4548}
4549
4550void state::console_log(std::string_view message) {
4551 current_scene.console_log(*this, message);
4552}
4553
4554void state::new_army_group(dcon::province_id hq) {
4555 bool invalid_province = false;
4556 world.for_each_automated_army_group(
4557 [&](dcon::automated_army_group_id item) {
4558 auto item_hq = world.automated_army_group_get_hq(item);
4559 if(item_hq == hq) {
4560 invalid_province = true;
4561 }
4562 }
4563 );
4564 if(invalid_province) {
4565 return;
4566 }
4567 auto new_group = world.create_automated_army_group();
4568 world.automated_army_group_set_hq(new_group, hq);
4569 world.automated_army_group_set_owner(new_group, local_player_nation);
4570
4571 game_state_updated.store(true, std::memory_order_release);
4572}
4573
4574void state::toggle_defensive_position(dcon::automated_army_group_id group, dcon::province_id position) {
4575 auto fat_group = fatten(world, group);
4576
4577 if(fat_group.get_provinces_defend().contains(position)) {
4578 fat_group.get_provinces_defend().remove_unique(position);
4579 } else {
4580 fat_group.get_provinces_defend().push_back(position);
4581 }
4582
4583 game_state_updated.store(true, std::memory_order_release);
4584 map_state.unhandled_province_selection = true;
4585}
4586
4587void state::toggle_enforce_control_position(dcon::automated_army_group_id group, dcon::province_id position) {
4588 auto fat_group = fatten(world, group);
4589
4590 if(fat_group.get_provinces_enforce_control().contains(position)) {
4591 fat_group.get_provinces_enforce_control().remove_unique(position);
4592 } else {
4593 fat_group.get_provinces_enforce_control().push_back(position);
4594 }
4595
4596 game_state_updated.store(true, std::memory_order_release);
4597 map_state.unhandled_province_selection = true;
4598}
4599
4600void state::toggle_designated_port(dcon::automated_army_group_id group, dcon::province_id position) {
4601 auto fat_group = fatten(world, group);
4602
4603 if(!world.province_get_is_coast(position)) {
4604 return;
4605 }
4606
4607 if(fat_group.get_provinces_ferry_origin().contains(position)) {
4608 fat_group.get_provinces_ferry_origin().remove_unique(position);
4609 } else {
4610 fat_group.get_provinces_ferry_origin().push_back(position);
4611 }
4612
4613 game_state_updated.store(true, std::memory_order_release);
4614 map_state.unhandled_province_selection = true;
4615}
4616
4617void state::army_group_add_regiment(dcon::automated_army_group_id group, dcon::regiment_id id) {
4618
4619 auto automation_check = world.regiment_get_automation(id);
4620 if(automation_check) {
4621 return;
4622 }
4623
4624 auto automation_data = world.create_regiment_automation_data();
4625
4626 {
4627 std::lock_guard l{ ugly_ui_game_interaction_hack };
4628 world.try_create_automated_army_group_membership_regiment(automation_data, group);
4629 world.try_create_automation(automation_data, id);
4630 }
4631
4632 auto fat_automation = fatten(world, automation_data);
4633
4634 auto army = world.regiment_get_army_from_army_membership(id);
4635 auto location = world.army_get_location_from_army_location(army);
4636
4637 fat_automation.set_status(army_group_regiment_status::standby);
4638 fat_automation.set_task(army_group_regiment_task::idle);
4639 fat_automation.set_target(location);
4640 fat_automation.set_ferry_origin({ });
4641 fat_automation.set_ferry_target({ });
4642 fat_automation.set_await_command_execution_flag(false);
4643
4644 // split it right away
4645 std::array<dcon::regiment_id, command::num_packed_units> data;
4646 int32_t i = 0;
4647 data.fill(dcon::regiment_id{});
4648 data[0] = id;
4649 command::mark_regiments_to_split(*this, local_player_nation, data);
4650 command::split_army(*this, local_player_nation, army);
4651
4652 game_state_updated.store(true, std::memory_order_release);
4653}
4654
4655void state::remove_navy_from_army_group(dcon::automated_army_group_id selected_group, dcon::navy_id navy_to_delete) {
4656 std::lock_guard l{ ugly_ui_game_interaction_hack };
4657
4658 auto membership = world.navy_get_automated_army_group_membership_navy(navy_to_delete);
4659 if(!membership) {
4660 return;
4661 }
4662
4663 world.delete_automated_army_group_membership_navy(membership);
4664}
4665
4666void state::remove_regiment_from_army_group(dcon::automated_army_group_id selected_group, dcon::regiment_id regiment_to_delete) {
4667 std::lock_guard l{ ugly_ui_game_interaction_hack };
4668
4669 auto regiment_automation_relation = world.regiment_get_automation(regiment_to_delete);
4670 auto automation_data = world.automation_get_automation_data(regiment_automation_relation);
4671
4672 if(!automation_data) {
4673 return;
4674 }
4675
4676 world.delete_regiment_automation_data(automation_data);
4677}
4678
4679void state::remove_regiment_from_all_army_groups(dcon::regiment_id regiment_to_delete) {
4680 world.for_each_automated_army_group([&](dcon::automated_army_group_id item) {
4681 remove_regiment_from_army_group(item, regiment_to_delete);
4682 });
4683}
4684
4685void state::remove_army_army_group_clean(dcon::automated_army_group_id group, dcon::army_id army_to_delete) {
4686 for(auto regiment_membership : world.army_get_army_membership(army_to_delete)) {
4687 remove_regiment_from_army_group(group, regiment_membership.get_regiment().id);
4688 }
4689}
4690
4691void state::add_army_to_army_group(dcon::automated_army_group_id selected_group, dcon::army_id selected_army) {
4692 for(auto item : world.army_get_army_membership(selected_army)) {
4693 army_group_add_regiment(selected_group, item.get_regiment());
4694 }
4695}
4696
4697void state::add_navy_to_army_group(dcon::automated_army_group_id selected_group, dcon::navy_id selected_navy) {
4698 auto automation_link = world.navy_get_automated_army_group_membership_navy(selected_navy);
4699 auto current_group = world.automated_army_group_membership_navy_get_army(automation_link);
4700
4701 if(current_group) {
4702 return;
4703 }
4704
4705 {
4706 std::lock_guard l{ ugly_ui_game_interaction_hack };
4707 auto new_link = world.try_create_automated_army_group_membership_navy(selected_navy, selected_group);
4708 }
4709
4710 game_state_updated.store(true, std::memory_order_release);
4711}
4712
4713void state::delete_army_group(dcon::automated_army_group_id group) {
4714 static std::vector<dcon::regiment_automation_data_id> to_delete = {};
4715 to_delete.clear();
4716
4717 world.automated_army_group_for_each_automated_army_group_membership_regiment(group, [&](dcon::automated_army_group_membership_regiment_id item) {
4718 auto regiment = world.automated_army_group_membership_regiment_get_regiment(item);
4719 to_delete.push_back(regiment);
4720 });
4721
4722 std::lock_guard l{ ugly_ui_game_interaction_hack };
4723
4724 if(!to_delete.empty()) {
4725 for(auto& regiment : to_delete) {
4726 world.delete_regiment_automation_data(regiment);
4727 }
4728 }
4729
4730 world.delete_automated_army_group(group);
4731 game_state_updated.store(true, std::memory_order_release);
4732}
4733
4734void state::update_armies_and_fleets(dcon::automated_army_group_id group) {
4735 auto owner = world.automated_army_group_get_owner(group);
4736
4737 if(owner == local_player_nation) {
4738 // clear up dead regiments
4739 static std::vector<dcon::regiment_automation_data_id> to_delete = {};
4740 to_delete.clear();
4741
4742 world.automated_army_group_for_each_automated_army_group_membership_regiment(group, [&](dcon::automated_army_group_membership_regiment_id item) {
4743 auto regiment = world.automated_army_group_membership_regiment_get_regiment(item);
4744 auto regiment_true = world.regiment_automation_data_get_regiment_from_automation(regiment);
4745 if(!regiment_true) {
4746 to_delete.push_back(regiment);
4747 }
4748 });
4749
4750 if(!to_delete.empty()) {
4751 std::lock_guard l{ ugly_ui_game_interaction_hack };
4752 for(auto& regiment : to_delete) {
4753 world.delete_regiment_automation_data(regiment);
4754 }
4755 }
4756 } else {
4757 // clear up army groups you don't own
4758 delete_army_group(group);
4759 }
4760}
4761
4762void state::smart_select_army_group(dcon::automated_army_group_id selected_group) {
4763 if(!selected_army_group) {
4764 select_army_group(selected_group);
4765 return;
4766 }
4767
4768 if(selected_army_group == selected_group) {
4769 deselect_army_group();
4770 return;
4771 }
4772
4773 select_army_group(selected_group);
4774}
4775
4776void state::select_army_group(dcon::automated_army_group_id selected_group) {
4777 selected_army_group = selected_group;
4778
4779 game_state_updated.store(true, std::memory_order_release);
4780}
4781
4782void state::deselect_army_group() {
4783 selected_army_group = {};
4784
4785 game_state_updated.store(true, std::memory_order_release);
4786}
4787
4788dcon::regiment_automation_data_id state::fill_province_up_to_supply_limit(
4789 dcon::automated_army_group_id group_id,
4790 dcon::province_id target,
4791 std::vector<float>& regiments_distribution,
4792 float overestimate_supply_limit,
4793 bool ignore_enemy_regiments_in_supply_calculations
4794) {
4795 auto group = fatten(world, group_id);
4796
4797 static std::vector<float> regiments_expectation_ideal;
4798 regiments_expectation_ideal.resize(military_definitions.unit_base_definitions.size() + 2);
4799
4800 for(uint32_t i = 0; i < military_definitions.unit_base_definitions.size(); ++i) {
4801 regiments_expectation_ideal[i] = 0.f;
4802 }
4803
4804 //count current available supply:
4805 float supply_limit = float(military::supply_limit_in_province(
4806 *this,
4807 local_player_nation,
4808 target
4809 )) * overestimate_supply_limit;
4810
4811 float current_weight = military::local_army_weight_max(
4812 *this,
4813 target
4814 );
4815
4816 if(ignore_enemy_regiments_in_supply_calculations) {
4817 current_weight -= military::local_enemy_army_weight_max(*this, target, local_player_nation);
4818 }
4819
4820 //regiments moving there
4821 for(auto regiment_id : group.get_automated_army_group_membership_regiment()) {
4822 auto regiment = dcon::fatten(world, regiment_id).get_regiment();
4823 if(regiment.get_target() == target && regiment.get_status() == army_group_regiment_status::move_to_target) {
4824 current_weight += 3.f;
4825 } else if(regiment.get_ferry_target() == target && regiment.get_status() == army_group_regiment_status::move_to_port) {
4826 current_weight += 3.f;
4827 }
4828 }
4829
4830 // calculate ideal regiment count
4831 float ideal = 0.f;
4832 for(uint32_t i = 0; i < military_definitions.unit_base_definitions.size(); ++i) {
4833 regiments_expectation_ideal[i] = floor(regiments_distribution[i] * floor(supply_limit / 3.f)) * 3.f;
4834 ideal += regiments_expectation_ideal[i];
4835 }
4836
4837 if(current_weight + 3.f < ideal) {
4838 return fill_province(group, target, regiments_expectation_ideal);
4839 }
4840
4841 return {};
4842}
4843
4844float state::army_group_available_supply(dcon::automated_army_group_id group, dcon::province_id province) {
4845 float max_supply = float(military::supply_limit_in_province(*this, local_player_nation, province));
4846 float current_weight = 0.f;
4847 for(auto regiment_id : world.automated_army_group_get_automated_army_group_membership_regiment(group)) {
4848 auto regiment = dcon::fatten(world, regiment_id).get_regiment();
4849 if(regiment.get_target() == province) {
4850 current_weight += 3.f;
4851 } else if(
4852 regiment.get_ferry_target() == province
4853 && (
4854 regiment.get_status() == army_group_regiment_status::move_to_port
4855 || regiment.get_status() == army_group_regiment_status::await_transport
4856 || regiment.get_status() == army_group_regiment_status::is_transported
4857 || regiment.get_status() == army_group_regiment_status::disembark
4858 )
4859 ) {
4860 current_weight += 3.f;
4861 } else if(
4862 regiment.get_ferry_origin() == province
4863 && (
4864 regiment.get_status() == army_group_regiment_status::move_to_port
4865 || regiment.get_status() == army_group_regiment_status::await_transport
4866 )
4867 ) {
4868 current_weight += 3.f;
4869 }
4870 }
4871
4872 return max_supply - current_weight;
4873}
4874
4875void state::regiment_reset_order(dcon::regiment_automation_data_id regiment) {
4876 auto fat_data = fatten(world, regiment);
4877
4878 fat_data.set_status(army_group_regiment_status::standby);
4879 fat_data.set_task(army_group_regiment_task::idle);
4880}
4881
4882dcon::province_id state::find_available_ferry_origin(dcon::automated_army_group_id group, dcon::regiment_automation_data_id regiment) {
4883 auto fat_reg = fatten(world, regiment);
4884 auto army = fat_reg.get_regiment_from_automation().get_army_from_army_membership();
4885 auto fat_group = fatten(world, group);
4886
4887 for(auto& item : fat_group.get_provinces_ferry_origin()) {
4888 // check available supply first:
4889 if(army_group_available_supply(group, item) < 3.f) {
4890 continue;
4891 }
4892 auto path = command::can_move_army(*this, local_player_nation, army, item);
4893 if(!path.empty()) {
4894 return item;
4895 }
4896 }
4897
4898 if(world.province_get_is_coast(army.get_location_from_army_location())) {
4899 return army.get_location_from_army_location();
4900 }
4901
4902 // flood fill until available port is found
4903 static std::vector<dcon::province_id> origin_port_candidates;
4904 origin_port_candidates.clear();
4905 size_t l = 0;
4906 size_t r = 1;
4907 origin_port_candidates.push_back(army.get_location_from_army_location());
4908
4909 while(l < r && l < 30) {
4910 auto current_location = origin_port_candidates[l];
4911
4912 if(world.province_get_is_coast(current_location)) {
4913 return current_location;
4914 }
4915
4916 if(current_location.value >= province_definitions.first_sea_province.value) {
4917 l += 1;
4918 continue;
4919 }
4920 auto path = command::can_move_army(*this, local_player_nation, army, current_location);
4921 if(path.empty()) {
4922 l += 1;
4923 continue;
4924 }
4925
4926 for(auto adj : world.province_get_province_adjacency(current_location)) {
4927 auto other = adj.get_connected_provinces(adj.get_connected_provinces(0) == current_location ? 1 : 0);
4928
4929 if(std::find(origin_port_candidates.begin(), origin_port_candidates.end(), other) == origin_port_candidates.end()) {
4930 origin_port_candidates.push_back(other);
4931 r += 1;
4932 }
4933 }
4934
4935 l += 1;
4936 }
4937
4938
4939 dcon::province_id invalid_province{};
4940 return invalid_province;
4941}
4942
4943bool state::move_to_available_port(dcon::automated_army_group_id group, dcon::regiment_automation_data_id regiment) {
4944 auto fat_reg = fatten(world, regiment);
4945 dcon::province_id target = find_available_ferry_origin(group, regiment);
4946 auto army = fat_reg.get_regiment_from_automation().get_army_from_army_membership();
4947
4948 if(target) {
4949 if(army.get_location_from_army_location() != target) {
4950 command::move_army(*this, local_player_nation, army, target, false);
4951 fat_reg.set_await_command_execution_flag(true);
4952 }
4953 fat_reg.set_status(army_group_regiment_status::move_to_port);
4954 fat_reg.set_ferry_origin(target);
4955 return true;
4956 }
4957
4958 return false;
4959}
4960
4961bool state::army_group_recalculate_distribution(dcon::automated_army_group_id group, std::vector<float>& regiments_distribution) {
4962 for(uint32_t i = 0; i < military_definitions.unit_base_definitions.size(); ++i) {
4963 regiments_distribution[i] = 0.f;
4964 }
4965
4966 //recalculate distribution
4967 float total = 0.f;
4968 for(auto regiment_id : world.automated_army_group_get_automated_army_group_membership_regiment(group)) {
4969 auto regiment = dcon::fatten(world, regiment_id).get_regiment();
4970
4971 auto regiment_type = regiment.get_regiment_from_automation().get_type();
4972
4973 if(!regiment_type) {
4974 continue;
4975 }
4976
4977 auto task = regiment.get_task();
4978 auto status = regiment.get_status();
4979 if(
4980 task == army_group_regiment_task::idle ||
4981 task == army_group_regiment_task::gather_at_hq
4982 ) {
4983 regiments_distribution[regiment_type.index()] += 1.f;
4984 total += 1.f;
4985 }
4986 }
4987
4988 if(total > 0.5f) {
4989 for(uint32_t i = 0; i < military_definitions.unit_base_definitions.size(); ++i) {
4990 regiments_distribution[i] = regiments_distribution[i] / total;
4991 }
4992 return true;
4993 }
4994 return false;
4995}
4996
4997void state::army_group_update_tasks(dcon::automated_army_group_id group) {
4998 // before update:
4999
5000 // look for ferry targets:
5001 dcon::province_id potential_ferry_target{};
5002 float ferry_supply_budget = 0.f;
5003
5004 auto fat_group = fatten(world, group);
5005
5006 for(auto regiment_id : world.automated_army_group_get_automated_army_group_membership_regiment(group)) {
5007 auto regiment = dcon::fatten(world, regiment_id).get_regiment();
5008
5009 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5010 // do not update armies on the move, they are busy with current orders
5011 auto current_path = world.army_get_path(army);
5012 if(current_path.size() > 0) {
5013 continue;
5014 }
5015
5016 auto province = world.army_get_location_from_army_location(army);
5017 auto controller = world.province_get_nation_from_province_control(province);
5018
5019 if(regiment.get_status() != army_group_regiment_status::standby) {
5020 continue;
5021 }
5022
5023 switch(regiment.get_task()) {
5024 case army_group_regiment_task::idle:
5025 break;
5026 case army_group_regiment_task::gather_at_hq:
5027 break;
5028 case army_group_regiment_task::defend_position:
5029 if(!fat_group.get_provinces_defend().contains(regiment.get_target())) {
5030 regiment_reset_order(regiment);
5031 }
5032 break;
5033 case army_group_regiment_task::siege:
5034 if(regiment.get_target() != province) {
5035 continue;
5036 }
5037 if(military::siege_potential(*this, local_player_nation, controller)) {
5038 continue;
5039 }
5040 regiment_reset_order(regiment);
5041 break;
5042 default:
5043 break;
5044 }
5045 }
5046}
5047
5048dcon::province_id state::get_port_for_landing(dcon::automated_army_group_id group, dcon::province_id target) {
5049 auto fat_group = fatten(world, group);
5050
5051 if(world.province_get_is_coast(target)) {
5052 return target;
5053 }
5054
5055 dcon::province_id potential_target_port{};
5056 for(auto& target_port : fat_group.get_provinces_ferry_origin()) {
5057 auto path = province::make_safe_land_path(*this, target_port, target, local_player_nation);
5058 if(path.size() > 0) {
5059 potential_target_port = target_port;
5060 }
5061 }
5062
5063 return potential_target_port;
5064}
5065
5066void state::army_group_distribute_tasks(dcon::automated_army_group_id group) {
5067 static std::vector<dcon::province_id> province_queue;
5068 static std::vector<dcon::province_id> provinces_to_reduce_weight;
5069 static std::vector<dcon::province_id> provinces_to_maintain;
5070 static std::vector<float> regiments_distribution;
5071 regiments_distribution.resize(military_definitions.unit_base_definitions.size() + 2);
5072
5073 auto fat_group = fatten(world, group);
5074
5075
5076 // handle "defence line" orders
5077 {
5078 if(army_group_recalculate_distribution(group, regiments_distribution)) {
5079 // find empty defensive position
5080 dcon::province_id candidate{};
5081 float supply_limit = 0.f;
5082 for(dcon::province_id defensive_position : fat_group.get_provinces_defend()) {
5083 auto regiment = fill_province_up_to_supply_limit(
5084 group,
5085 defensive_position,
5086 regiments_distribution,
5087 1.f, true
5088 );
5089
5090 if(regiment) {
5091 world.regiment_automation_data_set_task(regiment, army_group_regiment_task::defend_position);
5092 break;
5093 }
5094 }
5095 }
5096 }
5097
5098 // siege whatever you can siege
5099 {
5100 if(army_group_recalculate_distribution(group, regiments_distribution)) {
5101 for(dcon::province_id province_to_siege : fat_group.get_provinces_enforce_control()) {
5102 auto controller = world.province_get_nation_from_province_control(province_to_siege);
5103
5104 if(!military::siege_potential(*this, local_player_nation, controller)) {
5105 continue;
5106 }
5107 auto regiment = fill_province_up_to_supply_limit(
5108 group,
5109 province_to_siege,
5110 regiments_distribution,
5111 3.f, true
5112 );
5113
5114 if(regiment) {
5115 world.regiment_automation_data_set_task(regiment, army_group_regiment_task::siege);
5116 break;
5117 }
5118 }
5119 }
5120 }
5121
5122 // handle naval travels
5123
5124 // send orders to fleets:
5125
5126 for(auto fleet_membership : fat_group.get_automated_army_group_membership_navy()) {
5127 auto fleet = fleet_membership.get_navy();
5128 auto location = fleet.get_location_from_navy_location();
5129
5130 auto path = fleet.get_path();
5131 //fleet is moving, no need to send commands
5132 if(path.size() > 0) {
5133 continue;
5134 }
5135
5136 auto transported_armies = fleet.get_army_transport();
5137 bool wait_for_disembark = false;
5138 for(auto item : transported_armies) {
5139 auto army = item.get_army();
5140 auto path_army = world.army_get_path(army);
5141 //transported army is busy, so it attempts to disembark
5142 if(path_army.size() > 0) {
5143 wait_for_disembark = true;
5144 continue;
5145 }
5146 }
5147 if(wait_for_disembark) {
5148 continue;
5149 }
5150
5151 // we are not waiting
5152
5153 if(military::free_transport_capacity(*this, fleet) < military::transport_capacity(*this, fleet)) {
5154 // if we have some passengers, move to one of their targets:
5155 for(auto regiment_automation_link : fat_group.get_automated_army_group_membership_regiment()) {
5156 auto regiment = regiment_automation_link.get_regiment();
5157 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5158 auto target = regiment.get_ferry_target();
5159 auto port_to = world.province_get_port_to(target);
5160 auto transport = world.army_get_army_transport(army);
5161 auto regiment_fleet = world.army_transport_get_navy(transport);
5162 auto fleet_location = world.navy_get_location_from_navy_location(fleet);
5163
5164 if(regiment_fleet == fleet) {
5165 // allow regiment to disembark
5166 if(port_to == fleet_location) {
5167 break;
5168 }
5169
5170 if(command::can_move_navy(*this, local_player_nation, fleet, port_to).size() > 0) {
5171 command::move_navy(*this, local_player_nation, fleet, port_to, false);
5172 break;
5173 }
5174 }
5175 }
5176 } else {
5177 // try to get new passengers:
5178 for(auto regiment_automation_link : fat_group.get_automated_army_group_membership_regiment()) {
5179 auto regiment = regiment_automation_link.get_regiment();
5180
5181 if(
5182 regiment.get_status() != army_group_regiment_status::await_transport
5183 && regiment.get_status() != army_group_regiment_status::embark
5184 )
5185 continue;
5186
5187 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5188 auto regiment_location = army.get_location_from_army_location();
5189 auto port_to = regiment_location.get_port_to();
5190
5191 if(port_to == fleet.get_location_from_navy_location()) {
5192 // it means that someone could embark: WAIT
5193 break;
5194 }
5195
5196 if(command::can_move_navy(*this, local_player_nation, fleet, port_to).size() > 0) {
5197 command::move_navy(*this, local_player_nation, fleet, port_to, false);
5198 break;
5199 }
5200 }
5201 }
5202 }
5203
5204 // update vacant HQ location
5205 province_queue.clear();
5206 provinces_to_reduce_weight.clear();
5207 provinces_to_maintain.clear();
5208
5209 province_queue.push_back(fat_group.get_hq());
5210
5211 size_t l = 0;
5212 size_t r = 1;
5213
5214 while(l < r) {
5215 auto current_location = province_queue[l];
5216
5217 for(auto regiment_automation_link : fat_group.get_automated_army_group_membership_regiment()) {
5218
5219 auto regiment = regiment_automation_link.get_regiment();
5220 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5221 auto army_location = army.get_location_from_army_location();
5222 auto current_path = army.get_path();
5223 auto status = regiment.get_task();
5224 if(status != army_group_regiment_task::idle) {
5225 continue;
5226 }
5227 if(current_path.size() > 0) {
5228 continue;
5229 }
5230 if(current_location == army_location) {
5231 regiment.set_task(army_group_regiment_task::gather_at_hq);
5232 regiment.set_status(army_group_regiment_status::standby);
5233 continue;
5234 }
5235 }
5236
5237 if(current_location.value >= province_definitions.first_sea_province.value) {
5238 l += 1;
5239 continue;
5240 }
5241
5242 auto ownership = world.province_get_province_ownership_as_province(current_location);
5243 auto owner = world.province_ownership_get_nation(ownership);
5244 if(owner != local_player_nation && !military::are_at_war(*this, owner, local_player_nation)) {
5245 l += 1;
5246 continue;
5247 }
5248
5249 if(fat_group.get_provinces_defend().contains(current_location)) {
5250 l += 1;
5251 continue;
5252 }
5253
5254 float supply_limit = float(military::supply_limit_in_province(
5255 *this,
5256 local_player_nation,
5257 current_location
5258 ));
5259
5260 auto current_weight = military::local_army_weight_max(
5261 *this, current_location
5262 );
5263
5264 auto supply_left = army_group_available_supply(group, current_location);
5265
5266 if(4.f < supply_left) {
5267 break;
5268 } else if(current_weight > supply_limit) {
5269 provinces_to_reduce_weight.push_back(current_location);
5270 } else {
5271 provinces_to_maintain.push_back(current_location);
5272 }
5273 l += 1;
5274
5275 for(auto adj : world.province_get_province_adjacency(current_location)) {
5276 auto other = adj.get_connected_provinces(adj.get_connected_provinces(0) == current_location ? 1 : 0);
5277
5278 if(std::find(province_queue.begin(), province_queue.end(), other) == province_queue.end()) {
5279 province_queue.push_back(other);
5280 r += 1;
5281 }
5282 }
5283 }
5284
5285 // if l < r then there is a vacant province and we had stopped early
5286 // so try to fill the vacant location
5287 if(l < r) {
5288 auto target_location = province_queue[l];
5289 dcon::province_id potential_target_port = get_port_for_landing(group, fat_group.get_hq());
5290
5291 for(auto regiment_automation_link : fat_group.get_automated_army_group_membership_regiment()) {
5292 auto regiment = regiment_automation_link.get_regiment();
5293 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5294 auto army_location = army.get_location_from_army_location();
5295 auto army_path = army.get_path();
5296
5297 if(regiment.get_task() != army_group_regiment_task::idle) {
5298 continue;
5299 }
5300 if(army_path.size() > 0) {
5301 continue;
5302 }
5303 if(army_location == target_location) {
5304 regiment.set_task(army_group_regiment_task::gather_at_hq);
5305 regiment.set_status(army_group_regiment_status::standby);
5306 continue;
5307 }
5308
5309 auto path = command::can_move_army(*this, local_player_nation, army, target_location);
5310 if(path.empty()) {
5311 // handle the case when there is no land path
5312 if(potential_target_port) {
5313 if(move_to_available_port(group, regiment)) {
5314 regiment.set_task(army_group_regiment_task::gather_at_hq);
5315 regiment.set_target(target_location);
5316 regiment.set_ferry_target(potential_target_port);
5317 break;
5318 }
5319 }
5320 } else {
5321 regiment.set_task(army_group_regiment_task::gather_at_hq);
5322 regiment.set_status(army_group_regiment_status::move_to_target);
5323 regiment.set_target(path[0]);
5324 break;
5325 }
5326 }
5327 }
5328}
5329
5330void state::army_group_update_regiment_status(dcon::automated_army_group_id group) {
5331 auto fat_group = fatten(world, group);
5332
5333 for(auto regiment_link : fat_group.get_automated_army_group_membership_regiment()) {
5334 auto regiment = regiment_link.get_regiment();
5335 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5336
5337
5338 // do not update armies on the move ... for now
5339 // mostly to avoid commands spam
5340 auto current_path = army.get_path();
5341 if(current_path.size() > 0) {
5342 regiment.set_await_command_execution_flag(false);
5343 continue;
5344 }
5345
5346 if(regiment.get_await_command_execution_flag()) {
5347 continue;
5348 }
5349
5350 auto location = world.army_get_location_from_army_location(army);
5351 auto target = regiment.get_target();
5352
5353 //handle case by case
5354
5355 switch(regiment.get_status()) {
5356 case army_group_regiment_status::move_to_target:
5357 if(location == target) {
5358 regiment.set_status(army_group_regiment_status::standby);
5359 } else {
5360 auto path = command::can_move_army(*this, local_player_nation, army, target);
5361 if(!path.empty()) {
5362 command::move_army(*this, local_player_nation, army, target, false);
5363 regiment.set_await_command_execution_flag(true);
5364 } else {
5365 regiment_reset_order(regiment);
5366 }
5367 }
5368 break;
5369 case army_group_regiment_status::move_to_port:
5370 {
5371 if(regiment.get_ferry_origin() == location) {
5372 regiment.set_status(army_group_regiment_status::await_transport);
5373 } else {
5374 command::move_army(*this, local_player_nation, army, regiment.get_ferry_origin(), false);
5375 }
5376 break;
5377 }
5378 case army_group_regiment_status::standby:
5379 if(location != target) {
5380 regiment.set_status(army_group_regiment_status::move_to_target);
5381 }
5382 break;
5383 case army_group_regiment_status::await_transport:
5384 {
5385 if(location.value >= province_definitions.first_sea_province.value) {
5386 // we somehow got to the ship? good for us
5387 regiment.set_status(army_group_regiment_status::is_transported);
5388 break;
5389 }
5390
5391 // check that we are still at port
5392 bool at_ferry_origin = location == regiment.get_ferry_origin();
5393 if(!at_ferry_origin) {
5394 regiment_reset_order(regiment);
5395 } else {
5396 //check if there are available transports at the port
5397 dcon::province_id port_to = world.province_get_port_to(location);
5398
5399 for(auto fleet_membership : fat_group.get_automated_army_group_membership_navy()) {
5400 auto fleet = fleet_membership.get_navy();
5401 //ignore moving fleets
5402 auto path = fleet.get_path();
5403 if(path.size() > 0) {
5404 continue;
5405 }
5406
5407 auto fleet_location = world.navy_get_location_from_navy_location(fleet);
5408 if(fleet_location == port_to) {
5409 // try to fit the regiment there
5410 auto path_army = command::can_move_army(*this, local_player_nation, army, fleet_location);
5411 if(path_army.size() > 0) {
5412 command::move_army(*this, local_player_nation, army, fleet_location, false);
5413 regiment.set_status(army_group_regiment_status::embark);
5414 regiment.set_await_command_execution_flag(true);
5415 break;
5416 }
5417 }
5418 }
5419 }
5420 }
5421 break;
5422 case army_group_regiment_status::is_transported:
5423 if(location.value >= province_definitions.first_sea_province.value) {
5424 // handle disembarking:
5425 auto ferry_target = regiment.get_ferry_target();
5426 auto port_to = world.province_get_port_to(ferry_target);
5427 auto transport = world.army_get_army_transport(army);
5428 auto fleet = world.army_transport_get_navy(transport);
5429 auto fleet_location = world.navy_get_location_from_navy_location(fleet);
5430
5431 //if we are at port, then we can try to disembark
5432 if(fleet_location == port_to) {
5433 // try to disembark the regiment here
5434 auto path_army = command::can_move_army(*this, local_player_nation, army, ferry_target);
5435 if(path_army.size() > 0) {
5436 command::move_army(*this, local_player_nation, army, ferry_target, false);
5437 regiment.set_await_command_execution_flag(true);
5438 regiment.set_status(army_group_regiment_status::disembark);
5439 break;
5440 }
5441 }
5442 break;
5443 }
5444 // we are transported but but our location is not a sea location?
5445 regiment_reset_order(regiment);
5446 break;
5447 case army_group_regiment_status::disembark:
5448 if(location == regiment.get_ferry_target()) {
5449 regiment.set_status(army_group_regiment_status::move_to_target);
5450 break;
5451 }
5452 // we were trying to disembark at our ferry target but we are not there?
5453 regiment.set_status(army_group_regiment_status::is_transported);
5454 break;
5455 case army_group_regiment_status::embark:
5456 if(location.value >= province_definitions.first_sea_province.value) {
5457 regiment.set_status(army_group_regiment_status::is_transported);
5458 break;
5459 }
5460 // we are not moving AND we are at land? ship had sailed without us: wait for the next one
5461 regiment.set_status(army_group_regiment_status::await_transport);
5462 break;
5463 default:
5464 break;
5465 }
5466 }
5467}
5468
5469dcon::regiment_automation_data_id state::fill_province(
5470 dcon::automated_army_group_id group_id,
5471 dcon::province_id target,
5472 std::vector<float>& regiments_expectation_ideal
5473) {
5474 static std::vector<float> regiments_expectation_current;
5475 static std::vector<float> regiments_in_candidate_army;
5476
5477 regiments_expectation_current.resize(military_definitions.unit_base_definitions.size() + 2);
5478 regiments_in_candidate_army.resize(military_definitions.unit_base_definitions.size() + 2);
5479
5480 for(uint32_t i = 0; i < military_definitions.unit_base_definitions.size(); ++i) {
5481 regiments_expectation_current[i] = 0.f;
5482 regiments_in_candidate_army[i] = 0.f;
5483 }
5484
5485 auto fat_group = fatten(world, group_id);
5486
5487 bool success = false;
5488 // calculate current regiment expectation
5489
5490 //regiments with this province as a target
5491 for(auto regiment_membership : fat_group.get_automated_army_group_membership_regiment()) {
5492 auto regiment = regiment_membership.get_regiment();
5493 if(regiment.get_target() == target) {
5494 auto regiment_type = regiment.get_regiment_from_automation().get_type();
5495 if(regiment_type) {
5496 regiments_expectation_current[regiment_type.index()] += 3.f;
5497 }
5498 }
5499 }
5500
5501 dcon::regiment_automation_data_id final_regiment{};
5502
5503 // now find a unit to move there
5504 auto target_port = get_port_for_landing(group_id, target);
5505 for(auto regiment_membership : fat_group.get_automated_army_group_membership_regiment()) {
5506 auto regiment = regiment_membership.get_regiment();
5507
5508 auto current_task = regiment.get_task();
5509
5510 if(current_task != army_group_regiment_task::idle && current_task != army_group_regiment_task::gather_at_hq) {
5511 continue;
5512 }
5513
5514 auto regiment_type = regiment.get_regiment_from_automation().get_type();
5515
5516 if(!regiment_type) {
5517 continue;
5518 }
5519
5520 float required =
5521 regiments_expectation_ideal[regiment_type.index()]
5522 - regiments_expectation_current[regiment_type.index()];
5523
5524 if(required >= 2.9f) {
5525 auto army = regiment.get_regiment_from_automation().get_army_from_army_membership();
5526 auto path = command::can_move_army(*this, local_player_nation, army, target);
5527 if(path.empty() && target != army.get_location_from_army_location()) {
5528 //naval route
5529 if(target_port && move_to_available_port(group_id, regiment)) {
5530 regiment.set_ferry_target(target_port);
5531 regiment.set_target(target);
5532 return regiment;
5533 }
5534 } else {
5535 //land route
5536 regiment.set_status(army_group_regiment_status::move_to_target);
5537 regiment.set_target(target);
5538 return regiment;
5539 }
5540 }
5541 }
5542
5543 return {};
5544}
5545
5546void state::fill_vector_of_connected_provinces(dcon::province_id p1, bool is_land, std::vector<dcon::province_id>& provinces) {
5547 provinces.clear();
5548 if(world.province_get_nation_from_province_ownership(p1) == local_player_nation) {
5549 if(is_land) {
5550 auto rid = world.province_get_connected_region_id(p1);
5551 for(const auto pc : world.nation_get_province_ownership_as_nation(local_player_nation)) {
5552 if(pc.get_province().get_connected_region_id() == rid
5553 && pc.get_province().get_province_control().get_nation() == local_player_nation) {
5554 provinces.push_back(pc.get_province());
5555 }
5556 }
5557 } else {
5558 for(const auto pc : world.nation_get_province_ownership_as_nation(local_player_nation)) {
5559 if(pc.get_province().get_province_control().get_nation() == local_player_nation
5560 && pc.get_province().get_is_coast()) {
5561 provinces.push_back(pc.get_province());
5562 }
5563 }
5564 }
5565 }
5566}
5567
5569 dcon::province_id p;
5570 dcon::culture_id c;
5571 dcon::unit_type_id u;
5572};
5573
5574void state::build_up_to_template_land(
5575 macro_builder_template const& target_template,
5576 dcon::province_id target_province,
5577 std::vector<dcon::province_id>& available_provinces,
5578 std::array<uint8_t, sys::macro_builder_template::max_types>& current_distribution
5579) {
5580 // Have to queue commands [temporarily on UI side] or it may mess calculations up
5581 std::vector<build_queue_data> build_queue;
5582 auto upper_limit = sys::macro_builder_template::max_types;
5583
5584 // here we store how many units we need to build
5586 std::memcpy(remaining_to_build, target_template.amounts, sizeof(remaining_to_build));
5587
5588 // subtract current amount
5589 for(dcon::unit_type_id::value_base_t i = 0; i < upper_limit; i++) {
5590 dcon::unit_type_id utid = dcon::unit_type_id(i);
5591 if(remaining_to_build[i] < current_distribution[i]) {
5592 remaining_to_build[i] = 0;
5593 } else {
5594 remaining_to_build[i] -= current_distribution[i];
5595 }
5596 }
5597
5598 for(const auto prov : available_provinces) {
5599 for(const auto p : world.province_get_pop_location_as_province(prov)) {
5600 auto pop = p.get_pop();
5601 if(pop.get_poptype() != culture_definitions.soldiers)
5602 continue;
5603 int32_t possible = military::regiments_possible_from_pop(*this, p.get_pop());
5604
5605 auto source = pop.get_regiment_source();
5606 int32_t used = int32_t(source.end() - source.begin());
5607
5608 auto constructions = pop.get_province_land_construction_as_pop();
5609 used += int32_t(constructions.end() - constructions.begin());
5610
5611 int32_t avail = possible - used;
5612
5613 if(possible <= 0) {
5614 continue;
5615 }
5616 if(avail <= 0) {
5617 continue;
5618 }
5619
5620 auto unit_types = military_definitions.unit_base_definitions.size();
5621
5622 for(dcon::unit_type_id::value_base_t i = 0; i < unit_types; i++) {
5623 dcon::unit_type_id utid = dcon::unit_type_id(i);
5624
5625 if(!military_definitions.unit_base_definitions[utid].is_land) {
5626 continue;
5627 }
5628
5629 if(remaining_to_build[i] == 0) {
5630 continue;
5631 }
5632
5634 *this,
5635 local_player_nation,
5636 prov,
5637 pop.get_culture(),
5638 utid,
5639 target_province
5640 );
5641
5642 if(!can_build) {
5643 continue;
5644 }
5645
5646 for(int32_t j = 0; j < int32_t(remaining_to_build[i]) && j < avail; j++) {
5647 build_queue.push_back(build_queue_data{ prov, pop.get_culture(), utid });
5648 remaining_to_build[i]--;
5649 avail--;
5650 if(remaining_to_build[i] == 0)
5651 break;
5652 }
5653 }
5654 }
5655 }
5656 // Then flush them all!
5657 for(const auto& build : build_queue) {
5659 *this,
5660 local_player_nation,
5661 build.p,
5662 build.c,
5663 build.u,
5664 target_province
5665 );
5666 }
5667}
5668
5669void selected_regiments_add(sys::state& state, dcon::regiment_id reg) {
5670 for(unsigned i = 0; i < state.selected_regiments.size(); i++) {
5671 // Toggle selection
5672 if(state.selected_regiments[i] == reg) {
5673 state.selected_regiments[i] = dcon::regiment_id{};
5674 break;
5675 }
5676 // Add to selection
5677 if(!state.selected_regiments[i]) {
5678 state.selected_regiments[i] = reg;
5679 break;
5680 }
5681 }
5682 state.game_state_updated.store(true, std::memory_order_release);
5683}
5685 for(unsigned i = 0; i < state.selected_regiments.size(); i++) {
5686 if(state.selected_regiments[i]) {
5687 state.selected_regiments[i] = dcon::regiment_id{};
5688 } else {
5689 break;
5690 }
5691 }
5692 state.game_state_updated.store(true, std::memory_order_release);
5693}
5694
5695void selected_ships_add(sys::state& state, dcon::ship_id sh) {
5696 for(unsigned i = 0; i < state.selected_ships.size(); i++) {
5697 // Toggle selection
5698 if(state.selected_ships[i] == sh) {
5699 state.selected_ships[i] = dcon::ship_id{};
5700 break;
5701 }
5702 // Add to selection
5703 if(!state.selected_ships[i]) {
5704 state.selected_ships[i] = sh;
5705 break;
5706 }
5707 }
5708 state.game_state_updated.store(true, std::memory_order_release);
5709}
5711 for(unsigned i = 0; i < state.selected_ships.size(); i++) {
5712 if(state.selected_ships[i]) {
5713 state.selected_ships[i] = dcon::ship_id{};
5714 } else {
5715 break;
5716}
5717 }
5718 state.game_state_updated.store(true, std::memory_order_release);
5719}
5720
5721} // namespace sys
int blake2b(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen)
Definition: blake2.c:259
void update_fog_of_war(sys::state &state)
Definition: map.cpp:72
bool map_to_screen(sys::state &state, glm::vec2 map_pos, glm::vec2 screen_size, glm::vec2 &screen_pos)
Definition: map_state.cpp:1634
void on_mouse_wheel(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod, float amount)
Definition: map_state.cpp:1432
void load_map_data(parsers::scenario_building_context &context)
void on_mbuttom_up(int32_t x, int32_t y, sys::key_modifiers mod)
Definition: map_state.cpp:1554
void on_mbuttom_down(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1542
void update(sys::state &state)
Definition: map_state.cpp:1189
void on_key_up(sys::virtual_key keycode, sys::key_modifiers mod)
Definition: map_state.cpp:1388
float get_zoom()
Definition: map_state.hpp:97
glm::vec2 normalize_map_coord(glm::vec2 pos)
Definition: map_state.cpp:1793
map_mode::mode active_map_mode
Definition: map_state.hpp:57
void on_mouse_move(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1439
dcon::province_id get_province_under_mouse(sys::state &state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y)
Definition: map_state.cpp:1613
display_data map_data
Definition: map_state.hpp:60
void render(sys::state &state)
std::string accumulated_errors
Definition: parsers.hpp:62
std::string accumulated_warnings
Definition: parsers.hpp:63
std::string file_name
Definition: parsers.hpp:61
RIGTORP_NODISCARD T * front() noexcept
Definition: SPSCQueue.h:168
void pop() noexcept
Definition: SPSCQueue.h:179
RIGTORP_NODISCARD bool try_push(const T &v) noexcept(std::is_nothrow_copy_constructible< T >::value)
Definition: SPSCQueue.h:155
year_month_day to_ymd(absolute_time_point base) const noexcept
void change_locale(sys::state &state, dcon::locale_id l)
Definition: fonts.cpp:281
tagged_vector< element_data, dcon::gui_def_id > gui
tagged_vector< gfx_object, dcon::gfx_object_id > gfx
std::vector< diplomatic_message::message > messages
virtual message_result impl_set(sys::state &state, Cyto::Any &payload) noexcept
virtual tooltip_behavior has_tooltip(sys::state &state) noexcept
virtual void on_text(sys::state &state, char32_t ch) noexcept
virtual void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept
virtual message_result get(sys::state &state, Cyto::Any &payload) noexcept
virtual void on_hover_end(sys::state &state) noexcept
virtual message_result impl_on_scroll(sys::state &state, int32_t x, int32_t y, float amount, sys::key_modifiers mods) noexcept
virtual void impl_on_update(sys::state &state) noexcept
virtual void impl_render(sys::state &state, int32_t x, int32_t y) noexcept
virtual void on_drag_finish(sys::state &state) noexcept
element_data base_data
virtual void on_drag(sys::state &state, int32_t oldx, int32_t oldy, int32_t x, int32_t y, sys::key_modifiers mods) noexcept
void set_visible(sys::state &state, bool vis)
virtual message_result impl_on_mouse_move(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept
virtual mouse_probe impl_probe_mouse(sys::state &state, int32_t x, int32_t y, mouse_probe_type type) noexcept
virtual void on_hover(sys::state &state) noexcept
static void make_new_report(sys::state &state, military::land_battle_report const &r)
std::vector< notification::message > messages
static void make_new_report(sys::state &state, military::naval_battle_report const &r)
#define assert(condition)
Definition: debug.h:74
char * LLVMGetErrorMessage(LLVMErrorRef Err)
void LLVMDisposeErrorMessage(char *ErrMsg)
LLVMErrorRef LLVMOrcLLJITLookup(LLVMOrcLLJITRef J, LLVMOrcExecutorAddress *Result, const char *Name)
uint64_t LLVMOrcExecutorAddress
Definition: Orc.h:51
void bytes_to_windows(char const *bytes, size_t size, std::string const &project_name, ankerl::unordered_dense::map< std::string, sys::aui_pending_bytes > &map)
void update_crisis_leaders(sys::state &state)
Definition: ai.cpp:2334
void initialize_ai_tech_weights(sys::state &state)
Definition: ai.cpp:451
void daily_cleanup(sys::state &state)
Definition: ai.cpp:4266
void take_reforms(sys::state &state)
Definition: ai.cpp:2022
void form_alliances(sys::state &state)
Definition: ai.cpp:158
void update_ai_ruling_party(sys::state &state)
Definition: ai.cpp:1030
void make_war_decs(sys::state &state)
Definition: ai.cpp:3673
void upgrade_colonies(sys::state &state)
Definition: ai.cpp:2004
void update_cb_fabrication(sys::state &state)
Definition: ai.cpp:2926
void make_defense(sys::state &state)
Definition: ai.cpp:5455
void refresh_home_ports(sys::state &state)
Definition: ai.cpp:4258
void update_ai_colony_starting(sys::state &state)
Definition: ai.cpp:1954
void update_focuses(sys::state &state)
Definition: ai.cpp:785
void update_budget(sys::state &state)
Definition: ai.cpp:3800
void update_ai_colonial_investment(sys::state &state)
Definition: ai.cpp:1895
void perform_influence_actions(sys::state &state)
Definition: ai.cpp:726
void make_attacks(sys::state &state)
Definition: ai.cpp:5446
void update_ships(sys::state &state)
Definition: ai.cpp:4031
void update_ai_econ_construction(sys::state &state)
Definition: ai.cpp:1344
void build_ships(sys::state &state)
Definition: ai.cpp:4095
void prune_alliances(sys::state &state)
Definition: ai.cpp:187
void update_war_intervention(sys::state &state)
Definition: ai.cpp:2647
void update_influence_priorities(sys::state &state)
Definition: ai.cpp:600
void update_ai_research(sys::state &state)
Definition: ai.cpp:375
void update_ai_general_status(sys::state &state)
Definition: ai.cpp:58
void update_factory_types_priority(sys::state &state)
Definition: ai.cpp:496
void update_land_constructions(sys::state &state)
Definition: ai.cpp:5723
void identify_focuses(sys::state &state)
Definition: ai.cpp:766
void civilize(sys::state &state)
Definition: ai.cpp:2014
void make_peace_offers(sys::state &state)
Definition: ai.cpp:3360
void take_ai_decisions(sys::state &state)
Definition: ai.cpp:923
void general_ai_unit_tick(sys::state &state)
Definition: ai.cpp:6018
void add_gw_goals(sys::state &state)
Definition: ai.cpp:3330
bool can_start_research(sys::state &state, dcon::nation_id source, dcon::technology_id tech)
Definition: commands.cpp:188
void 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:694
void mark_regiments_to_split(sys::state &state, dcon::nation_id source, std::array< dcon::regiment_id, num_packed_units > const &list)
Definition: commands.cpp:4396
bool can_start_land_unit_construction(sys::state &state, dcon::nation_id source, dcon::province_id location, dcon::culture_id soldier_culture, dcon::unit_type_id type, dcon::province_id template_province)
Definition: commands.cpp:705
std::vector< dcon::province_id > can_move_navy(sys::state &state, dcon::nation_id source, dcon::navy_id n, dcon::province_id dest)
Definition: commands.cpp:3522
void make_event_choice(sys::state &state, event::pending_human_n_event const &e, uint8_t option_id)
Definition: commands.cpp:2087
void execute_pending_commands(sys::state &state)
Definition: commands.cpp:6047
std::vector< dcon::province_id > can_move_army(sys::state &state, dcon::nation_id source, dcon::army_id a, dcon::province_id dest)
Definition: commands.cpp:3284
void discover_inventions(sys::state &state)
Definition: culture.cpp:1040
void apply_invention(sys::state &state, dcon::nation_id target_nation, dcon::invention_id i_id)
Definition: culture.cpp:560
float effective_technology_cost(sys::state &state, uint32_t current_year, dcon::nation_id target_nation, dcon::technology_id tech_id)
Definition: culture.cpp:969
void fix_slaves_in_province(sys::state &state, dcon::nation_id owner, dcon::province_id p)
Definition: culture.cpp:805
void clear_existing_tech_effects(sys::state &state)
Definition: culture.cpp:94
void repopulate_technology_effects(sys::state &state)
Definition: culture.cpp:158
void update_all_nations_issue_rules(sys::state &state)
Definition: culture.cpp:866
void set_default_issue_and_reform_options(sys::state &state)
Definition: culture.cpp:39
void repopulate_invention_effects(sys::state &state)
Definition: culture.cpp:260
void update_research(sys::state &state, uint32_t current_year)
Definition: culture.cpp:1012
void restore_unsaved_values(sys::state &state)
Definition: culture.cpp:883
void create_initial_ideology_and_issues_distribution(sys::state &state)
Definition: culture.cpp:906
void apply_technology(sys::state &state, dcon::nation_id target_nation, dcon::technology_id t_id)
Definition: culture.cpp:412
tech_category
Definition: culture.hpp:106
pop_satisfaction_wrapper_fat fatten(data_container const &c, pop_satisfaction_wrapper_id id) noexcept
void alt_demographics_update_extras(sys::state &state)
void remove_small_pops(sys::state &state)
void update_immigration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
constexpr dcon::demographics_key total(0)
void apply_assimilation(sys::state &state, uint32_t offset, uint32_t divisions, assimilation_buffer &pbuf)
void update_internal_migration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
void apply_type_changes(sys::state &state, uint32_t offset, uint32_t divisions, promotion_buffer &pbuf)
void update_growth(sys::state &state, uint32_t offset, uint32_t divisions)
void alt_regenerate_from_pop_data_daily(sys::state &state)
void update_issues(sys::state &state, uint32_t offset, uint32_t divisions, issues_buffer &ibuf)
void apply_internal_migration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
void update_type_changes(sys::state &state, uint32_t offset, uint32_t divisions, promotion_buffer &pbuf)
void regenerate_from_pop_data_full(sys::state &state)
uint32_t size(sys::state const &state)
void update_assimilation(sys::state &state, uint32_t offset, uint32_t divisions, assimilation_buffer &pbuf)
void alt_regenerate_from_pop_data_full(sys::state &state)
void remove_size_zero_pops(sys::state &state)
void update_colonial_migration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
void apply_ideologies(sys::state &state, uint32_t offset, uint32_t divisions, ideology_buffer &pbuf)
void update_ideologies(sys::state &state, uint32_t offset, uint32_t divisions, ideology_buffer &ibuf)
void update_consciousness(sys::state &state, uint32_t offset, uint32_t divisions)
void update_literacy(sys::state &state, uint32_t offset, uint32_t divisions)
void update_militancy(sys::state &state, uint32_t offset, uint32_t divisions)
void apply_colonial_migration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
void regenerate_from_pop_data_daily(sys::state &state)
void apply_immigration(sys::state &state, uint32_t offset, uint32_t divisions, migration_buffer &pbuf)
void apply_issues(sys::state &state, uint32_t offset, uint32_t divisions, issues_buffer &pbuf)
void update_pending(sys::state &state)
void initialize(sys::state &state)
Definition: economy.cpp:1483
void update_factory_employment(sys::state &state)
Definition: economy.cpp:2044
void prune_factories(sys::state &state)
Definition: economy.cpp:8876
constexpr uint32_t gdp_history_length
Definition: economy.hpp:127
void presimulate(sys::state &state)
Definition: economy.cpp:1151
void update_rgo_employment(sys::state &state)
Definition: economy.cpp:1901
void daily_update(sys::state &state, bool presimulation, float presimulation_stage)
Definition: economy.cpp:5363
float price(sys::state const &state, dcon::state_instance_id s, dcon::commodity_id c)
Definition: economy.cpp:150
constexpr uint32_t price_history_length
Definition: economy.hpp:126
void regenerate_unsaved_values(sys::state &state)
Definition: economy.cpp:7485
int32_t most_recent_price_record_index(sys::state &state)
Definition: economy.cpp:825
void sanity_check(sys::state &state)
Definition: economy.cpp:136
constexpr int32_t max_building_types
Definition: constants.hpp:579
int32_t most_recent_gdp_record_index(sys::state &state)
Definition: economy.cpp:832
float gdp_adjusted(sys::state &state, dcon::market_id n)
Definition: economy.cpp:846
constexpr uint16_t no_payload
void execute(sys::state &state, dcon::effect_key key, int32_t primary, int32_t this_slot, int32_t from_slot, uint32_t r_lo, uint32_t r_hi)
Definition: effects.cpp:5413
void fire_fixed_event(sys::state &state, std::vector< nations::fixed_event > const &v, int32_t primary_slot, slot_type pt, dcon::nation_id this_slot, int32_t from_slot, slot_type ft)
Definition: events.cpp:709
void update_events(sys::state &state)
Definition: events.cpp:517
std::string multiplicative_modifier(sys::state &state, dcon::value_modifier_key modifier)
std::string additive_modifier(sys::state &state, dcon::value_modifier_key modifier)
void run_fif_interpreter(environment &env, std::string_view on_text)
Definition: fif.hpp:5096
void common_fif_environment(sys::state &state, fif::environment &env)
void on_lbutton_up(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mod)
Definition: game_scene.cpp:475
void switch_scene(sys::state &state, scene_id ui_scene)
Definition: game_scene.cpp:13
void on_rbutton_down(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mod)
Definition: game_scene.cpp:278
void on_key_down(sys::state &state, sys::virtual_key keycode, sys::key_modifiers mod)
Definition: game_scene.cpp:721
void on_lbutton_down(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mod)
Definition: game_scene.cpp:297
void set_map_mode(sys::state &state, mode mode)
Definition: map_modes.cpp:776
constexpr float zoom_close
Definition: constants.hpp:605
void update_text_lines(sys::state &state, display_data &map_data)
Definition: map_state.cpp:452
void run_gc(sys::state &state)
Definition: military.cpp:3314
void update_naval_supply_points(sys::state &state)
Definition: military.cpp:1274
void army_arrives_in_province(sys::state &state, dcon::army_id a, dcon::province_id p, crossing_type crossing, dcon::land_battle_id from)
Definition: military.cpp:4387
int32_t free_transport_capacity(sys::state &state, dcon::navy_id n)
Definition: military.cpp:6851
void apply_base_unit_stat_modifiers(sys::state &state)
Definition: military.cpp:195
void update_blockade_status(sys::state &state)
Definition: military.cpp:597
void update_ticking_war_score(sys::state &state)
Definition: military.cpp:3842
void increase_dig_in(sys::state &state)
Definition: military.cpp:7200
void apply_attrition(sys::state &state)
Definition: military.cpp:5478
void reset_unit_stats(sys::state &state)
Definition: military.cpp:186
void update_land_battles(sys::state &state)
Definition: military.cpp:5619
void update_cbs(sys::state &state)
Definition: military.cpp:1361
float local_enemy_army_weight_max(sys::state &state, dcon::province_id prov, dcon::nation_id nation)
Definition: military.cpp:5417
void regenerate_land_unit_average(sys::state &state)
Definition: military.cpp:1202
void recover_org(sys::state &state)
Definition: military.cpp:7278
void advance_mobilizations(sys::state &state)
Definition: military.cpp:7570
void update_movement(sys::state &state)
Definition: military.cpp:6585
int32_t regiments_possible_from_pop(sys::state &state, dcon::pop_id p)
Definition: military.cpp:851
void reinforce_regiments(sys::state &state)
Definition: military.cpp:7390
void apply_regiment_damage(sys::state &state)
Definition: military.cpp:5537
void monthly_leaders_update(sys::state &state)
Definition: military.cpp:2204
float local_army_weight_max(sys::state &state, dcon::province_id prov)
Definition: military.cpp:5405
void update_naval_battles(sys::state &state)
Definition: military.cpp:6155
void update_siege_progress(sys::state &state)
Definition: military.cpp:6928
void update_all_recruitable_regiments(sys::state &state)
Definition: military.cpp:1181
bool are_at_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
Definition: military.cpp:649
void set_initial_leaders(sys::state &state)
Definition: military.cpp:2877
void regenerate_total_regiment_counts(sys::state &state)
Definition: military.cpp:1190
int32_t supply_limit_in_province(sys::state &state, dcon::nation_id n, dcon::province_id p)
Definition: military.cpp:794
int32_t transport_capacity(sys::state &state, dcon::navy_id n)
Definition: military.cpp:6843
void repair_ships(sys::state &state)
Definition: military.cpp:7449
void daily_leaders_update(sys::state &state)
Definition: military.cpp:2258
void restore_unsaved_values(sys::state &state)
Definition: military.cpp:233
void regenerate_ship_scores(sys::state &state)
Definition: military.cpp:1227
void update_blackflag_status(sys::state &state, dcon::province_id p)
Definition: military.cpp:7166
bool siege_potential(sys::state &state, dcon::nation_id army_controller, dcon::nation_id province_controller)
Definition: military.cpp:6913
std::string int_to_tag(uint32_t v)
Definition: nations.hpp:14
void run_gc(sys::state &state)
Definition: nations.cpp:1790
void restore_unsaved_values(sys::state &state)
Definition: nations.cpp:134
void daily_update_flashpoint_tension(sys::state &state)
Definition: nations.cpp:2343
void monthly_flashpoint_update(sys::state &state)
Definition: nations.cpp:2266
void update_administrative_efficiency(sys::state &state)
Definition: nations.cpp:718
void update_influence(sys::state &state)
Definition: nations.cpp:2109
void update_military_scores(sys::state &state)
Definition: nations.cpp:935
void update_monthly_points(sys::state &state)
Definition: nations.cpp:1508
void generate_initial_state_instances(sys::state &state)
Definition: nations.cpp:626
void update_cached_values(sys::state &state)
Definition: nations.cpp:127
void update_revanchism(sys::state &state)
Definition: nations.cpp:1478
uint32_t tag_to_int(char first, char second, char third)
Definition: nations.hpp:11
void update_industrial_scores(sys::state &state)
Definition: nations.cpp:900
void update_research_points(sys::state &state)
Definition: nations.cpp:796
void restore_state_instances(sys::state &state)
Definition: nations.cpp:92
void generate_sea_trade_routes(sys::state &state)
Definition: nations.cpp:288
void update_great_powers(sys::state &state)
Definition: nations.cpp:1072
void recalculate_markets_distance(sys::state &state)
Definition: nations.cpp:162
void generate_initial_trade_routes(sys::state &state)
Definition: nations.cpp:581
float get_treasury(sys::state &state, dcon::nation_id n)
Definition: nations.cpp:1607
void update_ui_rankings(sys::state &state)
Definition: nations.cpp:997
void update_rankings(sys::state &state)
Definition: nations.cpp:966
void update_crisis(sys::state &state)
Definition: nations.cpp:2738
void send_and_receive_commands(sys::state &state)
Definition: network.cpp:1368
bool nation_is_interesting(sys::state &state, dcon::nation_id n)
void initialize_msaa(sys::state &state, int32_t size_x, int32_t size_y)
void deinitialize_msaa(sys::state &state)
void render_textured_rect(sys::state const &state, color_modification enabled, float x, float y, float width, float height, GLuint texture_handle, ui::rotation r, bool flipped, bool rtl)
GLuint get_texture_handle(sys::state &state, dcon::texture_id id, bool keep_data)
Definition: texture.cpp:1066
native_string flag_type_to_name(sys::state &state, culture::flag_type type)
Definition: texture.cpp:889
void parse_csv_pop_history_file(sys::state &state, const char *start, const char *end, error_handler &err, scenario_building_context &context)
void read_map_adjacency(char const *start, char const *end, error_handler &err, scenario_building_context &context)
void read_pending_rebel_type(dcon::rebel_type_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
dcon::trigger_key read_triggered_modifier_condition(token_generator &gen, error_handler &err, scenario_building_context &context)
dcon::trigger_key make_trigger(token_generator &gen, error_handler &err, trigger_building_context &context)
void parse_csv_province_history_file(sys::state &state, const char *start, const char *end, error_handler &err, scenario_building_context &context)
void read_pending_technology(dcon::technology_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
void commit_pending_events(error_handler &err, scenario_building_context &context)
void read_pending_reform(dcon::reform_option_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
void read_pending_invention(dcon::invention_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
void read_pending_option(dcon::issue_option_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
void read_pending_crime(dcon::crime_id id, token_generator &gen, error_handler &err, scenario_building_context &context)
void make_leader_images(scenario_building_context &outer_context)
int32_t parse_int(std::string_view content, int32_t line, error_handler &err)
Definition: parsers.cpp:226
void add_locale(sys::state &state, std::string_view locale_name, char const *data_start, char const *data_end)
dcon::effect_key make_effect(token_generator &gen, error_handler &err, effect_building_context &context)
void make_base_units(scenario_building_context &context)
void read_map_colors(char const *start, char const *end, error_handler &err, scenario_building_context &context)
void daily_party_loyalty_update(sys::state &state)
Definition: politics.cpp:690
void recalculate_upper_house(sys::state &state, dcon::nation_id n)
Definition: politics.cpp:523
void update_displayed_identity(sys::state &state, dcon::nation_id id)
Definition: politics.cpp:429
void update_elections(sys::state &state)
Definition: politics.cpp:751
void set_political_reform_desire(sys::state &state, dcon::pop_id p, float v)
void set_social_reform_desire(sys::state &state, dcon::pop_id p, float v)
uint32_t size(sys::state const &state)
void regenerate_is_primary_or_accepted(sys::state &state)
void set_demo(sys::state &state, dcon::pop_id p, dcon::pop_demographics_key k, float v)
dcon::pop_demographics_key to_key(sys::state const &state, dcon::ideology_id v)
float get_demo(sys::state const &state, dcon::pop_id p, dcon::pop_demographics_key k)
constexpr uint8_t impassible_bit
Definition: constants.hpp:595
constexpr uint8_t state_bit
Definition: constants.hpp:592
constexpr uint8_t national_bit
Definition: constants.hpp:593
constexpr uint8_t coastal_bit
Definition: constants.hpp:594
void update_connected_regions(sys::state &state)
Definition: province.cpp:30
void update_blockaded_cache(sys::state &state)
Definition: province.cpp:314
void update_nationalism(sys::state &state)
Definition: province.cpp:1113
void update_crimes(sys::state &state)
Definition: province.cpp:1121
std::vector< dcon::province_id > make_safe_land_path(sys::state &state, dcon::province_id start, dcon::province_id end, dcon::nation_id nation_as)
Definition: province.cpp:1930
void ve_for_each_land_province(sys::state &state, F const &func)
void restore_unsaved_values(sys::state &state)
Definition: province.cpp:330
void for_each_land_province(sys::state &state, F const &func)
void update_cached_values(sys::state &state)
Definition: province.cpp:305
void update_colonization(sys::state &state)
Definition: province.cpp:1550
constexpr uint16_t to_map_id(dcon::province_id id)
Definition: province.hpp:10
void restore_distances(sys::state &state)
Definition: province.cpp:2383
void execute_province_defections(sys::state &state)
Definition: rebels.cpp:1051
void rebel_risings_check(sys::state &state)
Definition: rebels.cpp:907
void update_movement_values(sys::state &state)
Definition: rebels.cpp:37
void update_armies(sys::state &state)
Definition: rebels.cpp:1333
void update_factions(sys::state &state)
Definition: rebels.cpp:722
void execute_rebel_victories(sys::state &state)
Definition: rebels.cpp:1184
void daily_update_rebel_organization(sys::state &state)
Definition: rebels.cpp:738
void update_movements(sys::state &state)
Definition: rebels.cpp:318
void rebel_hunting_check(sys::state &state)
Definition: rebels.cpp:810
std::vector< unopened_file > list_files(directory const &dir, native_char const *extension)
native_string win1250_to_native(std::string_view data_in)
std::vector< directory > list_subdirectories(directory const &dir)
directory open_directory(directory const &dir, native_string_view directory_name)
native_string utf8_to_native(std::string_view data_in)
void write_file(directory const &dir, native_string_view file_name, char const *file_data, uint32_t file_size)
directory get_or_create_oos_directory()
directory get_or_create_settings_directory()
native_string get_full_name(directory const &f)
directory get_or_create_save_game_directory()
std::optional< file > open_file(directory const &dir, native_string_view file_name)
std::optional< unopened_file > peek_file(directory const &dir, native_string_view file_name)
std::string native_to_utf8(native_string_view data_in)
file_contents view_contents(file const &f)
native_string get_file_name(unopened_file const &f)
audio_instance & get_peace_sound(sys::state &state)
Definition: sound_nix.cpp:326
audio_instance & get_minor_event_sound(sys::state &state)
Definition: sound_nix.cpp:359
audio_instance & get_revolt_sound(sys::state &state)
Definition: sound_nix.cpp:347
audio_instance & get_army_built_sound(sys::state &state)
Definition: sound_nix.cpp:329
audio_instance & get_accept_sound(sys::state &state)
Definition: sound_nix.cpp:368
audio_instance & get_technology_finished_sound(sys::state &state)
Definition: sound_nix.cpp:338
audio_instance & get_declaration_of_war_sound(sys::state &state)
Definition: sound_nix.cpp:335
audio_instance & get_naval_base_built_sound(sys::state &state)
Definition: sound_nix.cpp:356
audio_instance & get_major_event_sound(sys::state &state)
Definition: sound_nix.cpp:362
audio_instance & get_railroad_built_sound(sys::state &state)
Definition: sound_nix.cpp:353
void play_effect(sys::state &state, audio_instance &s, float volume)
Definition: sound_nix.cpp:198
audio_instance & get_decline_sound(sys::state &state)
Definition: sound_nix.cpp:365
audio_instance & get_election_sound(sys::state &state)
Definition: sound_nix.cpp:344
audio_instance & get_navy_built_sound(sys::state &state)
Definition: sound_nix.cpp:332
audio_instance & get_fort_built_sound(sys::state &state)
Definition: sound_nix.cpp:350
void play_interface_sound(sys::state &state, audio_instance &s, float volume)
Definition: sound_nix.cpp:203
audio_instance & get_diplomatic_request_sound(sys::state &state)
Definition: sound_nix.cpp:371
audio_instance & get_chat_message_sound(sys::state &state)
Definition: sound_nix.cpp:374
audio_instance & get_factory_built_sound(sys::state &state)
Definition: sound_nix.cpp:341
constexpr uint8_t popup
Definition: constants.hpp:537
constexpr uint8_t pause
Definition: constants.hpp:538
constexpr uint8_t log
Definition: constants.hpp:536
MOD_NAT_LIST constexpr uint32_t count
Definition: modifiers.hpp:215
MOD_PROV_LIST constexpr uint32_t count
Definition: modifiers.hpp:207
Definition: constants.hpp:4
int32_t days_difference(year_month_day start, year_month_day end)
uint8_t * write_scenario_section(uint8_t *ptr_in, sys::state &state)
void selected_ships_add(sys::state &state, dcon::ship_id sh)
void selected_ships_clear(sys::state &state)
constexpr msg_setting_entry message_setting_map[size_t(message_base_type::count)]
Definition: constants.hpp:417
size_t sizeof_save_section(sys::state &state)
uint8_t const * read_save_header(uint8_t const *ptr_in, save_header &header_out)
virtual_key
Definition: constants.hpp:5
void selected_regiments_clear(sys::state &state)
@ autosave
Definition: constants.hpp:567
uint32_t pack_color(float r, float g, float b)
constexpr size_t const_max_selected_units
void repopulate_modifier_effects(sys::state &state)
Definition: modifiers.cpp:654
key_modifiers
Definition: constants.hpp:156
uint8_t * write_save_section(uint8_t *ptr_in, sys::state &state)
scenario_size sizeof_scenario_section(sys::state &state)
constexpr int32_t tooltip_width
void update_modifier_effects(sys::state &state)
Definition: modifiers.cpp:662
constexpr int32_t max_autosaves
Definition: constants.hpp:176
size_t sizeof_save_header(save_header const &header_in)
bool is_playable_date(date d, absolute_time_point start, absolute_time_point end)
void write_save_file(sys::state &state, save_type type, std::string const &name)
color_blind_mode
Definition: constants.hpp:554
void selected_regiments_add(sys::state &state, dcon::regiment_id reg)
void list_pop_types(sys::state &state, parsers::scenario_building_context &context)
Definition: bmfont.cpp:118
columnar_layout create_columnar_layout(sys::state &state, layout &dest, layout_parameters const &params, int32_t column_width)
Definition: text.cpp:1897
char16_t win1250toUTF16(char in)
Definition: text.cpp:587
uint16_t name_into_font_id(sys::state &state, std::string_view txt)
Definition: fonts.cpp:93
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
Definition: text.cpp:617
dcon::text_key get_name(sys::state &state, dcon::nation_id id)
Definition: text.cpp:880
void consume_csv_file(sys::state &state, char const *file_content, uint32_t file_size, int32_t target_column, bool as_unicode)
Definition: text.cpp:96
dcon::text_key find_or_add_key(sys::state &state, std::string_view key, bool as_unicode)
Definition: text.cpp:695
int32_t to_generic(dcon::province_id v)
Definition: triggers.hpp:12
constexpr uint16_t no_payload
bool evaluate(sys::state &state, dcon::trigger_key key, int32_t primary, int32_t this_slot, int32_t from_slot)
Definition: triggers.cpp:5895
float evaluate_additive_modifier(sys::state &state, dcon::value_modifier_key modifier, int32_t primary, int32_t this_slot, int32_t from_slot)
Definition: triggers.cpp:5826
uint16_t * recurse_over_triggers(uint16_t *source, T const &f)
constexpr uint16_t association_ne
void populate_map_tooltip(sys::state &state, text::columnar_layout &contents, dcon::province_id prov)
void create_in_game_windows(sys::state &state)
ogl::color_modification get_color_modification(bool is_under_mouse, bool is_disabled, bool is_interactable)
void load_text_gui_definitions(sys::state &state, parsers::building_gfx_context &context, parsers::error_handler &err)
Definition: gui_graphics.cpp:7
bool events_pause_test(sys::state &state)
Definition: gui_event.cpp:1163
void new_event_window(sys::state &state, event_data_wrapper dat)
Definition: gui_event.cpp:1191
window_state
Definition: window.hpp:46
void change_cursor(sys::state &state, cursor_type type)
Definition: window_nix.cpp:351
#define NATIVE(X)
std::string_view native_string_view
std::string native_string
#define NATIVE_DIR_SEPARATOR
uint uint32_t
ulong uint64_t
uchar uint8_t
@ ident
std::vector< dcon::issue_id > political_issues
Definition: culture.hpp:126
std::vector< dcon::reform_id > military_issues
Definition: culture.hpp:128
tagged_vector< crime_info, dcon::crime_id > crimes
Definition: culture.hpp:134
std::vector< dcon::reform_id > economic_issues
Definition: culture.hpp:129
std::vector< dcon::issue_id > party_issues
Definition: culture.hpp:125
std::vector< dcon::issue_id > social_issues
Definition: culture.hpp:127
dcon::ideology_id conservative
Definition: culture.hpp:155
std::function< void(sys::state &state)> on_game_state_update
Definition: game_scene.hpp:176
std::function< ui::mouse_probe(sys::state &state, ui::mouse_probe mouse_probe, ui::mouse_probe tooltip_probe)> recalculate_tooltip_probe
Definition: game_scene.hpp:171
std::function< void(sys::state &state)> clean_up
Definition: game_scene.hpp:174
std::function< void(sys::state &state)> on_game_state_update_update_ui
Definition: game_scene.hpp:177
std::function< void(sys::state &state)> render_map
Definition: game_scene.hpp:165
std::function< ui::mouse_probe(sys::state &state, ui::mouse_probe mouse_probe, ui::mouse_probe tooltip_probe)> recalculate_mouse_probe
Definition: game_scene.hpp:169
std::function< ui::element_base *(sys::state &state)> get_root
Definition: game_scene.hpp:120
std::function< void(sys::state &state)> render_ui
Definition: game_scene.hpp:164
dcon::unit_type_id irregular
Definition: military.hpp:164
tagged_vector< unit_definition, dcon::unit_type_id > unit_base_definitions
Definition: military.hpp:142
dcon::unit_type_id infantry
Definition: military.hpp:165
dcon::unit_type_id artillery
Definition: military.hpp:166
static constexpr uint16_t mode_retreated
Definition: military.hpp:45
static constexpr uint16_t mode_sunk
Definition: military.hpp:46
static constexpr uint16_t mode_mask
Definition: military.hpp:40
std::vector< triggered_modifier > triggered_modifiers
Definition: nations.hpp:71
sys::message_base_type type
GLuint ui_shader_screen_height_uniform
GLuint ui_shader_secondary_texture_sampler_uniform
GLuint ui_shader_screen_width_uniform
GLuint ui_shader_program
GLuint ui_shader_texture_sampler_uniform
GLuint ui_shader_gamma_uniform
std::optional< simple_fs::file > ideologies_file
tagged_vector< std::string, dcon::national_identity_id > file_names_for_idents
std::optional< simple_fs::file > crimes_file
ankerl::unordered_dense::map< std::string, pending_invention_content > map_of_inventions
std::vector< simple_fs::file > tech_and_invention_files
ankerl::unordered_dense::map< std::string, pending_roption_content > map_of_roptions
ankerl::unordered_dense::map< std::string, pending_tech_content > map_of_technologies
ankerl::unordered_dense::map< std::string, dcon::pop_type_id > map_of_poptypes
ankerl::unordered_dense::map< std::string, pending_ideology_content > map_of_ideologies
std::optional< simple_fs::file > issues_file
std::vector< dcon::province_id > original_id_to_prov_id_map
ankerl::unordered_dense::map< std::string, pending_crime_content > map_of_crimes
std::optional< simple_fs::file > rebel_types_file
ankerl::unordered_dense::map< std::string, pending_option_content > map_of_ioptions
ankerl::unordered_dense::map< std::string, pending_cb_content > map_of_cb_types
std::optional< simple_fs::file > cb_types_file
std::vector< pending_triggered_modifier_content > set_of_triggered_modifiers
std::optional< simple_fs::file > triggered_modifiers_file
ankerl::unordered_dense::map< uint32_t, dcon::province_id > map_color_to_province_id
ankerl::unordered_dense::map< std::string, dcon::government_type_id > map_of_governments
void parse_file(sys::state &state, std::string_view data, parsers::error_handler &err)
Definition: defines.cpp:122
dcon::province_id p
dcon::culture_id c
dcon::unit_type_id u
static constexpr uint32_t max_types
Holds important data about the game world, state, and other data regarding windowing,...
text::font_manager font_collection
bool drag_selecting
uint32_t add_locale_data_win1252(std::string const &text)
culture::global_cultural_state culture_definitions
network_mode_type network_mode
std::vector< char > key_data
user_settings_s user_settings
int32_t mouse_x_position
void save_user_settings() const
dcon::text_key add_key_win1252(std::string const &text)
dcon::trigger_key commit_trigger_data(std::vector< uint16_t > data)
std::atomic< bool > ui_pause
std::vector< dcon::army_id > selected_armies
ankerl::unordered_dense::set< dcon::text_key, text::vector_backed_ci_hash, text::vector_backed_ci_eq > untrans_key_to_text_sequence
void on_resize(int32_t x, int32_t y, window::window_state win_state)
std::vector< dcon::navy_id > selected_navies
dcon::data_container world
uint32_t add_locale_data_utf8(std::string const &text)
void on_lbutton_down(int32_t x, int32_t y, key_modifiers mod)
rigtorp::SPSCQueue< event::pending_human_p_event > new_p_event
void army_group_distribute_tasks(dcon::automated_army_group_id group)
std::vector< uint16_t > effect_data
ogl::animation ui_animation
void on_rbutton_down(int32_t x, int32_t y, key_modifiers mod)
dcon::nation_id local_player_nation
std::atomic< bool > game_state_updated
parsing::defines defines
dcon::effect_key commit_effect_data(std::vector< uint16_t > data)
std::vector< char > unit_names
std::vector< int32_t > trigger_data_indices
std::string_view to_string_view(dcon::text_key tag) const
std::string_view locale_string_view(uint32_t tag) const
void on_mouse_wheel(int32_t x, int32_t y, key_modifiers mod, float amount)
void on_create()
std::vector< char > locale_text_data
simple_fs::file_system common_fs
game_scene::scene_properties current_scene
absolute_time_point start_date
absolute_time_point end_date
rigtorp::SPSCQueue< military::land_battle_report > land_battle_reports
int32_t mouse_y_position
void army_group_update_tasks(dcon::automated_army_group_id group)
military::global_military_state military_definitions
rigtorp::SPSCQueue< notification::message > new_messages
std::vector< dcon::ship_id > selected_ships
sys::date current_date
void open_diplomacy(dcon::nation_id target)
dcon::unit_name_id add_unit_name(std::string_view text)
void start_state_selection(state_selection_data &data)
std::vector< dcon::regiment_id > selected_regiments
int32_t x_size
std::vector< int32_t > unit_names_indices
void on_text(char32_t c)
void on_drag_finished(int32_t x, int32_t y, key_modifiers mod)
void on_rbutton_up(int32_t x, int32_t y, key_modifiers mod)
void on_key_up(virtual_key keycode, key_modifiers mod)
void on_mouse_move(int32_t x, int32_t y, key_modifiers mod)
dcon::text_key lookup_key(std::string_view text) const
void load_user_settings()
void on_lbutton_up(int32_t x, int32_t y, key_modifiers mod)
ankerl::unordered_dense::map< dcon::text_key, uint32_t, text::vector_backed_ci_hash, text::vector_backed_ci_eq > locale_key_to_text_sequence
void reset_locale_pool()
void on_mouse_drag(int32_t x, int32_t y, key_modifiers mod)
void update_armies_and_fleets(dcon::automated_army_group_id group)
void on_key_down(virtual_key keycode, key_modifiers mod)
rigtorp::SPSCQueue< event::pending_human_f_p_event > new_f_p_event
iui::iui_state iui_state
ogl::data open_gl
ui::state ui_state
void load_locale_strings(std::string_view locale_name)
map::map_state map_state
void on_mbutton_up(int32_t x, int32_t y, key_modifiers mod)
int32_t autosave_counter
void army_group_update_regiment_status(dcon::automated_army_group_id group)
rigtorp::SPSCQueue< diplomatic_message::message > new_requests
rigtorp::SPSCQueue< event::pending_human_n_event > new_n_event
void load_scenario_data(parsers::error_handler &err, sys::year_month_day bookmark_date)
int32_t y_size
void on_mbutton_down(int32_t x, int32_t y, key_modifiers mod)
dcon::text_key add_key_utf8(std::string const &text)
bool key_is_localized(dcon::text_key tag) const
std::vector< int32_t > effect_data_indices
ui::definitions ui_defs
rigtorp::SPSCQueue< military::naval_battle_report > naval_battle_reports
void state_select(dcon::state_definition_id sdef)
std::vector< uint16_t > trigger_data
void update_ui_scale(float new_scale)
std::optional< state_selection_data > state_selection
rigtorp::SPSCQueue< event::pending_human_f_n_event > new_f_n_event
std::atomic< bool > province_ownership_changed
nations::global_national_state national_definitions
uint8_t interesting_message_settings[int32_t(sys::message_setting_type::count)]
uint8_t self_message_settings[int32_t(sys::message_setting_type::count)]
map_label_mode map_label
uint8_t other_message_settings[int32_t(sys::message_setting_type::count)]
element_base * under_mouse
std::unique_ptr< element_base > army_group_selector_root
element_base * map_rr_legend
element_base * msg_log_window
element_base * menubar_window
int32_t last_tooltip_sub_index
element_base * map_rec_legend
std::unique_ptr< element_base > end_screen
std::chrono::time_point< std::chrono::steady_clock > last_render_time
element_base * chat_window
std::unique_ptr< element_base > nation_picker
element_base * multi_unit_selection_window
element_base * last_tooltip
std::unique_ptr< grid_box > unit_details_box
uint16_t default_body_font
uint32_t cursor_size
uint16_t scrollbar_timer
std::unique_ptr< element_base > province_details_root
element_base * map_nav_legend
std::unique_ptr< tool_tip > tooltip
std::unique_ptr< element_base > rgos_root
bool shift_held_down
std::unique_ptr< element_base > select_states_legend
xy_pair relative_mouse_location
element_base * topbar_subwindow
element_base * change_leader_window
element_base * r_chat_window
unit_details_window< dcon::army_id > * army_status_window
unit_details_window< dcon::navy_id > * navy_status_window
ankerl::unordered_dense::map< dcon::text_key, element_target, hash_text_key > defs_by_name
std::unique_ptr< element_base > root
element_base * edit_target
element_base * map_civ_level_legend
ankerl::unordered_dense::map< std::string, sys::aui_pending_bytes > new_ui_windows
uint16_t default_header_font
element_base * left_mouse_hold_target
std::vector< std::unique_ptr< element_base > > endof_navalcombat_windows
std::vector< simple_fs::file > held_open_ui_files
element_base * request_window
element_base * request_topbar_listbox
element_base * console_window
element_base * army_group_window_land
element_base * map_col_legend
bool lazy_load_in_game
uint16_t fps_timer
bool scrollbar_continuous_movement
element_base * map_rank_legend
bool ctrl_held_down
element_base * tl_chat_list
float last_fps
uint16_t tooltip_font
std::vector< dcon::technology_id > tech_queue
element_base * outliner_window
std::unique_ptr< element_base > economy_viewer_root
dcon::gfx_object_id bg_gfx_id
element_base * mouse_sensitive_target
element_base * map_dip_legend
element_base * diplomacy_subwindow
std::unique_ptr< element_base > military_root
element_base * drag_target
element_base * under_mouse
element_base * msg_window
element_base * scroll_target
xy_pair target_lr_bounds
element_base * map_gradient_legend
element_base * console_window_r
std::unique_ptr< element_base > units_root
xy_pair top_left
xy_pair size
static constexpr uint8_t is_moveable_mask
#define US_SAVE(x)
#define US_LOAD(x)