#include #include "Core.h" #include #include #include "Types.h" #include "MemAccess.h" #include "df/biome_type.h" #include "df/inorganic_raw.h" #include "df/material_flags.h" #include "df/viewscreen_choose_start_sitest.h" #include "df/world.h" #include "df/world_region_type.h" #include "df/world_raws.h" #include "embark-assistant.h" #include "finder_ui.h" #include "screen.h" using df::global::world; #define profile_file_name "./data/init/embark_assistant_profile.txt" namespace embark_assist { namespace finder_ui { enum class fields : int8_t { x_dim, y_dim, savagery_calm, savagery_medium, savagery_savage, good, neutral, evil, aquifer, min_river, max_river, waterfall, flat, clay, sand, flux, soil_min, soil_min_everywhere, soil_max, evil_weather, reanimation, thralling, spire_count_min, spire_count_max, magma_min, magma_max, biome_count_min, biome_count_max, region_type_1, region_type_2, region_type_3, biome_1, biome_2, biome_3, metal_1, metal_2, metal_3, economic_1, economic_2, economic_3, mineral_1, mineral_2, mineral_3 }; fields first_fields = fields::x_dim; fields last_fields = fields::mineral_3; struct display_map_elements { std::string text; int16_t key; }; typedef std::vector display_maps; typedef std::vector name_lists; typedef std::list< display_map_elements> sort_lists; struct ui_lists { uint16_t current_display_value; // Not the value itself, but a reference to its index. int16_t current_value; // The integer representation of the value (if an enum). uint16_t current_index; // What's selected uint16_t focus; // The value under the (possibly inactive) cursor display_maps list; // The strings to be displayed together with keys // to allow location of the actual elements (e.g. a raws.inorganics mat_index // or underlying enum value). }; typedef std::vector uis; const DFHack::Screen::Pen active_pen(' ', COLOR_YELLOW); const DFHack::Screen::Pen passive_pen(' ', COLOR_DARKGREY); const DFHack::Screen::Pen normal_pen(' ', COLOR_GREY); const DFHack::Screen::Pen white_pen(' ', COLOR_WHITE); const DFHack::Screen::Pen lr_pen(' ', COLOR_LIGHTRED); //========================================================================================================== struct states { embark_assist::defs::find_callbacks find_callback; uis ui; display_maps finder_list; // Don't need the element key, but it's easier to use the same type. uint16_t finder_list_focus; bool finder_list_active; uint16_t max_inorganic; }; static states *state = 0; //========================================================================================================== bool compare(const display_map_elements& first, const display_map_elements& second) { uint16_t i = 0; while (i < first.text.length() && i < second.text.length()) { if (first.text[i] < second.text[i]) { return true; } else if (first.text[i] > second.text[i]) { return false; } ++i; } return first.text.length() < second.text.length(); } //========================================================================================================== void append(sort_lists *sort_list, display_map_elements element) { sort_lists::iterator iterator; for (iterator = sort_list->begin(); iterator != sort_list->end(); ++iterator) { if (iterator->key == element.key) { return; } } sort_list->push_back(element); } //========================================================================================================== void save_profile() { color_ostream_proxy out(Core::getInstance().getConsole()); FILE* outfile = fopen(profile_file_name, "w"); fields i = first_fields; while (true) { for (int k = 0; k < state->ui[static_cast(i)]->list.size(); k++) { if (state->ui[static_cast(i)]->current_value == state->ui[static_cast(i)]->list[k].key) { fprintf(outfile, "[%s:%s]\n", state->finder_list[static_cast(i)].text.c_str(), state->ui[static_cast(i)]->list[k].text.c_str()); break; } } // fprintf(outfile, "[%s:%i]\n", state->finder_list[static_cast(i)].text.c_str(), state->ui[static_cast(i)]->current_value); if (i == last_fields) { break; // done } i = static_cast (static_cast(i) + 1); } fclose(outfile); } //========================================================================================================== void load_profile() { color_ostream_proxy out(Core::getInstance().getConsole()); FILE* infile = fopen(profile_file_name, "r"); if (!infile) { out.printerr("No profile file found at %s\n", profile_file_name); return; } fields i = first_fields; char line[80]; int count = 80; bool found; while (true) { fgets(line, count, infile); if (line[0] != '[') { out.printerr("Failed to find token start '[' at line %i\n", static_cast(i)); fclose(infile); return; } for (int k = 1; k < count; k++) { if (line[k] == ':') { for (int l = 1; l < k; l++) { if (state->finder_list[static_cast(i)].text.c_str()[l - 1] != line[l]) { out.printerr("Token mismatch of %s vs %s\n", line, state->finder_list[static_cast(i)].text.c_str()); fclose(infile); return; } } found = false; for (int l = 0; l < state->ui[static_cast(i)]->list.size(); l++) { for (int m = k + 1; m < count; m++) { if (state->ui[static_cast(i)]->list[l].text.c_str()[m - (k + 1)] != line[m]) { if (state->ui[static_cast(i)]->list[l].text.c_str()[m - (k + 1)] == '\0' && line[m] == ']') { found = true; } break; } } if (found) { break; } } if (!found) { out.printerr("Value extraction failure from %s\n", line); fclose(infile); return; } break; } } if (!found) { out.printerr("Value delimiter not found in %s\n", line); fclose(infile); return; } if (i == last_fields) { break; // done } i = static_cast (static_cast(i) + 1); } fclose(infile); // Checking done. No do the work. infile = fopen(profile_file_name, "r"); i = first_fields; while (true) { fgets(line, count, infile); for (int k = 1; k < count; k++) { if (line[k] == ':') { found = false; for (int l = 0; l < state->ui[static_cast(i)]->list.size(); l++) { for (int m = k + 1; m < count; m++) { if (state->ui[static_cast(i)]->list[l].text.c_str()[m - (k + 1)] != line[m]) { if (state->ui[static_cast(i)]->list[l].text.c_str()[m - (k + 1)] == '\0' && line[m] == ']') { state->ui[static_cast(i)]->current_value = state->ui[static_cast(i)]->list[l].key; state->ui[static_cast(i)]->current_display_value = l; found = true; } break; } } if (found) { break; } } break; } } if (i == last_fields) { break; // done } i = static_cast (static_cast(i) + 1); } fclose(infile); } //========================================================================================================== void ui_setup(embark_assist::defs::find_callbacks find_callback, uint16_t max_inorganic) { // color_ostream_proxy out(Core::getInstance().getConsole()); if (!embark_assist::finder_ui::state) { state = new(states); state->finder_list_focus = 0; state->finder_list_active = true; state->find_callback = find_callback; state->max_inorganic = max_inorganic; } fields i = first_fields; ui_lists *element; while (true) { element = new ui_lists; element->current_display_value = 0; element->current_index = 0; element->focus = 0; switch (i) { case fields::x_dim: for (int16_t k = 1; k < 16; k++) { element->list.push_back({ std::to_string(k), k }); } break; case fields::y_dim: for (int16_t k = 1; k < 16; k++) { element->list.push_back({ std::to_string(k), k }); } break; case fields::savagery_calm: case fields::savagery_medium: case fields::savagery_savage: case fields::good: case fields::neutral: case fields::evil: { embark_assist::defs::evil_savagery_values k = embark_assist::defs::evil_savagery_values::NA; while (true) { switch (k) { case embark_assist::defs::evil_savagery_values::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::evil_savagery_values::All: element->list.push_back({ "All", static_cast(k) }); break; case embark_assist::defs::evil_savagery_values::Present: element->list.push_back({ "Present", static_cast(k) }); break; case embark_assist::defs::evil_savagery_values::Absent: element->list.push_back({ "Absent", static_cast(k) }); break; } if (k == embark_assist::defs::evil_savagery_values::Absent) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::aquifer: { embark_assist::defs::aquifer_ranges k = embark_assist::defs::aquifer_ranges::NA; while (true) { switch (k) { case embark_assist::defs::aquifer_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::aquifer_ranges::All: element->list.push_back({ "All", static_cast(k) }); break; case embark_assist::defs::aquifer_ranges::Present: element->list.push_back({ "Present", static_cast(k) }); break; case embark_assist::defs::aquifer_ranges::Partial: element->list.push_back({ "Partial", static_cast(k) }); break; case embark_assist::defs::aquifer_ranges::Not_All: element->list.push_back({ "Not All", static_cast(k) }); break; case embark_assist::defs::aquifer_ranges::Absent: element->list.push_back({ "Absent", static_cast(k) }); break; } if (k == embark_assist::defs::aquifer_ranges::Absent) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::min_river: case fields::max_river: { embark_assist::defs::river_ranges k = embark_assist::defs::river_ranges::NA; while (true) { switch (k) { case embark_assist::defs::river_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::river_ranges::None: element->list.push_back({ "None", static_cast(k) }); break; case embark_assist::defs::river_ranges::Brook: element->list.push_back({ "Brook", static_cast(k) }); break; case embark_assist::defs::river_ranges::Stream: element->list.push_back({ "Stream", static_cast(k) }); break; case embark_assist::defs::river_ranges::Minor: element->list.push_back({ "Minor", static_cast(k) }); break; case embark_assist::defs::river_ranges::Medium: element->list.push_back({ "Medium", static_cast(k) }); break; case embark_assist::defs::river_ranges::Major: element->list.push_back({ "Major", static_cast(k) }); break; } if (k == embark_assist::defs::river_ranges::Major) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::waterfall: case fields::flat: case fields::evil_weather: case fields::reanimation: case fields::thralling: { embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA; while (true) { switch (k) { case embark_assist::defs::yes_no_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::yes_no_ranges::Yes: element->list.push_back({ "Yes", static_cast(k) }); break; case embark_assist::defs::yes_no_ranges::No: element->list.push_back({ "No", static_cast(k) }); break; } if (k == embark_assist::defs::yes_no_ranges::No) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::soil_min_everywhere: { embark_assist::defs::all_present_ranges k = embark_assist::defs::all_present_ranges::All; while (true) { switch (k) { case embark_assist::defs::all_present_ranges::All: element->list.push_back({ "All", static_cast(k) }); break; case embark_assist::defs::all_present_ranges::Present: element->list.push_back({ "Present", static_cast(k) }); break; } if (k == embark_assist::defs::all_present_ranges::Present) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::clay: case fields::sand: case fields::flux: { embark_assist::defs::present_absent_ranges k = embark_assist::defs::present_absent_ranges::NA; while (true) { switch (k) { case embark_assist::defs::present_absent_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::present_absent_ranges::Present: element->list.push_back({ "Present", static_cast(k) }); break; case embark_assist::defs::present_absent_ranges::Absent: element->list.push_back({ "Absent", static_cast(k) }); break; } if (k == embark_assist::defs::present_absent_ranges::Absent) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::soil_min: case fields::soil_max: { embark_assist::defs::soil_ranges k = embark_assist::defs::soil_ranges::NA; while (true) { switch (k) { case embark_assist::defs::soil_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::soil_ranges::None: element->list.push_back({ "None", static_cast(k) }); break; case embark_assist::defs::soil_ranges::Very_Shallow: element->list.push_back({ "Very Shallow", static_cast(k) }); break; case embark_assist::defs::soil_ranges::Shallow: element->list.push_back({ "Shallow", static_cast(k) }); break; case embark_assist::defs::soil_ranges::Deep: element->list.push_back({ "Deep", static_cast(k) }); break; case embark_assist::defs::soil_ranges::Very_Deep: element->list.push_back({ "Very Deep", static_cast(k) }); break; } if (k == embark_assist::defs::soil_ranges::Very_Deep) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::spire_count_min: case fields::spire_count_max: for (int16_t k = -1; k <= 9; k++) { if (k == -1) { element->list.push_back({ "N/A", k }); } else { element->list.push_back({ std::to_string(k), k }); } } break; case fields::magma_min: case fields::magma_max: { embark_assist::defs::magma_ranges k = embark_assist::defs::magma_ranges::NA; while (true) { switch (k) { case embark_assist::defs::magma_ranges::NA: element->list.push_back({ "N/A", static_cast(k) }); break; case embark_assist::defs::magma_ranges::Cavern_3: element->list.push_back({ "Third Cavern", static_cast(k) }); break; case embark_assist::defs::magma_ranges::Cavern_2: element->list.push_back({ "Second Cavern", static_cast(k) }); break; case embark_assist::defs::magma_ranges::Cavern_1: element->list.push_back({ "First Cavern", static_cast(k) }); break; case embark_assist::defs::magma_ranges::Volcano: element->list.push_back({ "Volcano", static_cast(k) }); break; } if (k == embark_assist::defs::magma_ranges::Volcano) { break; } k = static_cast (static_cast(k) + 1); } } break; case fields::biome_count_min: case fields::biome_count_max: for (int16_t k = 0; k < 10; k++) { if (k == 0) { element->list.push_back({ "N/A", -1 }); } else { element->list.push_back({ std::to_string(k), k }); } } break; case fields::region_type_1: case fields::region_type_2: case fields::region_type_3: { std::list name; std::list::iterator iterator; FOR_ENUM_ITEMS(world_region_type, iter) { name.push_back({ ENUM_KEY_STR(world_region_type, iter), static_cast(iter) }); } name.sort(compare); element->list.push_back({ "N/A", -1 }); for (iterator = name.begin(); iterator != name.end(); ++iterator) { element->list.push_back({ iterator->text, iterator->key }); } name.clear(); } break; case fields::biome_1: case fields::biome_2: case fields::biome_3: { sort_lists name; sort_lists::iterator iterator; FOR_ENUM_ITEMS(biome_type, iter) { std::string s = ENUM_KEY_STR(biome_type, iter); if (s.substr(0, 4) != "POOL" && s.substr(0, 5) != "RIVER" && s.substr(0, 3) != "SUB") { name.push_back({ s, static_cast(iter) }); } } name.sort(compare); element->list.push_back({ "N/A", -1 }); for (iterator = name.begin(); iterator != name.end(); ++iterator) { element->list.push_back({ iterator->text, iterator->key }); } name.clear(); } break; case fields::metal_1: case fields::metal_2: case fields::metal_3: { sort_lists name; sort_lists::iterator iterator; for (uint16_t k = 0; k < embark_assist::finder_ui::state->max_inorganic; k++) { for (uint16_t l = 0; l < world->raws.inorganics[k]->metal_ore.mat_index.size(); l++) { append(&name, { world->raws.inorganics[world->raws.inorganics[k]->metal_ore.mat_index[l]]->id, world->raws.inorganics[k]->metal_ore.mat_index[l] }); } } name.sort(compare); element->list.push_back({ "N/A", -1 }); for (iterator = name.begin(); iterator != name.end(); ++iterator) { element->list.push_back({ iterator->text, iterator->key }); } name.clear(); } break; case fields::economic_1: case fields::economic_2: case fields::economic_3: { sort_lists name; sort_lists::iterator iterator; for (int16_t k = 0; k < embark_assist::finder_ui::state->max_inorganic; k++) { if (world->raws.inorganics[k]->economic_uses.size() != 0 && !world->raws.inorganics[k]->material.flags.is_set(df::material_flags::IS_METAL)) { append(&name, { world->raws.inorganics[k]->id, k }); } } name.sort(compare); element->list.push_back({ "N/A", -1 }); for (iterator = name.begin(); iterator != name.end(); ++iterator) { element->list.push_back({ iterator->text, iterator->key }); } name.clear(); } break; case fields::mineral_1: case fields::mineral_2: case fields::mineral_3: { sort_lists name; sort_lists::iterator iterator; for (int16_t k = 0; k < embark_assist::finder_ui::state->max_inorganic; k++) { if (world->raws.inorganics[k]->environment.location.size() != 0 || world->raws.inorganics[k]->environment_spec.mat_index.size() != 0 || world->raws.inorganics[k]->flags.is_set(df::inorganic_flags::SEDIMENTARY) || world->raws.inorganics[k]->flags.is_set(df::inorganic_flags::IGNEOUS_EXTRUSIVE) || world->raws.inorganics[k]->flags.is_set(df::inorganic_flags::IGNEOUS_INTRUSIVE) || world->raws.inorganics[k]->flags.is_set(df::inorganic_flags::METAMORPHIC) || world->raws.inorganics[k]->flags.is_set(df::inorganic_flags::SOIL)) { append(&name, { world->raws.inorganics[k]->id, k }); } } name.sort(compare); element->list.push_back({ "N/A", -1 }); for (iterator = name.begin(); iterator != name.end(); ++iterator) { element->list.push_back({ iterator->text, iterator->key }); } name.clear(); } break; } element->current_value = element->list[0].key; state->ui.push_back(element); switch (i) { case fields::x_dim: state->finder_list.push_back({ "X Dimension", static_cast(i) }); break; case fields::y_dim: state->finder_list.push_back({ "Y Dimension", static_cast(i) }); break; case fields::savagery_calm: state->finder_list.push_back({ "Low Savagery", static_cast(i) }); break; case fields::savagery_medium: state->finder_list.push_back({ "Medium Savagery", static_cast(i) }); break; case fields::savagery_savage: state->finder_list.push_back({ "High Savagery", static_cast(i) }); break; case fields::good: state->finder_list.push_back({ "Good", static_cast(i) }); break; case fields::neutral: state->finder_list.push_back({ "Neutral", static_cast(i) }); break; case fields::evil: state->finder_list.push_back({ "Evil", static_cast(i) }); break; case fields::aquifer: state->finder_list.push_back({ "Aquifer", static_cast(i) }); break; case fields::min_river: state->finder_list.push_back({ "Min River", static_cast(i) }); break; case fields::max_river: state->finder_list.push_back({ "Max River", static_cast(i) }); break; case fields::waterfall: state->finder_list.push_back({ "Waterfall", static_cast(i) }); break; case fields::flat: state->finder_list.push_back({ "Flat", static_cast(i) }); break; case fields::soil_min_everywhere: state->finder_list.push_back({ "Min Soil Everywhere", static_cast(i) }); break; case fields::evil_weather: state->finder_list.push_back({ "Evil Weather", static_cast(i) }); break; case fields::reanimation: state->finder_list.push_back({ "Reanimation", static_cast(i) }); break; case fields::thralling: state->finder_list.push_back({ "Thralling", static_cast(i) }); break; case fields::clay: state->finder_list.push_back({ "Clay", static_cast(i) }); break; case fields::sand: state->finder_list.push_back({ "Sand", static_cast(i) }); break; case fields::flux: state->finder_list.push_back({ "Flux", static_cast(i) }); break; case fields::soil_min: state->finder_list.push_back({ "Min Soil", static_cast(i) }); break; case fields::soil_max: state->finder_list.push_back({ "Max Soil", static_cast(i) }); break; case fields::spire_count_min: state->finder_list.push_back({ "Min Adamantine", static_cast(i) }); break; case fields::spire_count_max: state->finder_list.push_back({ "Max Adamantine", static_cast(i) }); break; case fields::magma_min: state->finder_list.push_back({ "Min Magma", static_cast(i) }); break; case fields::magma_max: state->finder_list.push_back({ "Max Magma", static_cast(i) }); break; case fields::biome_count_min: state->finder_list.push_back({ "Min Biome Count", static_cast(i) }); break; case fields::biome_count_max: state->finder_list.push_back({ "Max Biome Count", static_cast(i) }); break; case fields::region_type_1: state->finder_list.push_back({ "Region Type 1", static_cast(i) }); break; case fields::region_type_2: state->finder_list.push_back({ "Region Type 2", static_cast(i) }); break; case fields::region_type_3: state->finder_list.push_back({ "Region Type 3", static_cast(i) }); break; case fields::biome_1: state->finder_list.push_back({ "Biome 1", static_cast(i) }); break; case fields::biome_2: state->finder_list.push_back({ "Biome 2", static_cast(i) }); break; case fields::biome_3: state->finder_list.push_back({ "Biome 3", static_cast(i) }); break; case fields::metal_1: state->finder_list.push_back({ "Metal 1", static_cast(i) }); break; case fields::metal_2: state->finder_list.push_back({ "Metal 2", static_cast(i) }); break; case fields::metal_3: state->finder_list.push_back({ "Metal 3", static_cast(i) }); break; case fields::economic_1: state->finder_list.push_back({ "Economic 1", static_cast(i) }); break; case fields::economic_2: state->finder_list.push_back({ "Economic 2", static_cast(i) }); break; case fields::economic_3: state->finder_list.push_back({ "Economic 3", static_cast(i) }); break; case fields::mineral_1: state->finder_list.push_back({ "Mineral 1", static_cast(i) }); break; case fields::mineral_2: state->finder_list.push_back({ "Mineral 2", static_cast(i) }); break; case fields::mineral_3: state->finder_list.push_back({ "Mineral 3", static_cast(i) }); break; } if (i == last_fields) { break; // done } i = static_cast (static_cast(i) + 1); } // Default embark area size to that of the current selection. The "size" calculation is actually one // off to compensate for the list starting with 1 at index 0. // auto screen = Gui::getViewscreenByType(0); int16_t x = screen->location.region_pos.x; int16_t y = screen->location.region_pos.y; state->ui[static_cast(fields::x_dim)]->current_display_value = Gui::getViewscreenByType(0)->location.embark_pos_max.x - Gui::getViewscreenByType(0)->location.embark_pos_min.x; state->ui[static_cast(fields::x_dim)]->current_index = state->ui[static_cast(fields::x_dim)]->current_display_value; state->ui[static_cast(fields::x_dim)]->current_value = state->ui[static_cast(fields::x_dim)]->current_display_value + 1; state->ui[static_cast(fields::y_dim)]->current_display_value = Gui::getViewscreenByType(0)->location.embark_pos_max.y - Gui::getViewscreenByType(0)->location.embark_pos_min.y; state->ui[static_cast(fields::y_dim)]->current_index = state->ui[static_cast(fields::y_dim)]->current_display_value; state->ui[static_cast(fields::y_dim)]->current_value = state->ui[static_cast(fields::y_dim)]->current_display_value + 1; } //========================================================================================================== void find() { // color_ostream_proxy out(Core::getInstance().getConsole()); embark_assist::defs::finders finder; fields i = first_fields; while (true) { switch (i) { case fields::x_dim: finder.x_dim = state->ui[static_cast(i)]->current_value; break; case fields::y_dim: finder.y_dim = state->ui[static_cast(i)]->current_value; break; case fields::savagery_calm: finder.savagery[0] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::savagery_medium: finder.savagery[1] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::savagery_savage: finder.savagery[2] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::good: finder.evilness[0] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::neutral: finder.evilness[1] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::evil: finder.evilness[2] = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::aquifer: finder.aquifer = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::min_river: finder.min_river = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::max_river: finder.max_river = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::waterfall: finder.waterfall = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::flat: finder.flat = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::soil_min_everywhere: finder.soil_min_everywhere = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::evil_weather: finder.evil_weather = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::reanimation: finder.reanimation = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::thralling: finder.thralling = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::clay: finder.clay = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::sand: finder.sand = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::flux: finder.flux = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::soil_min: finder.soil_min = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::soil_max: finder.soil_max = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::spire_count_min: finder.spire_count_min = state->ui[static_cast(i)]->current_value; break; case fields::spire_count_max: finder.spire_count_max = state->ui[static_cast(i)]->current_value; break; case fields::magma_min: finder.magma_min = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::magma_max: finder.magma_max = static_cast(state->ui[static_cast(i)]->current_value); break; case fields::biome_count_min: finder.biome_count_min = state->ui[static_cast(i)]->current_value; break; case fields::biome_count_max: finder.biome_count_max = state->ui[static_cast(i)]->current_value; break; case fields::region_type_1: finder.region_type_1 = state->ui[static_cast(i)]->current_value; break; case fields::region_type_2: finder.region_type_2 = state->ui[static_cast(i)]->current_value; break; case fields::region_type_3: finder.region_type_3 = state->ui[static_cast(i)]->current_value; break; case fields::biome_1: finder.biome_1 = state->ui[static_cast(i)]->current_value; break; case fields::biome_2: finder.biome_2 = state->ui[static_cast(i)]->current_value; break; case fields::biome_3: finder.biome_3 = state->ui[static_cast(i)]->current_value; break; case fields::metal_1: finder.metal_1 = state->ui[static_cast(i)]->current_value; break; case fields::metal_2: finder.metal_2 = state->ui[static_cast(i)]->current_value; break; case fields::metal_3: finder.metal_3 = state->ui[static_cast(i)]->current_value; break; case fields::economic_1: finder.economic_1 = state->ui[static_cast(i)]->current_value; break; case fields::economic_2: finder.economic_2 = state->ui[static_cast(i)]->current_value; break; case fields::economic_3: finder.economic_3 = state->ui[static_cast(i)]->current_value; break; case fields::mineral_1: finder.mineral_1 = state->ui[static_cast(i)]->current_value; break; case fields::mineral_2: finder.mineral_2 = state->ui[static_cast(i)]->current_value; break; case fields::mineral_3: finder.mineral_3 = state->ui[static_cast(i)]->current_value; break; } if (i == last_fields) { break; // done } i = static_cast (static_cast(i) + 1); } state->find_callback(finder); } //========================================================================================================== class ViewscreenFindUi : public dfhack_viewscreen { public: ViewscreenFindUi(); void feed(std::set *input); void render(); std::string getFocusString() { return "Finder UI"; } private: }; //=============================================================================== void ViewscreenFindUi::feed(std::set *input) { if (input->count(df::interface_key::LEAVESCREEN)) { input->clear(); Screen::dismiss(this); return; } else if (input->count(df::interface_key::CURSOR_LEFT) || input->count(df::interface_key::CURSOR_RIGHT)) { state->finder_list_active = !state->finder_list_active; } else if (input->count(df::interface_key::CURSOR_UP)) { if (state->finder_list_active) { if (state->finder_list_focus > 0) { state->finder_list_focus--; } else { state->finder_list_focus = static_cast(last_fields); } } else { if (state->ui[state->finder_list_focus]->current_index > 0) { state->ui[state->finder_list_focus]->current_index--; } else { state->ui[state->finder_list_focus]->current_index = static_cast(state->ui[state->finder_list_focus]->list.size()) - 1; } } } else if (input->count(df::interface_key::CURSOR_DOWN)) { if (state->finder_list_active) { if (state->finder_list_focus < static_cast(last_fields)) { state->finder_list_focus++; } else { state->finder_list_focus = 0; } } else { if (state->ui[state->finder_list_focus]->current_index < state->ui[state->finder_list_focus]->list.size() - 1) { state->ui[state->finder_list_focus]->current_index++; } else { state->ui[state->finder_list_focus]->current_index = 0; } } } else if (input->count(df::interface_key::SELECT)) { if (!state->finder_list_active) { state->ui[state->finder_list_focus]->current_display_value = state->ui[state->finder_list_focus]->current_index; state->ui[state->finder_list_focus]->current_value = state->ui[state->finder_list_focus]->list[state->ui[state->finder_list_focus]->current_index].key; state->finder_list_active = true; } } else if (input->count(df::interface_key::CUSTOM_F)) { input->clear(); Screen::dismiss(this); find(); return; } else if (input->count(df::interface_key::CUSTOM_S)) { // Save save_profile(); } else if (input->count(df::interface_key::CUSTOM_L)) { // Load load_profile(); } } //=============================================================================== void ViewscreenFindUi::render() { // color_ostream_proxy out(Core::getInstance().getConsole()); auto screen_size = DFHack::Screen::getWindowSize(); const int list_column = 53; uint16_t offset = 0; Screen::clear(); Screen::drawBorder("Embark Assistant Site Finder"); embark_assist::screen::paintString(lr_pen, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_LEFT).c_str()); embark_assist::screen::paintString(white_pen, 2, 1, "/"); embark_assist::screen::paintString(lr_pen, 3, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_RIGHT).c_str()); embark_assist::screen::paintString(white_pen, 4, 1, ":\x1b/\x1a"); embark_assist::screen::paintString(lr_pen, 9, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_UP).c_str()); embark_assist::screen::paintString(white_pen, 10, 1, "/"); embark_assist::screen::paintString(lr_pen, 11, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_DOWN).c_str()); embark_assist::screen::paintString(white_pen, 12, 1, ":Up/Down"); embark_assist::screen::paintString(lr_pen, 21, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str()); embark_assist::screen::paintString(white_pen, 26, 1, ":Select"); embark_assist::screen::paintString(lr_pen, 34, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str()); embark_assist::screen::paintString(white_pen, 35, 1, ":Find"); embark_assist::screen::paintString(lr_pen, 41, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str()); embark_assist::screen::paintString(white_pen, 44, 1, ":Abort"); embark_assist::screen::paintString(lr_pen, 51, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_S).c_str()); embark_assist::screen::paintString(white_pen, 52, 1, ":Save"); embark_assist::screen::paintString(lr_pen, 58, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str()); embark_assist::screen::paintString(white_pen, 59, 1, ":Load"); for (uint16_t i = 0; i < state->finder_list.size(); i++) { if (i == state->finder_list_focus) { if (state->finder_list_active) { embark_assist::screen::paintString(active_pen, 1, 2 + i, state->finder_list[i].text); } else { embark_assist::screen::paintString(passive_pen, 1, 2 + i, state->finder_list[i].text); } embark_assist::screen::paintString(active_pen, 21, 2 + i, state->ui[i]->list[state->ui[i]->current_display_value].text); } else { embark_assist::screen::paintString(normal_pen, 1, 2 + i, state->finder_list[i].text); embark_assist::screen::paintString(white_pen, 21, 2 + i, state->ui[i]->list[state->ui[i]->current_display_value].text); } } // Implement scrolling lists if they don't fit on the screen. if (state->ui[state->finder_list_focus]->list.size() > screen_size.y - 3) { offset = (screen_size.y - 3) / 2; if (state->ui[state->finder_list_focus]->current_index < offset) { offset = 0; } else { offset = state->ui[state->finder_list_focus]->current_index - offset; } if (state->ui[state->finder_list_focus]->list.size() - offset < screen_size.y - 3) { offset = static_cast(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - 3); } } for (uint16_t i = 0; i < state->ui[state->finder_list_focus]->list.size(); i++) { if (i == state->ui[state->finder_list_focus]->current_index) { if (!state->finder_list_active) { // Negated expression to get the display lines in the same order as above. embark_assist::screen::paintString(active_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); } else { embark_assist::screen::paintString(passive_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); } } else { embark_assist::screen::paintString(normal_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); } } dfhack_viewscreen::render(); } //=============================================================================== ViewscreenFindUi::ViewscreenFindUi() { } } } //=============================================================================== // Exported operations //=============================================================================== void embark_assist::finder_ui::init(DFHack::Plugin *plugin_self, embark_assist::defs::find_callbacks find_callback, uint16_t max_inorganic) { if (!embark_assist::finder_ui::state) { // First call. Have to do the setup embark_assist::finder_ui::ui_setup(find_callback, max_inorganic); } Screen::show(new ViewscreenFindUi(), plugin_self); } //=============================================================================== void embark_assist::finder_ui::activate() { } //=============================================================================== void embark_assist::finder_ui::shutdown() { if (embark_assist::finder_ui::state) { for (uint16_t i = 0; i < embark_assist::finder_ui::state->ui.size(); i++) { delete embark_assist::finder_ui::state->ui[i]; } delete embark_assist::finder_ui::state; embark_assist::finder_ui::state = nullptr; } }