diff --git a/assets/alice.csv b/assets/alice.csv index 8ed4df908..a9a6345fa 100644 --- a/assets/alice.csv +++ b/assets/alice.csv @@ -1365,6 +1365,20 @@ aml_civ_s2;Uncolonized aml_civ_s3;100% Westernized aml_civ_s4;0% Westernized aml_civ_s5;Colony +macro_builder;Macro-builder;;;Macro-constructor +macro_total_desc;Total unit stats: +macro_remove_template;Delete +macro_switch_type_land;Switch to: Land +macro_switch_type_naval;Switch to: Naval +macro_save_template;Save template;;;Guardar plantilla +macro_new_template;New template;;;Nueva plantila +macro_warn_overseas;§RCan't build this unit overseas!§W +macro_warn_culture;§RCan only build using primary culture POPs!§W +macro_warn_unlocked;§RUsing units not unlocked yet!§W +macro_select_province;Please select a province on the map +macro_warn_insuff;§RCan't build $x$ $name$ (only $y$)§W +macro_warn_invalid_province;§RProvince not part of a region§W +macro_apply_template;Apply aml_col_s1;Potential aml_col_s2;NA aml_col_s3;Can Invest diff --git a/assets/alice.gui b/assets/alice.gui index 569cb3390..fdeac9616 100644 --- a/assets/alice.gui +++ b/assets/alice.gui @@ -3560,23 +3560,143 @@ guiTypes = { windowType = { name = "alice_macro_builder" - position = { 0 200 } - size = { 320 320 } + position = { 420 400 } + size = { 640 320 } guiButtonType = { name = "background" position = { 0 0 } - size = { 320 320 } + size = { 640 320 } quadTextureSprite = "console_background" } + guiButtonType = { + name = "close" + position = { -32 0 } + orientation = "UPPER_RIGHT" + quadTextureSprite = "GFX_main_close_button" + } instantTextBoxType = { name = "title" - text = "Macro builder" + text = "macro_builder" position = { 0 4 } font = "vic_22" borderSize = { 0 0 } - maxsize = { 320 24 } - orientation = "UPPER_LEFT" + maxsize = { 640 24 } + format = center + } + listboxType = { + name = "template_listbox" + position = { 0 28 } + size = { 105 240 } + spacing = 0 + borderSize = { 0 0 } + } + listboxType = { + name = "unit_listbox" + position = { 108 28 } + size = { 210 240 } + spacing = 0 + borderSize = { 0 0 } + } + guiButtonType = { + name = "new_template" + position = { 2 288 } + size = { 128 24 } + quadTextureSprite = "GFX_button_128wide" + buttonText = "macro_new_template" + buttonFont = "vic_22_black" + } + guiButtonType = { + name = "save_template" + position = { 130 288 } + size = { 128 24 } + quadTextureSprite = "GFX_button_128wide" + buttonText = "macro_save_template" + buttonFont = "vic_22_black" + } + guiButtonType = { + name = "remove_template" + position = { 260 288 } + size = { 128 24 } + quadTextureSprite = "GFX_button_128wide" + buttonText = "macro_remove_template" + buttonFont = "vic_22_black" + } + guiButtonType = { + name = "switch_type" + position = { 390 288 } + size = { 128 24 } + quadTextureSprite = "GFX_button_128wide" + buttonText = "" + buttonFont = "vic_22_black" + } + guiButtonType = { + name = "apply" + position = { 520 288 } + size = { 128 24 } + quadTextureSprite = "GFX_button_128wide" + buttonText = "macro_apply_template" + buttonFont = "vic_22_black" + } + editBoxType = { + name = "input" + position = { 330 28 } + textureFile = "gfx\\interface\\small_tiles_dialog.dds" + font = "FPS_Font" + borderSize = { 10 10 } + maxsize = { 308 40 } + format = center + } + instantTextBoxType = { + name = "details" + position = { 330 72 } + font = "vic_22" + borderSize = { 0 0 } + maxsize = { 288 172 } + format = center + } + } + windowType = { + name = "alice_macro_builder_template_entry" + position = { 0 0 } + size = { 105 24 } + guiButtonType = { + name = "background" + position = { 0 0 } + size = { 105 24 } + quadTextureSprite = "console_background" + } + instantTextBoxType = { + name = "name" + position = { 32 0 } + font = "vic_22" + borderSize = { 0 0 } + maxsize = { 70 24 } format = center } + guiButtonType = { + name = "shield" + position = { 4 4 } + quadTextureSprite = "GFX_flag_new" + Orientation = "UPPER_LEFT" + } + } + windowType = { + name = "alice_macro_builder_unit_entry" + position = { 0 0 } + size = { 210 24 } + guiButtonType = { + name = "background" + position = { 0 0 } + size = { 210 24 } + quadTextureSprite = "console_background" + } + instantTextBoxType = { + name = "name" + position = { 4 0 } + font = "vic_22" + borderSize = { 0 0 } + maxsize = { 128 24 } + format = left + } } } diff --git a/src/common_types/container_types.hpp b/src/common_types/container_types.hpp index d3c9c5767..920faef6a 100644 --- a/src/common_types/container_types.hpp +++ b/src/common_types/container_types.hpp @@ -296,4 +296,16 @@ struct player_name { }; static_assert(sizeof(player_name) == sizeof(player_name::data)); +struct macro_builder_template { + static constexpr uint32_t max_types = 48; + sys::checksum_key scenario_checksum; + dcon::nation_id source; + char name[8] = { 0 }; + uint8_t amounts[max_types] = { 0 }; + + bool operator!=(macro_builder_template& o) { + return std::memcmp(this, &o, sizeof(*this)); + } +}; + } // namespace sys diff --git a/src/culture/rebels.cpp b/src/culture/rebels.cpp index f961dc320..6e97e93a4 100644 --- a/src/culture/rebels.cpp +++ b/src/culture/rebels.cpp @@ -789,8 +789,8 @@ void sort_hunting_targets(sys::state& state, dcon::army_id ar, std::vector(ai::estimate_army_strength(state, a), 1.f); - auto bs = 0.02f * std::max(ai::estimate_army_strength(state, b), 1.f); + auto as = 0.001f * std::max(ai::estimate_army_strength(state, a), 1.f); + auto bs = 0.001f * std::max(ai::estimate_army_strength(state, b), 1.f); auto da = province::sorting_distance(state, pa, closest_prov) + as; auto db = province::sorting_distance(state, pb, closest_prov) + bs; if(da != db) diff --git a/src/filesystem/simple_fs.hpp b/src/filesystem/simple_fs.hpp index fad3e71c0..085780c87 100644 --- a/src/filesystem/simple_fs.hpp +++ b/src/filesystem/simple_fs.hpp @@ -74,6 +74,7 @@ native_string get_full_name(file const& f); // functions that operate outside of a filesystem object directory get_or_create_save_game_directory(); +directory get_or_create_templates_directory(); directory get_or_create_oos_directory(); directory get_or_create_scenario_directory(); directory get_or_create_settings_directory(); diff --git a/src/filesystem/simple_fs_nix.cpp b/src/filesystem/simple_fs_nix.cpp index 0777d8926..bb2808462 100644 --- a/src/filesystem/simple_fs_nix.cpp +++ b/src/filesystem/simple_fs_nix.cpp @@ -472,6 +472,13 @@ directory get_or_create_save_game_directory() { return directory(nullptr, path); } +directory get_or_create_templates_directory() { + native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/templates/"; + make_directories(path); + + return directory(nullptr, path); +} + directory get_or_create_oos_directory() { native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/oos/"; make_directories(path); diff --git a/src/filesystem/simple_fs_win.cpp b/src/filesystem/simple_fs_win.cpp index 23d6ce3d8..95dd5ca76 100644 --- a/src/filesystem/simple_fs_win.cpp +++ b/src/filesystem/simple_fs_win.cpp @@ -382,6 +382,21 @@ directory get_or_create_save_game_directory() { return directory(nullptr, base_path); } +directory get_or_create_templates_directory() { + wchar_t* local_path_out = nullptr; + std::wstring base_path; + if(SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &local_path_out) == S_OK) { + base_path = std::wstring(local_path_out) + NATIVE("\\Project Alice"); + } + CoTaskMemFree(local_path_out); + if(base_path.length() > 0) { + CreateDirectoryW(base_path.c_str(), nullptr); + base_path += NATIVE("\\templates"); + CreateDirectoryW(base_path.c_str(), nullptr); + } + return directory(nullptr, base_path); +} + directory get_or_create_oos_directory() { wchar_t* local_path_out = nullptr; std::wstring base_path; diff --git a/src/gamestate/commands.cpp b/src/gamestate/commands.cpp index e309e349a..ac2e13dc3 100644 --- a/src/gamestate/commands.cpp +++ b/src/gamestate/commands.cpp @@ -589,17 +589,18 @@ void execute_begin_factory_building_construction(sys::state& state, dcon::nation } } -void start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type) { +void start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province) { payload p; memset(&p, 0, sizeof(payload)); p.type = command_type::begin_naval_unit_construction; p.source = source; p.data.naval_unit_construction.location = location; p.data.naval_unit_construction.type = type; + p.data.naval_unit_construction.template_province = template_province; add_to_command_queue(state, p); } -bool can_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type) { +bool can_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province) { /* The province must be owned and controlled by the building nation, without an ongoing siege. The unit type must be available from start / unlocked by the nation @@ -641,12 +642,13 @@ bool can_start_naval_unit_construction(sys::state& state, dcon::nation_id source } } -void execute_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type) { +void execute_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province) { auto c = fatten(state.world, state.world.try_create_province_naval_construction(location, source)); c.set_type(type); + c.set_template_province(template_province); } -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) { +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) { payload p; memset(&p, 0, sizeof(payload)); p.type = command_type::begin_land_unit_construction; @@ -654,9 +656,10 @@ void start_land_unit_construction(sys::state& state, dcon::nation_id source, dco p.data.land_unit_construction.location = location; p.data.land_unit_construction.type = type; p.data.land_unit_construction.pop_culture = soldier_culture; + p.data.land_unit_construction.template_province = template_province; add_to_command_queue(state, p); } -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) { +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) { /* The province must be owned and controlled by the building nation, without an ongoing siege. The unit type must be available from start / unlocked by the nation @@ -687,11 +690,12 @@ bool can_start_land_unit_construction(sys::state& state, dcon::nation_id source, return false; } } -void execute_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) { +void execute_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) { auto soldier = military::find_available_soldier(state, location, soldier_culture); auto c = fatten(state.world, state.world.try_create_province_land_construction(soldier, source)); c.set_type(type); + c.set_template_province(template_province); } void cancel_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type) { @@ -4457,7 +4461,7 @@ bool can_perform_command(sys::state& state, payload& c) { case command_type::begin_naval_unit_construction: return can_start_naval_unit_construction(state, c.source, c.data.naval_unit_construction.location, - c.data.naval_unit_construction.type); + c.data.naval_unit_construction.type, c.data.naval_unit_construction.template_province); case command_type::cancel_naval_unit_construction: return can_cancel_naval_unit_construction(state, c.source, c.data.naval_unit_construction.location, @@ -4465,7 +4469,7 @@ bool can_perform_command(sys::state& state, payload& c) { case command_type::begin_land_unit_construction: return can_start_land_unit_construction(state, c.source, c.data.land_unit_construction.location, - c.data.land_unit_construction.pop_culture, c.data.land_unit_construction.type); + c.data.land_unit_construction.pop_culture, c.data.land_unit_construction.type, c.data.land_unit_construction.template_province); case command_type::cancel_land_unit_construction: return can_cancel_land_unit_construction(state, c.source, c.data.land_unit_construction.location, @@ -4817,7 +4821,7 @@ void execute_command(sys::state& state, payload& c) { break; case command_type::begin_naval_unit_construction: execute_start_naval_unit_construction(state, c.source, c.data.naval_unit_construction.location, - c.data.naval_unit_construction.type); + c.data.naval_unit_construction.type, c.data.naval_unit_construction.template_province); break; case command_type::cancel_naval_unit_construction: execute_cancel_naval_unit_construction(state, c.source, c.data.naval_unit_construction.location, @@ -4825,7 +4829,7 @@ void execute_command(sys::state& state, payload& c) { break; case command_type::begin_land_unit_construction: execute_start_land_unit_construction(state, c.source, c.data.land_unit_construction.location, - c.data.land_unit_construction.pop_culture, c.data.land_unit_construction.type); + c.data.land_unit_construction.pop_culture, c.data.land_unit_construction.type, c.data.land_unit_construction.template_province); break; case command_type::cancel_land_unit_construction: execute_cancel_land_unit_construction(state, c.source, c.data.land_unit_construction.location, diff --git a/src/gamestate/commands.hpp b/src/gamestate/commands.hpp index 0a9fdbdad..1028bcd48 100644 --- a/src/gamestate/commands.hpp +++ b/src/gamestate/commands.hpp @@ -173,6 +173,7 @@ struct diplo_action_data { struct naval_unit_construction_data { dcon::province_id location; dcon::unit_type_id type; + dcon::province_id template_province; }; struct rally_point_data { @@ -185,6 +186,7 @@ struct land_unit_construction_data { dcon::province_id location; dcon::culture_id pop_culture; dcon::unit_type_id type; + dcon::province_id template_province; }; struct factory_data { @@ -534,11 +536,11 @@ bool can_begin_factory_building_construction(sys::state& state, dcon::nation_id void cancel_factory_building_construction(sys::state& state, dcon::nation_id source, dcon::state_instance_id location, dcon::factory_type_id type); bool can_cancel_factory_building_construction(sys::state& state, dcon::nation_id source, dcon::state_instance_id location, dcon::factory_type_id type); -void start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type); -bool can_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type); +void start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province = dcon::province_id{}); +bool can_start_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type, dcon::province_id template_province = dcon::province_id{}); -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); -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); +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 = dcon::province_id{}); +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 = dcon::province_id{}); void cancel_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type); bool can_cancel_naval_unit_construction(sys::state& state, dcon::nation_id source, dcon::province_id location, dcon::unit_type_id type); diff --git a/src/gui/gui_element_types.cpp b/src/gui/gui_element_types.cpp index 0ca0a3a19..ef462455d 100644 --- a/src/gui/gui_element_types.cpp +++ b/src/gui/gui_element_types.cpp @@ -2134,4 +2134,163 @@ void unit_frame_bg::update_tooltip(sys::state& state, int32_t x, int32_t y, text single_unit_tooltip(state, contents, std::get(display_unit)); } +void populate_shortcut_tooltip(sys::state& state, ui::element_base& elm, text::columnar_layout& contents) noexcept { + if(elm.base_data.get_element_type() != ui::element_type::button) + return; + if(elm.base_data.data.button.shortcut == sys::virtual_key::NONE) + return; + static const std::string_view key_names[] = { //enum class virtual_key : uint8_t { + "", //NONE = 0x00, + "Left-Button", //LBUTTON = 0x01, + "Right-Button", //RBUTTON = 0x02, + "Cancel", //CANCEL = 0x03, + "Multimedia button", //MBUTTON = 0x04, + "XButton1", //XBUTTON_1 = 0x05, + "XButton2", //XBUTTON_2 = 0x06, + "Backspace", //BACK = 0x08, + "TAB", //TAB = 0x09, + "Clear", //CLEAR = 0x0C, + "Return", //RETURN = 0x0D, + "Shift", //SHIFT = 0x10, + "Control", //CONTROL = 0x11, + "Menu", //MENU = 0x12, + "Pause", //PAUSE = 0x13, + "Capital", //CAPITAL = 0x14, + "Kana", //KANA = 0x15, + "Junja", //JUNJA = 0x17, + "Final", //FINAL = 0x18, + "Kanji", //KANJI = 0x19, + "Escape", //ESCAPE = 0x1B, + "Convert", //CONVERT = 0x1C, + "Nonconvert", //NONCONVERT = 0x1D, + "Accept", //ACCEPT = 0x1E, + "Modechange", //MODECHANGE = 0x1F, + "Spacebar", //SPACE = 0x20, + "Prior", //PRIOR = 0x21, + "Next", //NEXT = 0x22, + "End", //END = 0x23, + "Home", //HOME = 0x24, + "Left", //LEFT = 0x25, + "Up", //UP = 0x26, + "Right", //RIGHT = 0x27, + "Down", //DOWN = 0x28, + "Select", //SELECT = 0x29, + "Print", //PRINT = 0x2A, + "Execute", //EXECUTE = 0x2B, + "Snapshot", //SNAPSHOT = 0x2C, + "Insert", //INSERT = 0x2D, + "Delete", //DELETE_KEY = 0x2E, + "Help", //HELP = 0x2F, + "0", //NUM_0 = 0x30, + "1", //NUM_1 = 0x31, + "2", //NUM_2 = 0x32, + "3", //NUM_3 = 0x33, + "4", //NUM_4 = 0x34, + "5", //NUM_5 = 0x35, + "6", //NUM_6 = 0x36, + "7", //NUM_7 = 0x37, + "8", //NUM_8 = 0x38, + "9", //NUM_9 = 0x39, + "A", //A = 0x41, + "B", //B = 0x42, + "C", //C = 0x43, + "D", //D = 0x44, + "E", //E = 0x45, + "F", //F = 0x46, + "G", //G = 0x47, + "H", //H = 0x48, + "I", //I = 0x49, + "J", //J = 0x4A, + "K", //K = 0x4B, + "L", //L = 0x4C, + "M", //M = 0x4D, + "N", //N = 0x4E, + "O", //O = 0x4F, + "P", //P = 0x50, + "Q", //Q = 0x51, + "R", //R = 0x52, + "S", //S = 0x53, + "T", //T = 0x54, + "U", //U = 0x55, + "V", //V = 0x56, + "W", //W = 0x57, + "X", //X = 0x58, + "Y", //Y = 0x59, + "Z", //Z = 0x5A, + "Left Windows", //LWIN = 0x5B, + "Right Windows", //RWIN = 0x5C, + "Apps", //APPS = 0x5D, + "Sleep", //SLEEP = 0x5F, + "Numpad 0", //NUMPAD0 = 0x60, + "Numpad 1", //NUMPAD1 = 0x61, + "Numpad 2", //NUMPAD2 = 0x62, + "Numpad 3", //NUMPAD3 = 0x63, + "Numpad 4", //NUMPAD4 = 0x64, + "Numpad 5", //NUMPAD5 = 0x65, + "Numpad 6", //NUMPAD6 = 0x66, + "Numpad 7", //NUMPAD7 = 0x67, + "Numpad 8", //NUMPAD8 = 0x68, + "Numpad 9", //NUMPAD9 = 0x69, + "Numpad *", //MULTIPLY = 0x6A, + "Numpad +", //ADD = 0x6B, + "Numpad .", //SEPARATOR = 0x6C, + "Numpad -", //SUBTRACT = 0x6D, + "Numpad .", //DECIMAL = 0x6E, + "Numpad /", //DIVIDE = 0x6F, + "F1", //F1 = 0x70, + "F2", //F2 = 0x71, + "F3", //F3 = 0x72, + "F4", //F4 = 0x73, + "F5", //F5 = 0x74, + "F6", //F6 = 0x75, + "F7", //F7 = 0x76, + "F8", //F8 = 0x77, + "F9", //F9 = 0x78, + "F10", //F10 = 0x79, + "F11", //F11 = 0x7A, + "F12", //F12 = 0x7B, + "F13", //F13 = 0x7C, + "F14", //F14 = 0x7D, + "F15", //F15 = 0x7E, + "F16", //F16 = 0x7F, + "F17", //F17 = 0x80, + "F18", //F18 = 0x81, + "F19", //F19 = 0x82, + "F20", //F20 = 0x83, + "F21", //F21 = 0x84, + "F22", //F22 = 0x85, + "F23", //F23 = 0x86, + "F24", //F24 = 0x87, + "Navigation View", //NAVIGATION_VIEW = 0x88, + "Navigation Menu", //NAVIGATION_MENU = 0x89, + "Navigation Up", //NAVIGATION_UP = 0x8A, + "Navigation Down", //NAVIGATION_DOWN = 0x8B, + "Navigation Left", //NAVIGATION_LEFT = 0x8C, + "Navigation Right", //NAVIGATION_RIGHT = 0x8D, + "Navigation Accept", //NAVIGATION_ACCEPT = 0x8E, + "Navigation Cancel", //NAVIGATION_CANCEL = 0x8F, + "Numlock", //NUMLOCK = 0x90, + "Scroll lock", //SCROLL = 0x91, + "=", //OEM_NEC_EQUAL = 0x92, + "Left Shift", //LSHIFT = 0xA0, + "Right Shift", //RSHIFT = 0xA1, + "Left Control", //LCONTROL = 0xA2, + "Right Control", //RCONTROL = 0xA3, + "Left Menu", //LMENU = 0xA4, + "Right Menu", //RMENU = 0xA5, + ";", //SEMICOLON = 0xBA, + "+", //PLUS = 0xBB, + ",", //COMMA = 0xBC, + "-", //MINUS = 0xBD, + ".", //PERIOD = 0xBE, + "\\", //FORWARD_SLASH = 0xBF, + "~", //TILDA = 0xC0, + "[", //OPEN_BRACKET = 0xDB, + "/", //BACK_SLASH = 0xDC, + "]", //CLOSED_BRACKET = 0xDD, + "\"", //QUOTE = 0xDE + }; + text::add_line(state, contents, "alice_shortcut_tooltip", text::variable_type::x, key_names[uint8_t(elm.base_data.data.button.shortcut)]); +} + } // namespace ui diff --git a/src/gui/gui_element_types.hpp b/src/gui/gui_element_types.hpp index 212946a34..73b02cb7f 100644 --- a/src/gui/gui_element_types.hpp +++ b/src/gui/gui_element_types.hpp @@ -1703,163 +1703,7 @@ class grid_box : public window_element_base { set_visible(state, true); } }; -inline void populate_shortcut_tooltip(sys::state& state, ui::element_base& elm, text::columnar_layout& contents) noexcept { - if(elm.base_data.get_element_type() != ui::element_type::button) - return; - if(elm.base_data.data.button.shortcut == sys::virtual_key::NONE) - return; - static const std::string_view key_names[] = { //enum class virtual_key : uint8_t { - "", //NONE = 0x00, - "Left-Button", //LBUTTON = 0x01, - "Right-Button", //RBUTTON = 0x02, - "Cancel", //CANCEL = 0x03, - "Multimedia button", //MBUTTON = 0x04, - "XButton1", //XBUTTON_1 = 0x05, - "XButton2", //XBUTTON_2 = 0x06, - "Backspace", //BACK = 0x08, - "TAB", //TAB = 0x09, - "Clear", //CLEAR = 0x0C, - "Return", //RETURN = 0x0D, - "Shift", //SHIFT = 0x10, - "Control", //CONTROL = 0x11, - "Menu", //MENU = 0x12, - "Pause", //PAUSE = 0x13, - "Capital", //CAPITAL = 0x14, - "Kana", //KANA = 0x15, - "Junja", //JUNJA = 0x17, - "Final", //FINAL = 0x18, - "Kanji", //KANJI = 0x19, - "Escape", //ESCAPE = 0x1B, - "Convert", //CONVERT = 0x1C, - "Nonconvert", //NONCONVERT = 0x1D, - "Accept", //ACCEPT = 0x1E, - "Modechange", //MODECHANGE = 0x1F, - "Spacebar", //SPACE = 0x20, - "Prior", //PRIOR = 0x21, - "Next", //NEXT = 0x22, - "End", //END = 0x23, - "Home", //HOME = 0x24, - "Left", //LEFT = 0x25, - "Up", //UP = 0x26, - "Right", //RIGHT = 0x27, - "Down", //DOWN = 0x28, - "Select", //SELECT = 0x29, - "Print", //PRINT = 0x2A, - "Execute", //EXECUTE = 0x2B, - "Snapshot", //SNAPSHOT = 0x2C, - "Insert", //INSERT = 0x2D, - "Delete", //DELETE_KEY = 0x2E, - "Help", //HELP = 0x2F, - "0", //NUM_0 = 0x30, - "1", //NUM_1 = 0x31, - "2", //NUM_2 = 0x32, - "3", //NUM_3 = 0x33, - "4", //NUM_4 = 0x34, - "5", //NUM_5 = 0x35, - "6", //NUM_6 = 0x36, - "7", //NUM_7 = 0x37, - "8", //NUM_8 = 0x38, - "9", //NUM_9 = 0x39, - "A", //A = 0x41, - "B", //B = 0x42, - "C", //C = 0x43, - "D", //D = 0x44, - "E", //E = 0x45, - "F", //F = 0x46, - "G", //G = 0x47, - "H", //H = 0x48, - "I", //I = 0x49, - "J", //J = 0x4A, - "K", //K = 0x4B, - "L", //L = 0x4C, - "M", //M = 0x4D, - "N", //N = 0x4E, - "O", //O = 0x4F, - "P", //P = 0x50, - "Q", //Q = 0x51, - "R", //R = 0x52, - "S", //S = 0x53, - "T", //T = 0x54, - "U", //U = 0x55, - "V", //V = 0x56, - "W", //W = 0x57, - "X", //X = 0x58, - "Y", //Y = 0x59, - "Z", //Z = 0x5A, - "Left Windows", //LWIN = 0x5B, - "Right Windows", //RWIN = 0x5C, - "Apps", //APPS = 0x5D, - "Sleep", //SLEEP = 0x5F, - "Numpad 0", //NUMPAD0 = 0x60, - "Numpad 1", //NUMPAD1 = 0x61, - "Numpad 2", //NUMPAD2 = 0x62, - "Numpad 3", //NUMPAD3 = 0x63, - "Numpad 4", //NUMPAD4 = 0x64, - "Numpad 5", //NUMPAD5 = 0x65, - "Numpad 6", //NUMPAD6 = 0x66, - "Numpad 7", //NUMPAD7 = 0x67, - "Numpad 8", //NUMPAD8 = 0x68, - "Numpad 9", //NUMPAD9 = 0x69, - "Numpad *", //MULTIPLY = 0x6A, - "Numpad +", //ADD = 0x6B, - "Numpad .", //SEPARATOR = 0x6C, - "Numpad -", //SUBTRACT = 0x6D, - "Numpad .", //DECIMAL = 0x6E, - "Numpad /", //DIVIDE = 0x6F, - "F1", //F1 = 0x70, - "F2", //F2 = 0x71, - "F3", //F3 = 0x72, - "F4", //F4 = 0x73, - "F5", //F5 = 0x74, - "F6", //F6 = 0x75, - "F7", //F7 = 0x76, - "F8", //F8 = 0x77, - "F9", //F9 = 0x78, - "F10", //F10 = 0x79, - "F11", //F11 = 0x7A, - "F12", //F12 = 0x7B, - "F13", //F13 = 0x7C, - "F14", //F14 = 0x7D, - "F15", //F15 = 0x7E, - "F16", //F16 = 0x7F, - "F17", //F17 = 0x80, - "F18", //F18 = 0x81, - "F19", //F19 = 0x82, - "F20", //F20 = 0x83, - "F21", //F21 = 0x84, - "F22", //F22 = 0x85, - "F23", //F23 = 0x86, - "F24", //F24 = 0x87, - "Navigation View", //NAVIGATION_VIEW = 0x88, - "Navigation Menu", //NAVIGATION_MENU = 0x89, - "Navigation Up", //NAVIGATION_UP = 0x8A, - "Navigation Down", //NAVIGATION_DOWN = 0x8B, - "Navigation Left", //NAVIGATION_LEFT = 0x8C, - "Navigation Right", //NAVIGATION_RIGHT = 0x8D, - "Navigation Accept", //NAVIGATION_ACCEPT = 0x8E, - "Navigation Cancel", //NAVIGATION_CANCEL = 0x8F, - "Numlock", //NUMLOCK = 0x90, - "Scroll lock", //SCROLL = 0x91, - "=", //OEM_NEC_EQUAL = 0x92, - "Left Shift", //LSHIFT = 0xA0, - "Right Shift", //RSHIFT = 0xA1, - "Left Control", //LCONTROL = 0xA2, - "Right Control", //RCONTROL = 0xA3, - "Left Menu", //LMENU = 0xA4, - "Right Menu", //RMENU = 0xA5, - ";", //SEMICOLON = 0xBA, - "+", //PLUS = 0xBB, - ",", //COMMA = 0xBC, - "-", //MINUS = 0xBD, - ".", //PERIOD = 0xBE, - "\\", //FORWARD_SLASH = 0xBF, - "~", //TILDA = 0xC0, - "[", //OPEN_BRACKET = 0xDB, - "/", //BACK_SLASH = 0xDC, - "]", //CLOSED_BRACKET = 0xDD, - "\"", //QUOTE = 0xDE - }; - text::add_line(state, contents, "alice_shortcut_tooltip", text::variable_type::x, key_names[uint8_t(elm.base_data.data.button.shortcut)]); -} + +void populate_shortcut_tooltip(sys::state& state, ui::element_base& elm, text::columnar_layout& contents) noexcept; } // namespace ui diff --git a/src/gui/gui_graphics.hpp b/src/gui/gui_graphics.hpp index 0d4bd298a..cb91e7653 100644 --- a/src/gui/gui_graphics.hpp +++ b/src/gui/gui_graphics.hpp @@ -439,8 +439,11 @@ struct state { std::vector> endof_landcombat_windows; std::vector> endof_navalcombat_windows; - int32_t held_game_speed = 1; // used to keep track of speed while paused + element_base* macro_builder_window = nullptr; + int32_t held_game_speed = 1; // used to keep track of speed while paused + sys::macro_builder_template current_template; // used as the currently edited template + std::vector templates; uint16_t tooltip_font = 0; bool ctrl_held_down = false; diff --git a/src/gui/gui_minimap.hpp b/src/gui/gui_minimap.hpp index 5a328864c..ac8fa5727 100644 --- a/src/gui/gui_minimap.hpp +++ b/src/gui/gui_minimap.hpp @@ -162,6 +162,571 @@ class minimap_ledger_button : public button_element_base { } }; +class macro_builder_template_name : public simple_text_element_base { +public: + void on_update(sys::state& state) noexcept override { + auto index = retrieve(state, parent); + auto const& name = state.ui_state.templates[index].name; + auto sv = std::string_view(name, name + sizeof(name)); + set_text(state, std::string(sv)); + } +}; +class macro_builder_template_flag : public flag_button { +public: + void on_update(sys::state& state) noexcept override { + auto index = retrieve(state, parent); + auto nid = state.ui_state.templates[index].source; + set_current_nation(state, state.world.nation_get_identity_from_identity_holder(nid)); + } +}; +struct notify_template_select {}; +class macro_builder_template_select : public button_element_base { +public: + void button_action(sys::state& state) noexcept override { + auto index = retrieve(state, parent); + if(index >= uint32_t(state.ui_state.templates.size())) + return; + std::memcpy(&state.ui_state.current_template, &state.ui_state.templates[index], sizeof(sys::macro_builder_template)); + send(state, parent, notify_template_select{}); + } +}; +class macro_builder_template_entry : public listbox_row_element_base { +public: + std::unique_ptr make_child(sys::state& state, std::string_view name, dcon::gui_def_id id) noexcept override { + if(name == "name") { + return make_element_by_type(state, id); + } else if(name == "shield") { + return make_element_by_type(state, id); + } else if(name == "background") { + return make_element_by_type(state, id); + } else { + return nullptr; + } + } +}; +class macro_builder_template_listbox : public listbox_element_base { +protected: + std::string_view get_row_element_name() override { + return "alice_macro_builder_template_entry"; + } +public: + void on_create(sys::state& state) noexcept override { + listbox_element_base::on_create(state); + auto sdir = simple_fs::get_or_create_templates_directory(); + auto f = simple_fs::open_file(sdir, state.loaded_scenario_file); + if(f) { + auto contents = simple_fs::view_contents(*f); + if(contents.file_size > 0) { + uint32_t num_templates = contents.file_size / sizeof(sys::macro_builder_template); + //Corruption protection + if(num_templates >= 8192 * 4) + num_templates = 8192 * 4; + state.ui_state.templates.resize(num_templates); + std::memcpy(state.ui_state.templates.data(), contents.data, num_templates * sizeof(sys::macro_builder_template)); + } + } + } + void on_update(sys::state& state) noexcept override { + row_contents.resize(state.ui_state.templates.size(), 0); + for(uint32_t i = 0; i < uint32_t(state.ui_state.templates.size()); i++) + row_contents[i] = i; + update(state); + } +}; + +class macro_builder_unit_name : public simple_text_element_base { +public: + void on_update(sys::state& state) noexcept override { + auto content = retrieve(state, parent); + auto name = text::produce_simple_string(state, state.military_definitions.unit_base_definitions[content].name); + int32_t amount = state.ui_state.current_template.amounts[content.index()]; + set_text(state, "(" + std::to_string(amount) + ") " + name); + } +}; +class macro_builder_unit_button : public right_click_button_element_base { +public: + void button_action(sys::state& state) noexcept override { + auto content = retrieve(state, parent); + if(state.ui_state.current_template.amounts[content.index()] < 255) { + state.ui_state.current_template.amounts[content.index()] += 1; + } + send(state, parent, notify_setting_update{}); + } + void button_right_action(sys::state& state) noexcept override { + auto content = retrieve(state, parent); + if(state.ui_state.current_template.amounts[content.index()] > 0) { + state.ui_state.current_template.amounts[content.index()] -= 1; + } + send(state, parent, notify_setting_update{}); + } +}; +class macro_builder_unit_entry : public listbox_row_element_base { +public: + std::unique_ptr make_child(sys::state& state, std::string_view name, dcon::gui_def_id id) noexcept override { + if(name == "name") { + return make_element_by_type(state, id); + } else if(name == "background") { + return make_element_by_type(state, id); + } else { + return nullptr; + } + } +}; +class macro_builder_unit_listbox : public listbox_element_base { +protected: + std::string_view get_row_element_name() override { + return "alice_macro_builder_unit_entry"; + } +public: + void on_update(sys::state& state) noexcept override { + row_contents.clear(); + bool is_land = retrieve(state, parent); + for(dcon::unit_type_id::value_base_t i = 0; i < state.military_definitions.unit_base_definitions.size(); i++) { + if(state.military_definitions.unit_base_definitions[dcon::unit_type_id(i)].is_land == is_land) { + row_contents.push_back(dcon::unit_type_id(i)); + } + } + update(state); + } +}; +class macro_builder_new_template_button : public button_element_base { +public: + void button_action(sys::state& state) noexcept override { + state.ui_state.current_template = sys::macro_builder_template{}; + std::memset(state.ui_state.current_template.name, ' ', sizeof(sys::macro_builder_template::name)); + send(state, parent, notify_setting_update{}); + } +}; +class macro_builder_save_template_button : public button_element_base { +public: + void button_action(sys::state& state) noexcept override { + sys::macro_builder_template& t = state.ui_state.current_template; + t.source = state.local_player_nation; + // Replace templates with the same name and of the same scenario + bool overwrite = false; + for(auto& u : state.ui_state.templates) { + if(std::memcmp(u.name, t.name, sizeof(sys::macro_builder_template::name)) == 0) { + std::memcpy(&u, &t, sizeof(sys::macro_builder_template)); + overwrite = true; + break; + } + } + if(!overwrite) { + state.ui_state.templates.push_back(t); + } + auto sdir = simple_fs::get_or_create_templates_directory(); + simple_fs::write_file(sdir, state.loaded_scenario_file, reinterpret_cast(state.ui_state.templates.data()), uint32_t(state.ui_state.templates.size()) * sizeof(sys::macro_builder_template)); + send(state, parent, notify_setting_update{}); + } +}; +class macro_builder_remove_template_button : public button_element_base { +public: + void button_action(sys::state& state) noexcept override { + sys::macro_builder_template& t = state.ui_state.current_template; + for(uint32_t i = 0; i < uint32_t(state.ui_state.templates.size()); i++) { + auto const& u = state.ui_state.templates[i]; + if(std::memcmp(u.name, t.name, sizeof(sys::macro_builder_template::name)) == 0) { + state.ui_state.templates.erase(state.ui_state.templates.begin() + i); + break; + } + } + auto sdir = simple_fs::get_or_create_templates_directory(); + simple_fs::write_file(sdir, state.loaded_scenario_file, reinterpret_cast(state.ui_state.templates.data()), uint32_t(state.ui_state.templates.size()) * sizeof(sys::macro_builder_template)); + send(state, parent, notify_setting_update{}); + } +}; +class macro_builder_switch_type_button : public button_element_base { +public: + void on_update(sys::state& state) noexcept override { + auto is_land = retrieve(state, parent); + if(is_land) { + set_button_text(state, text::produce_simple_string(state, "macro_switch_type_naval")); + } else { + set_button_text(state, text::produce_simple_string(state, "macro_switch_type_land")); + } + } + void button_action(sys::state& state) noexcept override { + auto is_land = retrieve(state, parent); + send(state, parent, element_selection_wrapper{ !is_land }); + } +}; +class macro_builder_name_input : public edit_box_element_base { +public: + void edit_box_update(sys::state& state, std::string_view str) noexcept override { + auto s = parsers::remove_surrounding_whitespace(str); + std::memset(state.ui_state.current_template.name, ' ', sizeof(sys::macro_builder_template::name)); + if(!s.empty()) + std::memcpy(state.ui_state.current_template.name, s.data(), std::min(s.length(), sizeof(sys::macro_builder_template::name))); + } +}; +class macro_builder_details : public scrollable_text { +public: + void on_update(sys::state& state) noexcept override { + auto contents = text::create_endless_layout(delegate->internal_layout, + text::layout_parameters{ 0, 0, int16_t(base_data.size.x), int16_t(base_data.size.y), + base_data.data.text.font_handle, 0, text::alignment::left, + text::is_black_from_font_id(base_data.data.text.font_handle) ? text::text_color::black : text::text_color::white, false }); + auto is_land = retrieve(state, parent); + auto const& t = state.ui_state.current_template; + + float reconnaissance_or_fire_range = 0.f; + float siege_or_torpedo_attack = 0.f; + float attack_or_gun_power = 0.f; + float defence_or_hull = 0.f; + float discipline_or_evasion = std::numeric_limits::max(); + float support = 0.f; + float supply_consumption = 0.f; + float maximum_speed = std::numeric_limits::max(); + float maneuver = std::numeric_limits::max(); + int32_t supply_consumption_score = 0; + bool warn_overseas = false; + bool warn_culture = false; + bool warn_active = false; + for(dcon::unit_type_id::value_base_t i = 0; i < sys::macro_builder_template::max_types; i++) { + if(t.amounts[i] == 0) //not needed to show this + continue; + dcon::unit_type_id utid = dcon::unit_type_id(i); + if(is_land != state.military_definitions.unit_base_definitions[utid].is_land) + continue; + + if(!state.military_definitions.unit_base_definitions[utid].active && !state.world.nation_get_active_unit(state.local_player_nation, utid)) + warn_active = true; + if(state.military_definitions.unit_base_definitions[utid].primary_culture) + warn_culture = true; + if(state.military_definitions.unit_base_definitions[utid].can_build_overseas) + warn_overseas = true; + + reconnaissance_or_fire_range += state.world.nation_get_unit_stats(state.local_player_nation, utid).reconnaissance_or_fire_range * float(t.amounts[i]); + siege_or_torpedo_attack += state.world.nation_get_unit_stats(state.local_player_nation, utid).siege_or_torpedo_attack * float(t.amounts[i]); + attack_or_gun_power += state.world.nation_get_unit_stats(state.local_player_nation, utid).attack_or_gun_power * float(t.amounts[i]); + defence_or_hull += state.world.nation_get_unit_stats(state.local_player_nation, utid).defence_or_hull * float(t.amounts[i]); + discipline_or_evasion += std::min(discipline_or_evasion, state.world.nation_get_unit_stats(state.local_player_nation, utid).discipline_or_evasion); + supply_consumption += state.world.nation_get_unit_stats(state.local_player_nation, utid).supply_consumption * float(t.amounts[i]); + maximum_speed = std::min(maximum_speed, state.world.nation_get_unit_stats(state.local_player_nation, utid).maximum_speed); + if(is_land) { + support += state.world.nation_get_unit_stats(state.local_player_nation, utid).support * float(t.amounts[i]); + maneuver += std::min(maneuver, state.military_definitions.unit_base_definitions[utid].maneuver); + } else { + supply_consumption_score += state.military_definitions.unit_base_definitions[utid].supply_consumption_score * int32_t(t.amounts[i]); + } + } + + if(warn_overseas) + text::add_line(state, contents, "macro_warn_overseas"); + if(warn_culture) + text::add_line(state, contents, "macro_warn_culture"); + if(warn_active) + text::add_line(state, contents, "macro_warn_unlocked"); + + // Total + if(maximum_speed == std::numeric_limits::max()) maximum_speed = 0.f; + if(discipline_or_evasion == std::numeric_limits::max()) discipline_or_evasion = 0.f; + if(maneuver == std::numeric_limits::max()) maneuver = 0.f; + text::add_line(state, contents, text::produce_simple_string(state, "macro_total_desc")); + if(is_land) { + if(reconnaissance_or_fire_range > 0.f) { + text::add_line(state, contents, "alice_recon", text::variable_type::x, text::format_float(reconnaissance_or_fire_range, 2)); + } + if(siege_or_torpedo_attack > 0.f) { + text::add_line(state, contents, "alice_siege", text::variable_type::x, text::format_float(siege_or_torpedo_attack, 2)); + } + text::add_line(state, contents, "alice_attack", text::variable_type::x, text::format_float(attack_or_gun_power, 2)); + text::add_line(state, contents, "alice_defence", text::variable_type::x, text::format_float(defence_or_hull, 2)); + text::add_line(state, contents, "alice_discipline", text::variable_type::x, text::format_float(discipline_or_evasion * 100, 0)); + if(support > 0.f) { + text::add_line(state, contents, "alice_support", text::variable_type::x, text::format_float(support * 100, 0)); + } + text::add_line(state, contents, "alice_maneuver", text::variable_type::x, text::format_float(maneuver, 0)); + text::add_line(state, contents, "alice_maximum_speed", text::variable_type::x, text::format_float(maximum_speed, 2)); + text::add_line(state, contents, "alice_supply_consumption", text::variable_type::x, text::format_float(supply_consumption * 100, 0)); + } else { + text::add_line(state, contents, "alice_maximum_speed", text::variable_type::x, text::format_float(maximum_speed, 2)); + text::add_line(state, contents, "alice_attack", text::variable_type::x, text::format_float(attack_or_gun_power, 2)); + if(siege_or_torpedo_attack > 0.f) { + text::add_line(state, contents, "alice_torpedo_attack", text::variable_type::x, text::format_float(siege_or_torpedo_attack, 2)); + } + text::add_line(state, contents, "alice_hull", text::variable_type::x, text::format_float(defence_or_hull, 2)); + text::add_line(state, contents, "alice_fire_range", text::variable_type::x, text::format_float(reconnaissance_or_fire_range, 2)); + if(discipline_or_evasion > 0.f) { + text::add_line(state, contents, "alice_evasion", text::variable_type::x, text::format_float(discipline_or_evasion * 100, 0)); + } + text::add_line(state, contents, "alice_supply_consumption", text::variable_type::x, text::format_float(supply_consumption * 100, 0)); + text::add_line(state, contents, "alice_supply_load", text::variable_type::x, supply_consumption_score); + } + text::add_line_break_to_layout(state, contents); + + // Describe for each + for(dcon::unit_type_id::value_base_t i = 0; i < sys::macro_builder_template::max_types; i++) { + if(t.amounts[i] == 0) //not needed to show this + continue; + dcon::unit_type_id utid = dcon::unit_type_id(i); + if(is_land != state.military_definitions.unit_base_definitions[utid].is_land) + continue; + text::add_line(state, contents, state.military_definitions.unit_base_definitions[utid].name); + if(is_land) { + if(state.world.nation_get_unit_stats(state.local_player_nation, utid).reconnaissance_or_fire_range > 0) { + text::add_line(state, contents, "alice_recon", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).reconnaissance_or_fire_range * float(t.amounts[i]), 2)); + } + if(state.world.nation_get_unit_stats(state.local_player_nation, utid).siege_or_torpedo_attack > 0) { + text::add_line(state, contents, "alice_siege", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).siege_or_torpedo_attack * float(t.amounts[i]), 2)); + } + text::add_line(state, contents, "alice_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).attack_or_gun_power * float(t.amounts[i]), 2)); + text::add_line(state, contents, "alice_defence", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).defence_or_hull * float(t.amounts[i]), 2)); + text::add_line(state, contents, "alice_discipline", text::variable_type::x, text::format_float(state.military_definitions.unit_base_definitions[utid].discipline_or_evasion * 100 * float(t.amounts[i]), 0)); + if(state.military_definitions.unit_base_definitions[utid].support > 0.f) { + text::add_line(state, contents, "alice_support", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).support * 100 * float(t.amounts[i]), 0)); + } + text::add_line(state, contents, "alice_maneuver", text::variable_type::x, text::format_float(state.military_definitions.unit_base_definitions[utid].maneuver * float(t.amounts[i]), 0)); + text::add_line(state, contents, "alice_maximum_speed", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).maximum_speed * float(t.amounts[i]), 2)); + text::add_line(state, contents, "alice_supply_consumption", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).supply_consumption * 100 * float(t.amounts[i]), 0)); + } else { + text::add_line(state, contents, "alice_maximum_speed", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).maximum_speed * float(t.amounts[i]), 2)); + text::add_line(state, contents, "alice_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).attack_or_gun_power * float(t.amounts[i]), 2)); + if(state.world.nation_get_unit_stats(state.local_player_nation, utid).siege_or_torpedo_attack > 0) { + text::add_line(state, contents, "alice_torpedo_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).siege_or_torpedo_attack * float(t.amounts[i]), 2)); + } + text::add_line(state, contents, "alice_hull", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).defence_or_hull* float(t.amounts[i]), 2)); + text::add_line(state, contents, "alice_fire_range", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).reconnaissance_or_fire_range* float(t.amounts[i]), 2)); + if(state.military_definitions.unit_base_definitions[utid].discipline_or_evasion > 0.f) { + text::add_line(state, contents, "alice_evasion", text::variable_type::x, text::format_float(state.military_definitions.unit_base_definitions[utid].discipline_or_evasion * 100 * float(t.amounts[i]), 0)); + } + text::add_line(state, contents, "alice_supply_consumption", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, utid).supply_consumption * 100 * float(t.amounts[i]), 0)); + text::add_line(state, contents, "alice_supply_load", text::variable_type::x, state.military_definitions.unit_base_definitions[utid].supply_consumption_score * int32_t(t.amounts[i])); + } + text::add_line_break_to_layout(state, contents); + } + calibrate_scrollbar(state); + } + + message_result test_mouse(sys::state& state, int32_t x, int32_t y, mouse_probe_type type) noexcept override { + return message_result::consumed; + } +}; +class macro_builder_apply_button : public button_element_base { + std::vector provinces; + std::vector marked; + void get_provinces(sys::state& state, dcon::province_id p) { + if(marked[p.index()]) + return; + marked[p.index()] = true; + if(state.world.province_get_nation_from_province_control(p) == state.local_player_nation && state.world.province_get_nation_from_province_ownership(p) == state.local_player_nation) { + provinces.push_back(p); + for(const auto adj : state.world.province_get_province_adjacency_as_connected_provinces(p)) { + auto p2 = adj.get_connected_provinces(adj.get_connected_provinces(0) == p ? 1 : 0); + get_provinces(state, p2); + } + } + } +public: + void on_create(sys::state& state) noexcept override { + button_element_base::on_create(state); + marked.resize(state.world.province_size() + 1, false); + } + void on_update(sys::state& state) noexcept override { + disabled = (state.map_state.selected_province == dcon::province_id{}); + } + void button_action(sys::state& state) noexcept override { + auto is_land = retrieve(state, parent); + auto const& t = state.ui_state.current_template; + + provinces.clear(); + marked.assign(marked.size(), false); + get_provinces(state, state.map_state.selected_province); + if(provinces.empty()) { + return; + } + + std::vector marked_p(state.world.province_size() + 1, false); + std::vector marked_c(state.world.culture_size() + 1, false); + for(dcon::unit_type_id::value_base_t i = 0; i < sys::macro_builder_template::max_types; i++) { + if(t.amounts[i] == 0) //not needed to show this + continue; + dcon::unit_type_id utid = dcon::unit_type_id(i); + if(is_land != state.military_definitions.unit_base_definitions[utid].is_land) + continue; + uint8_t total_built = 0; + if(is_land) { + for(const auto p : provinces) { + for(const auto c : state.world.in_culture) { + if(marked_p[p.index()] == false + && marked_c[c.id.index()] == false + && command::can_start_land_unit_construction(state, state.local_player_nation, p, c, utid, state.map_state.selected_province)) { + command::start_land_unit_construction(state, state.local_player_nation, p, c, utid, state.map_state.selected_province); + marked_p[p.index()] = true; + marked_c[c.id.index()] = true; + total_built++; + } + if(total_built >= t.amounts[i]) break; + } + if(total_built >= t.amounts[i]) break; + } + } else { + for(const auto p : provinces) { + if(marked_p[p.index()] == false + && command::can_start_naval_unit_construction(state, state.local_player_nation, p, utid, state.map_state.selected_province)) { + command::start_naval_unit_construction(state, state.local_player_nation, p, utid, state.map_state.selected_province); + marked_p[p.index()] = true; + total_built++; + } + if(total_built >= t.amounts[i]) break; + } + } + } + } + tooltip_behavior has_tooltip(sys::state& state) noexcept override { + return tooltip_behavior::variable_tooltip; + } + + void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override { + if(state.map_state.selected_province == dcon::province_id{}) { + text::add_line(state, contents, "macro_select_province"); + return; + } + + provinces.clear(); + marked.assign(marked.size(), false); + get_provinces(state, state.map_state.selected_province); + if(provinces.empty()) { + text::add_line(state, contents, "macro_warn_invalid_province"); + return; + } + + auto is_land = retrieve(state, parent); + auto const& t = state.ui_state.current_template; + + std::vector marked_p(state.world.province_size() + 1, false); + std::vector marked_c(state.world.culture_size() + 1, false); + for(dcon::unit_type_id::value_base_t i = 0; i < sys::macro_builder_template::max_types; i++) { + if(t.amounts[i] == 0) //not needed to show this + continue; + dcon::unit_type_id utid = dcon::unit_type_id(i); + if(is_land != state.military_definitions.unit_base_definitions[utid].is_land) + continue; + uint8_t total_built = 0; + if(is_land) { + for(const auto p : provinces) { + for(const auto c : state.world.in_culture) { + if(marked_p[p.index()] == false + && marked_c[c.id.index()] == false + && command::can_start_land_unit_construction(state, state.local_player_nation, p, c, utid, state.map_state.selected_province)) { + marked_p[p.index()] = true; + marked_c[c.id.index()] = true; + total_built++; + } + if(total_built >= t.amounts[i]) break; + } + if(total_built >= t.amounts[i]) break; + } + } else { + for(const auto p : provinces) { + if(marked_p[p.index()] == false + && command::can_start_naval_unit_construction(state, state.local_player_nation, p, utid, state.map_state.selected_province)) { + marked_p[p.index()] = true; + total_built++; + } + if(total_built >= t.amounts[i]) break; + } + } + if(total_built != t.amounts[i]) { + text::substitution_map sub{}; + text::add_to_substitution_map(sub, text::variable_type::x, text::int_wholenum{ t.amounts[i] }); + text::add_to_substitution_map(sub, text::variable_type::y, text::int_wholenum{ total_built }); + text::add_to_substitution_map(sub, text::variable_type::name, state.military_definitions.unit_base_definitions[utid].name); + auto box = text::open_layout_box(contents); + text::localised_format_box(state, contents, box, "macro_warn_insuff", sub); + text::close_layout_box(contents, box); + } + } + } +}; +class macro_builder_window : public window_element_base { + bool is_land = true; + macro_builder_name_input* name_input = nullptr; +public: + void on_create(sys::state& state) noexcept override { + window_element_base::on_create(state); + impl_on_update(state); + } + + std::unique_ptr make_child(sys::state& state, std::string_view name, dcon::gui_def_id id) noexcept override { + if(name == "background") { + return make_element_by_type(state, id); + } else if(name == "close") { + return make_element_by_type(state, id); + } else if(name == "input") { + auto ptr = make_element_by_type(state, id); + name_input = ptr.get(); + return ptr; + } else if(name == "template_listbox") { + return make_element_by_type(state, id); + } else if(name == "unit_listbox") { + return make_element_by_type(state, id); + } else if(name == "new_template") { + return make_element_by_type(state, id); + } else if(name == "save_template") { + return make_element_by_type(state, id); + } else if(name == "remove_template") { + return make_element_by_type(state, id); + } else if(name == "switch_type") { + return make_element_by_type(state, id); + } else if(name == "apply") { + return make_element_by_type(state, id); + } else if(name == "details") { + return make_element_by_type(state, id); + } else { + return nullptr; + } + } + + message_result get(sys::state& state, Cyto::Any& payload) noexcept override { + if(payload.holds_type()) { + payload.emplace(is_land); + return message_result::consumed; + } else if(payload.holds_type>()) { + is_land = Cyto::any_cast>(payload).data; + impl_on_update(state); + return message_result::consumed; + } else if(payload.holds_type< notify_template_select>()) { + auto const& name = state.ui_state.current_template.name; + auto sv = std::string_view(name, name + sizeof(name)); + auto s = std::string(sv); + name_input->edit_index_position(state, 0); + name_input->set_text(state, s); + impl_on_update(state); + return message_result::consumed; + } else if(payload.holds_type()) { + impl_on_update(state); + return message_result::consumed; + } + return window_element_base::impl_get(state, payload); + } +}; +class minimap_macro_builder_button : public button_element_base { +public: + void button_action(sys::state& state) noexcept override { + if(!state.ui_state.macro_builder_window) { + auto window = make_element_by_type(state, "alice_macro_builder"); + state.ui_state.macro_builder_window = window.get(); + state.ui_state.root->add_child_to_front(std::move(window)); + } else if(state.ui_state.macro_builder_window->is_visible()) { + state.ui_state.macro_builder_window->set_visible(state, false); + } else { + state.ui_state.macro_builder_window->set_visible(state, true); + state.ui_state.root->move_child_to_front(state.ui_state.macro_builder_window); + } + } + + tooltip_behavior has_tooltip(sys::state& state) noexcept override { + return tooltip_behavior::tooltip; + } + + void update_tooltip(sys::state& state, int32_t x, int32_t t, text::columnar_layout& contents) noexcept override { + auto box = text::open_layout_box(contents, 0); + text::localised_format_box(state, contents, box, std::string_view("macro_builder")); + text::close_layout_box(contents, box); + } +}; + class minimap_msg_settings_button : public button_element_base { public: void button_action(sys::state& state) noexcept override { @@ -351,6 +916,9 @@ class minimap_container_window : public window_element_base { } else if(name == "menu_button") { return make_element_by_type(state, id); } else if(name == "button_goto") { + auto ptr = make_element_by_type(state, id); + ptr->base_data.position.y += ptr->base_data.size.y; + add_child_to_front(std::move(ptr)); return make_element_by_type(state, id); } else if(name == "ledger_button") { return make_element_by_type(state, id); diff --git a/src/gui/topbar_subwindows/military_subwindows/gui_build_unit_large_window.hpp b/src/gui/topbar_subwindows/military_subwindows/gui_build_unit_large_window.hpp index eac3bd89e..2d16b1fe1 100644 --- a/src/gui/topbar_subwindows/military_subwindows/gui_build_unit_large_window.hpp +++ b/src/gui/topbar_subwindows/military_subwindows/gui_build_unit_large_window.hpp @@ -560,7 +560,7 @@ class units_build_listbox : public listbox_element_base= 1000.0f) { + if(pl.get_pop().get_poptype() == state.culture_definitions.soldiers && state.world.pop_get_size(pl.get_pop()) >= state.defines.pop_min_size_for_regiment) { info.pop_info = pl.get_pop(); break; } diff --git a/src/map/modes/political.hpp b/src/map/modes/political.hpp index d281ef541..73e71b9c4 100644 --- a/src/map/modes/political.hpp +++ b/src/map/modes/political.hpp @@ -1,6 +1,8 @@ #pragma once #include "prng.hpp" +#include "prng.hpp" + uint32_t derive_color_from_ol_color(sys::state& state, uint32_t ol_color, dcon::nation_id n) { auto base = sys::rgb_to_hsv(ol_color); auto roff = rng::get_random_pair(state, uint32_t(n.index()), uint32_t(n.index()));