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