Merge remote-tracking branch 'PatrikLundell/embark_assistant' into develop

develop
lethosor 2020-03-20 23:22:38 -04:00
commit d42e866a35
8 changed files with 1476 additions and 274 deletions

@ -38,6 +38,14 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future
## Misc Improvements
- `embark-assistant`:
- updated embark aquifer info to show all aquifer kinds present
- added neighbor display, including kobolds (SKULKING) and necro tower count
- updated aquifer search criteria to handle the new variation
- added search criteria for embark initial tree cover
- added search criteria for necro tower count, neighbor civ count, and specific neighbors. Should handle additional entities, but not tested
# 0.47.03-beta1
## New Scripts

@ -17,7 +17,7 @@ namespace embark_assist {
namespace defs {
// Survey types
//
enum class river_sizes {
enum class river_sizes : int8_t {
None,
Brook,
Stream,
@ -26,8 +26,27 @@ namespace embark_assist {
Major
};
enum class aquifer_sizes : int8_t {
NA,
None,
Light,
None_Light,
Heavy,
None_Heavy,
Light_Heavy,
None_Light_Heavy
};
enum class tree_levels : int8_t {
None,
Very_Scarce,
Scarce,
Woodland,
Heavily_Forested
};
struct mid_level_tile {
bool aquifer = false;
aquifer_sizes aquifer = aquifer_sizes::NA;
bool clay = false;
bool sand = false;
bool flux = false;
@ -40,6 +59,7 @@ namespace embark_assist {
int8_t adamantine_level; // -1 = none, 0 .. 3 = cavern 1 .. magma sea. Currently not used beyond present/absent.
int8_t magma_level; // -1 = none, 0 .. 3 = cavern 3 .. surface/volcano
int8_t biome_offset;
tree_levels trees;
uint8_t savagery_level; // 0 - 2
uint8_t evilness_level; // 0 - 2
std::vector<bool> metals;
@ -51,7 +71,7 @@ namespace embark_assist {
struct region_tile_datum {
bool surveyed = false;
uint16_t aquifer_count = 0;
aquifer_sizes aquifer = aquifer_sizes::NA;
uint16_t clay_count = 0;
uint16_t sand_count = 0;
uint16_t flux_count = 0;
@ -65,6 +85,8 @@ namespace embark_assist {
uint8_t biome_count;
int16_t min_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
int16_t max_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
tree_levels min_tree_level = embark_assist::defs::tree_levels::Heavily_Forested;
tree_levels max_tree_level = embark_assist::defs::tree_levels::None;
bool blood_rain[10];
bool blood_rain_possible;
bool blood_rain_full;
@ -85,6 +107,8 @@ namespace embark_assist {
std::vector<bool> metals;
std::vector<bool> economics;
std::vector<bool> minerals;
std::vector<int16_t> neighbors; // entity_raw indices
uint8_t necro_neighbors;
mid_level_tile north_row[16];
mid_level_tile south_row[16];
mid_level_tile west_column[16];
@ -121,8 +145,7 @@ namespace embark_assist {
struct site_infos {
bool incursions_processed;
bool aquifer;
bool aquifer_full;
aquifer_sizes aquifer;
uint8_t min_soil;
uint8_t max_soil;
bool flat;
@ -140,6 +163,8 @@ namespace embark_assist {
std::vector<uint16_t> economics;
std::vector<uint16_t> minerals;
// Could add savagery, evilness, and biomes, but DF provides those easily.
std::vector<int16_t> neighbors; // entity_raw indices
uint8_t necro_neighbors;
};
typedef std::vector<sites> site_lists;
@ -176,11 +201,17 @@ namespace embark_assist {
enum class aquifer_ranges : int8_t {
NA = -1,
All,
Present,
Partial,
Not_All,
Absent
None,
At_Most_Light,
None_Plus_Light,
None_Plus_At_Least_Light,
Light,
At_Least_Light,
None_Plus_Heavy,
At_Most_Light_Plus_Heavy,
Light_Plus_Heavy,
None_Light_Heavy,
Heavy
};
enum class river_ranges : int8_t {
@ -263,6 +294,20 @@ namespace embark_assist {
Never
};
enum class tree_ranges : int8_t {
NA = -1,
None,
Very_Scarce, // DF dislays this with a different color but still the "scarce" text
Scarce,
Woodland,
Heavily_Forested
};
struct neighbor {
int16_t entity_raw; // entity_raw
present_absent_ranges present;
};
struct finders {
uint16_t x_dim;
uint16_t y_dim;
@ -296,6 +341,8 @@ namespace embark_assist {
int8_t biome_1; // N/A(-1), df::biome_type
int8_t biome_2; // N/A(-1), df::biome_type
int8_t biome_3; // N/A(-1), df::biome_type
tree_ranges min_trees;
tree_ranges max_trees;
int16_t metal_1; // N/A(-1), 0-max_inorganic;
int16_t metal_2; // N/A(-1), 0-max_inorganic;
int16_t metal_3; // N/A(-1), 0-max_inorganic;
@ -305,6 +352,11 @@ namespace embark_assist {
int16_t mineral_1; // N/A(-1), 0-max_inorganic;
int16_t mineral_2; // N/A(-1), 0-max_inorganic;
int16_t mineral_3; // N/A(-1), 0-max_inorganic;
int8_t min_necro_neighbors; // N/A(-1), 0 - 9, where 9 = 9+
int8_t max_necro_neighbors; // N/A(-1), 0 - 9, where 9 = 9+
int8_t min_civ_neighbors; // N/A(-1), 0 - 9, where 9 = 9+
int8_t max_civ_neighbors; // N/A(-1), 0 - 9, where 9 = 9+
std::vector<neighbor> neighbors;
};
struct match_iterators {

@ -311,7 +311,7 @@ command_result embark_assistant(color_ostream &out, std::vector <std::string> &
for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) {
embark_assist::main::state->survey_results[i][k].surveyed = false;
embark_assist::main::state->survey_results[i][k].aquifer_count = 0;
embark_assist::main::state->survey_results[i][k].aquifer = embark_assist::defs::aquifer_sizes::NA;
embark_assist::main::state->survey_results[i][k].clay_count = 0;
embark_assist::main::state->survey_results[i][k].sand_count = 0;
embark_assist::main::state->survey_results[i][k].flux_count = 0;
@ -319,6 +319,8 @@ command_result embark_assistant(color_ostream &out, std::vector <std::string> &
embark_assist::main::state->survey_results[i][k].max_region_soil = 0;
embark_assist::main::state->survey_results[i][k].max_waterfall = 0;
embark_assist::main::state->survey_results[i][k].river_size = embark_assist::defs::river_sizes::None;
embark_assist::main::state->survey_results[i][k].min_tree_level = embark_assist::defs::tree_levels::Heavily_Forested;
embark_assist::main::state->survey_results[i][k].max_tree_level = embark_assist::defs::tree_levels::None;
for (uint8_t l = 1; l < 10; l++) {
embark_assist::main::state->survey_results[i][k].biome_index[l] = -1;

@ -8,11 +8,14 @@
#include "MemAccess.h"
#include "df/biome_type.h"
#include "df/entity_raw.h"
#include "df/entity_raw_flags.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_data.h"
#include "df/world_raws.h"
#include "df/world_region_type.h"
#include "df/world_raws.h"
@ -64,6 +67,8 @@ namespace embark_assist {
biome_1,
biome_2,
biome_3,
min_trees,
max_trees,
metal_1,
metal_2,
metal_3,
@ -72,10 +77,15 @@ namespace embark_assist {
economic_3,
mineral_1,
mineral_2,
mineral_3
mineral_3,
min_necro_neighbors,
max_necro_neighbors,
min_civ_neighbors,
max_civ_neighbors,
neighbors
};
fields first_fields = fields::x_dim;
fields last_fields = fields::mineral_3;
fields last_fields = fields::neighbors;
struct display_map_elements {
std::string text;
@ -104,6 +114,11 @@ namespace embark_assist {
const DFHack::Screen::Pen white_pen(' ', COLOR_WHITE);
const DFHack::Screen::Pen lr_pen(' ', COLOR_LIGHTRED);
struct civ_entities {
int16_t id;
std::string description;
};
//==========================================================================================================
struct states {
@ -113,6 +128,7 @@ namespace embark_assist {
uint16_t finder_list_focus;
bool finder_list_active;
uint16_t max_inorganic;
std::vector<civ_entities> civs;
};
static states *state = 0;
@ -152,20 +168,26 @@ namespace embark_assist {
FILE* outfile = fopen(profile_file_name, "w");
fields i = first_fields;
size_t civ = 0;
while (true) {
for (size_t k = 0; k < state->ui[static_cast<int8_t>(i)]->list.size(); k++) {
if (state->ui[static_cast<int8_t>(i)]->current_value == state->ui[static_cast<int8_t>(i)]->list[k].key) {
fprintf(outfile, "[%s:%s]\n", state->finder_list[static_cast<int8_t>(i)].text.c_str(), state->ui[static_cast<int8_t>(i)]->list[k].text.c_str());
if (state->ui[static_cast<int8_t>(i) + civ]->current_value == state->ui[static_cast<int8_t>(i) + civ]->list[k].key) {
fprintf(outfile, "[%s:%s]\n", state->finder_list[static_cast<int8_t>(i) + civ].text.c_str(), state->ui[static_cast<int8_t>(i) + civ]->list[k].text.c_str());
break;
}
}
// fprintf(outfile, "[%s:%i]\n", state->finder_list[static_cast<int8_t>(i)].text.c_str(), state->ui[static_cast<int8_t>(i)]->current_value);
if (i == last_fields) {
break; // done
}
civ++;
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
if (civ == state->civs.size()) {
break; // done
}
}
else {
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
}
}
fclose(outfile);
@ -176,6 +198,7 @@ namespace embark_assist {
void load_profile() {
color_ostream_proxy out(Core::getInstance().getConsole());
FILE* infile = fopen(profile_file_name, "r");
size_t civ = 0;
if (!infile) {
out.printerr("No profile file found at %s\n", profile_file_name);
@ -197,8 +220,8 @@ namespace embark_assist {
for (int k = 1; k < count; k++) {
if (line[k] == ':') {
for (int l = 1; l < k; l++) {
if (state->finder_list[static_cast<int8_t>(i)].text.c_str()[l - 1] != line[l]) {
out.printerr("Token mismatch of %s vs %s\n", line, state->finder_list[static_cast<int8_t>(i)].text.c_str());
if (state->finder_list[static_cast<int8_t>(i) + civ].text.c_str()[l - 1] != line[l]) {
out.printerr("Token mismatch of %s vs %s\n", line, state->finder_list[static_cast<int8_t>(i) + civ].text.c_str());
fclose(infile);
return;
}
@ -206,10 +229,10 @@ namespace embark_assist {
found = false;
for (size_t l = 0; l < state->ui[static_cast<int8_t>(i)]->list.size(); l++) {
for (size_t l = 0; l < state->ui[static_cast<int8_t>(i) + civ]->list.size(); l++) {
for (int m = k + 1; m < count; m++) {
if (state->ui[static_cast<int8_t>(i)]->list[l].text.c_str()[m - (k + 1)] != line[m]) {
if (state->ui[static_cast<int8_t>(i)]->list[l].text.c_str()[m - (k + 1)] == '\0' &&
if (state->ui[static_cast<int8_t>(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) {
if (state->ui[static_cast<int8_t>(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' &&
line[m] == ']') {
found = true;
}
@ -238,10 +261,15 @@ namespace embark_assist {
}
if (i == last_fields) {
break; // done
}
civ++;
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
if (civ == state->civs.size()) {
break; // done
}
}
else {
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
}
}
fclose(infile);
@ -250,6 +278,7 @@ namespace embark_assist {
infile = fopen(profile_file_name, "r");
i = first_fields;
civ = 0;
while (true) {
if (!fgets(line, count, infile))
@ -262,13 +291,13 @@ namespace embark_assist {
found = false;
for (size_t l = 0; l < state->ui[static_cast<int8_t>(i)]->list.size(); l++) {
for (size_t l = 0; l < state->ui[static_cast<int8_t>(i) + civ]->list.size(); l++) {
for (int m = k + 1; m < count; m++) {
if (state->ui[static_cast<int8_t>(i)]->list[l].text.c_str()[m - (k + 1)] != line[m]) {
if (state->ui[static_cast<int8_t>(i)]->list[l].text.c_str()[m - (k + 1)] == '\0' &&
if (state->ui[static_cast<int8_t>(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) {
if (state->ui[static_cast<int8_t>(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' &&
line[m] == ']') {
state->ui[static_cast<int8_t>(i)]->current_value = state->ui[static_cast<int8_t>(i)]->list[l].key;
state->ui[static_cast<int8_t>(i)]->current_display_value = l;
state->ui[static_cast<int8_t>(i) + civ]->current_value = state->ui[static_cast<int8_t>(i) + civ]->list[l].key;
state->ui[static_cast<int8_t>(i) + civ]->current_display_value = l;
found = true;
}
@ -285,10 +314,15 @@ namespace embark_assist {
}
if (i == last_fields) {
break; // done
}
civ++;
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
if (civ == state->civs.size()) {
break; // done
}
}
else {
i = static_cast <fields>(static_cast<int8_t>(i) + 1);
}
}
fclose(infile);
@ -308,6 +342,30 @@ namespace embark_assist {
fields i = first_fields;
ui_lists *element;
int16_t controllable_civs = 0;
int16_t max_civs;
for (int16_t i = 0; i < (int16_t)world->raws.entities.size(); i++) {
if (world->raws.entities[i]->flags.is_set(df::entity_raw_flags::CIV_CONTROLLABLE)) controllable_civs++;
}
for (int16_t i = 0; i < (int16_t)world->raws.entities.size(); i++) {
if (!world->raws.entities[i]->flags.is_set(df::entity_raw_flags::LAYER_LINKED) && // Animal people
!world->raws.entities[i]->flags.is_set(df::entity_raw_flags::GENERATED) && // Vault guardians
(controllable_civs > 1 || // Suppress the playable civ when only 1
!world->raws.entities[i]->flags.is_set(df::entity_raw_flags::CIV_CONTROLLABLE))) { // Too much work to change dynamically for modded worlds.
if (world->raws.entities[i]->translation == "") {
state->civs.push_back({ i, world->raws.entities[i]->code }); // Kobolds don't have a translation...
}
else {
state->civs.push_back({ i, world->raws.entities[i]->translation });
}
}
}
max_civs = state->civs.size();
if (controllable_civs > 1) max_civs = max_civs - 1;
while (true) {
element = new ui_lists;
@ -376,28 +434,52 @@ namespace embark_assist {
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::All:
element->list.push_back({ "All", static_cast<int8_t>(k) });
case embark_assist::defs::aquifer_ranges::None:
element->list.push_back({ "None", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Present:
element->list.push_back({ "Present", static_cast<int8_t>(k) });
case embark_assist::defs::aquifer_ranges::At_Most_Light:
element->list.push_back({ "<= Light", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Partial:
element->list.push_back({ "Partial", static_cast<int8_t>(k) });
case embark_assist::defs::aquifer_ranges::None_Plus_Light:
element->list.push_back({ "None + Light", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Not_All:
element->list.push_back({ "Not All", static_cast<int8_t>(k) });
case embark_assist::defs::aquifer_ranges::None_Plus_At_Least_Light:
element->list.push_back({ "None + >= Light", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Absent:
element->list.push_back({ "Absent", static_cast<int8_t>(k) });
case embark_assist::defs::aquifer_ranges::Light:
element->list.push_back({ "Light", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::At_Least_Light:
element->list.push_back({ ">= Light", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::None_Plus_Heavy:
element->list.push_back({ "None + Heavy", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::At_Most_Light_Plus_Heavy:
element->list.push_back({ "<= Light + Heavy", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Light_Plus_Heavy:
element->list.push_back({ "Light + Heavy", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::None_Light_Heavy:
element->list.push_back({ "None + Light + Heavy", static_cast<int8_t>(k) });
break;
case embark_assist::defs::aquifer_ranges::Heavy:
element->list.push_back({ "Heavy", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::aquifer_ranges::Absent) {
if (k == embark_assist::defs::aquifer_ranges::Heavy) {
break;
}
@ -836,6 +918,47 @@ namespace embark_assist {
}
break;
case fields::min_trees:
case fields::max_trees:
{
embark_assist::defs::tree_ranges k = embark_assist::defs::tree_ranges::NA;
while (true) {
switch (k) {
case embark_assist::defs::tree_ranges::NA:
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::tree_ranges::None:
element->list.push_back({ "None", static_cast<int8_t>(k) });
break;
case embark_assist::defs::tree_ranges::Very_Scarce:
element->list.push_back({ "Very Scarce", static_cast<int8_t>(k) });
break;
case embark_assist::defs::tree_ranges::Scarce:
element->list.push_back({ "Scarce", static_cast<int8_t>(k) });
break;
case embark_assist::defs::tree_ranges::Woodland:
element->list.push_back({ "Woodland", static_cast<int8_t>(k) });
break;
case embark_assist::defs::tree_ranges::Heavily_Forested:
element->list.push_back({ "Heavily Forested", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::tree_ranges::Heavily_Forested) {
break;
}
k = static_cast <embark_assist::defs::tree_ranges>(static_cast<int8_t>(k) + 1);
}
}
break;
case fields::metal_1:
case fields::metal_2:
case fields::metal_3:
@ -918,10 +1041,73 @@ namespace embark_assist {
name.clear();
}
break;
case fields::min_necro_neighbors:
case fields::max_necro_neighbors:
for (int16_t k = -1; k <= 16; k++) {
if (k == -1) {
element->list.push_back({ "N/A", k });
}
else {
element->list.push_back({ std::to_string(k), k });
}
}
break;
case fields::min_civ_neighbors:
case fields::max_civ_neighbors:
for (int16_t k = -1; k <= max_civs; k++) {
if (k == -1) {
element->list.push_back({ "N/A", k });
}
else {
element->list.push_back({ std::to_string(k), k });
}
}
break;
case fields::neighbors:
for (size_t l = 0; l < state->civs.size(); l++) {
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<int8_t>(k) });
break;
case embark_assist::defs::present_absent_ranges::Present:
element->list.push_back({ "Present", static_cast<int8_t>(k) });
break;
case embark_assist::defs::present_absent_ranges::Absent:
element->list.push_back({ "Absent", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::present_absent_ranges::Absent) {
break;
}
k = static_cast <embark_assist::defs::present_absent_ranges>(static_cast<int8_t>(k) + 1);
}
if (l < state->civs.size() - 1) {
element->current_value = element->list[0].key;
state->ui.push_back(element);
element = new ui_lists;
element->current_display_value = 0;
element->current_index = 0;
element->focus = 0;
}
}
break;
}
element->current_value = element->list[0].key;
state->ui.push_back(element);
switch (i) {
@ -1069,6 +1255,14 @@ namespace embark_assist {
state->finder_list.push_back({ "Biome 3", static_cast<int8_t>(i) });
break;
case fields::min_trees:
state->finder_list.push_back({ "Min Trees", static_cast<int8_t>(i) });
break;
case fields::max_trees:
state->finder_list.push_back({ "Max Trees", static_cast<int8_t>(i) });
break;
case fields::metal_1:
state->finder_list.push_back({ "Metal 1", static_cast<int8_t>(i) });
break;
@ -1104,6 +1298,28 @@ namespace embark_assist {
case fields::mineral_3:
state->finder_list.push_back({ "Mineral 3", static_cast<int8_t>(i) });
break;
case fields::min_necro_neighbors:
state->finder_list.push_back({ "Min Necro Tower", static_cast<int8_t>(i) });
break;
case fields::max_necro_neighbors:
state->finder_list.push_back({ "Max Necro Tower", static_cast<int8_t>(i) });
break;
case fields::min_civ_neighbors:
state->finder_list.push_back({ "Min Near Civs", static_cast<int8_t>(i) });
break;
case fields::max_civ_neighbors:
state->finder_list.push_back({ "Max Near Civs", static_cast<int8_t>(i) });
break;
case fields::neighbors:
for (uint8_t k = 0; k < state->civs.size(); k++) {
state->finder_list.push_back({ state->civs[k].description, (int16_t)(static_cast<int8_t>(i) + k) });
}
break;
}
if (i == last_fields) {
@ -1136,7 +1352,6 @@ namespace embark_assist {
state->ui[static_cast<int8_t>(fields::y_dim)]->current_display_value + 1;
}
//==========================================================================================================
void find() {
@ -1312,6 +1527,14 @@ namespace embark_assist {
finder.biome_3 = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::min_trees:
finder.min_trees = static_cast<embark_assist::defs::tree_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::max_trees:
finder.max_trees = static_cast<embark_assist::defs::tree_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::metal_1:
finder.metal_1 = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
@ -1347,6 +1570,28 @@ namespace embark_assist {
case fields::mineral_3:
finder.mineral_3 = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::min_necro_neighbors:
finder.min_necro_neighbors = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::max_necro_neighbors:
finder.max_necro_neighbors = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::min_civ_neighbors:
finder.min_civ_neighbors = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::max_civ_neighbors:
finder.max_civ_neighbors = state->ui[static_cast<uint8_t>(i)]->current_value;
break;
case fields::neighbors:
for (size_t k = 0; k < state->civs.size(); k++) {
finder.neighbors.push_back({ state->civs[k].id, static_cast<embark_assist::defs::present_absent_ranges>(state->ui[static_cast<uint8_t>(i) + k]->current_value) });
}
break;
}
if (i == last_fields) {
@ -1394,7 +1639,7 @@ namespace embark_assist {
state->finder_list_focus--;
}
else {
state->finder_list_focus = static_cast<uint16_t>(last_fields);
state->finder_list_focus = static_cast<uint16_t>(last_fields) + state->civs.size() - 1;
}
}
else {
@ -1407,7 +1652,7 @@ namespace embark_assist {
} else if (input->count(df::interface_key::STANDARDSCROLL_DOWN)) {
if (state->finder_list_active) {
if (state->finder_list_focus < static_cast<uint16_t>(last_fields)) {
if (state->finder_list_focus < static_cast<uint16_t>(last_fields) + state->civs.size() - 1) {
state->finder_list_focus++;
} else {
state->finder_list_focus = 0;

@ -178,7 +178,7 @@ namespace embark_assist{
help_text.push_back("Clay, if present, including thorugh incursions.");
help_text.push_back("Min and Max soil depth in the embark rectangle, including incursions.");
help_text.push_back("Flat indicator if all the tiles and incursions have the same elevation.");
help_text.push_back("Aquifer indicator: Part(ial) or Full, when present, including incursions.");
help_text.push_back("Aquifer indicator: 'No', 'Lt', 'Hv' for presence/type including incursions.");
help_text.push_back("Waterfall and largest Z level drop if the river has elevation differences");
help_text.push_back("Evil weather, when present: BR = Blood Rain, TS = Temporary Syndrome");
help_text.push_back("PS = Permanent Syndrome, Re = Reanimating, and Th = Thralling. Incursions.");
@ -186,6 +186,7 @@ namespace embark_assist{
help_text.push_back("A list of all metals present in the embark. Not incursions.");
help_text.push_back("A list of all economic minerals present in the embark. Both clays and flux");
help_text.push_back("stones are economic, so they show up here as well. Not incursions.");
help_text.push_back("Neighbors, including Kobolds (SKULKING) (hidden in vanilla) and Towers.");
help_text.push_back("In addition to the above, the Find functionality can also produce blinking");
help_text.push_back("overlays over the Local, Region, and World maps to indicate where");
help_text.push_back("matching embarks are found. The Local display marks the top left corner of");
@ -215,11 +216,7 @@ namespace embark_assist{
help_text.push_back("Present means at least one embark tile has to have this value.");
help_text.push_back("Absent means the feature mustn't exist in any of the embark tiles.");
help_text.push_back("N/A means no restrictions are applied.");
help_text.push_back("The Aquifer criterion introduces some new parameters:");
help_text.push_back("Partial means at least one tile has to have an aquifer, but it also has");
help_text.push_back("to be absent from at least one tile. Not All means an aquifer is tolerated");
help_text.push_back("as long as at least one tile doesn't have one, but it doesn't have to have");
help_text.push_back("any at all.");
help_text.push_back("The Aquifer criterion allows you to search for a number of combinations.");
help_text.push_back("Min/Max rivers should be self explanatory. The Yes and No values of");
help_text.push_back("Clay, etc. means one has to be Present and Absent respectivey.");
help_text.push_back("Min Waterfall Drop finds embarks with drops of at least that number");
@ -238,8 +235,10 @@ namespace embark_assist{
help_text.push_back("The parameters for biomes, regions, etc. all require that the required");
help_text.push_back("feature is Present in the embark, and entering the same value multiple");
help_text.push_back("times does nothing (the first match ticks all requirements off). It can be");
help_text.push_back("noted that all the Economic materials are found in the much longer Mineral");
help_text.push_back("list. Note that Find is a fairly time consuming task (as it is in vanilla).");
help_text.push_back("noted that all the Economic materials are found among the longer Minerals.");
help_text.push_back("Min/Max Necro Towers and Min/Max Near Civs work on neighbor counts, while");
help_text.push_back("the civ entity selections let you choose your specific neighbors.");
help_text.push_back("Note that Find is a fairly time consuming task (as it is in vanilla).");
break;
case pages::Caveats_1:
@ -266,6 +265,10 @@ namespace embark_assist{
help_text.push_back("ones.");
help_text.push_back("");
help_text.push_back("Caveats & technical stuff:");
help_text.push_back("- embark-tools' neutralizing of DF random rectangle placement on world");
help_text.push_back(" tile shifts changes placement info after it's read. This is compensated");
help_text.push_back(" for, but at the cost of incorrect info without it. In tile move back");
help_text.push_back(" and forth causes the correct info to be displayed.");
help_text.push_back("- The plugin does in fact allow for a single, optional case sensitive");
help_text.push_back(" parameter when invoked: 'fileresult'. When this parameter is provided");
help_text.push_back(" The plugin will read the search profile stored to file and immediately");
@ -293,11 +296,10 @@ namespace embark_assist{
case pages::Caveats_2:
Screen::drawBorder(" Embark Assistant Help/Info Caveats 2 Page ");
help_text.push_back("- The site info is deduced by the author, so there may be errors and");
help_text.push_back(" there are probably site types that end up not being identified.");
help_text.push_back("- Aquifer indications are based on the author's belief that they occur");
help_text.push_back(" whenever an aquifer supporting layer is present at a depth of 3 or");
help_text.push_back(" more.");
help_text.push_back(" more. In addition, Toady's description on how Heavy aquifers are");
help_text.push_back(" selected has been used.");
help_text.push_back("- Thralling is determined by whether material interactions causes");
help_text.push_back(" blinking, which the author believes is one of 4 thralling changes.");
help_text.push_back("- The geo information is gathered by code which is essentially a");
@ -320,10 +322,11 @@ namespace embark_assist{
help_text.push_back(" whether to make a second, completing, search.");
help_text.push_back("- Incursions are taken into consideration when looking for Aquifers,");
help_text.push_back(" Clay, Sand, Min Soil when Everywhere, Biomes, Regions, Evil Weather,");
help_text.push_back(" Savagery, Evilness, Freezing and Flatness, but ignored for metals/");
help_text.push_back(" economics/minerals (including Flux and Coal) as any volumes are typically");
help_text.push_back(" too small to be of interest. Rivers, Waterfalls, Spires, and Magma Pools");
help_text.push_back(" are not incursion related features.");
help_text.push_back(" Savagery, Evilness, Freezing and Flatness, and trees, but ignored for");
help_text.push_back(" metals/economics/minerals (including Flux and Coal) as any volumes are");
help_text.push_back(" typically too small to be of interest. Rivers, Waterfalls, Spires, and");
help_text.push_back(" Magma Pools are not incursion related features.");
help_text.push_back("- Neighbor determination makes use of a flag in entities.");
help_text.push_back("- There are special rules for handing of incursions from Lakes and Oceans,");
help_text.push_back(" as well as Mountains into everything that isn't a Lake or Ocean, and the");
help_text.push_back(" rules state that these incursions should be reversed (i.e. 'normal' biomes");
@ -335,7 +338,7 @@ namespace embark_assist{
help_text.push_back(" the N, followed by the one to the W, and lastly the one acting as the");
help_text.push_back(" reference. This means there's a risk embarks with such 'trouble' corners");
help_text.push_back(" may get affected corner(s) evaluated incorrectly.");
help_text.push_back("Version 0.10 2019-09-21");
help_text.push_back("Version 0.11 2020-03-03");
break;
}

File diff suppressed because it is too large Load Diff

@ -1,6 +1,7 @@
#include <modules/Gui.h>
#include "df/coord2d.h"
#include "df/entity_raw.h"
#include "df/inorganic_raw.h"
#include "df/dfhack_material_category.h"
#include "df/interface_key.h"
@ -369,14 +370,50 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Flat" });
}
if (site_info->aquifer) {
if (site_info->aquifer_full) {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Full Aquifer" });
}
else {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Part. Aquifer" });
if (site_info->aquifer != embark_assist::defs::aquifer_sizes::None) {
std::string none = " ";
std::string light = " ";
std::string heavy = " ";
std::string no = "No ";
std::string lt = "Lt ";
std::string hv = "Hv";
switch (site_info->aquifer) {
case embark_assist::defs::aquifer_sizes::NA:
case embark_assist::defs::aquifer_sizes::None: // Neither of these should appear
break;
case embark_assist::defs::aquifer_sizes::Light:
light = lt;
break;
case embark_assist::defs::aquifer_sizes::None_Light:
none = no;
light = lt;
break;
case embark_assist::defs::aquifer_sizes::Heavy:
heavy = hv;
break;
case embark_assist::defs::aquifer_sizes::None_Heavy:
none = no;
heavy = hv;
break;
case embark_assist::defs::aquifer_sizes::Light_Heavy:
light = lt;
heavy = hv;
break;
case embark_assist::defs::aquifer_sizes::None_Light_Heavy:
none = no;
light = lt;
heavy = hv;
break;
}
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Aq: " + none + light + heavy });
}
if (site_info->max_waterfall > 0) {
@ -443,6 +480,20 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in
for (auto const& i : site_info->economics) {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), world->raws.inorganics[i]->id });
}
for (uint16_t i = 0; i < site_info->neighbors.size(); i++) {
if (world->raws.entities[site_info->neighbors[i]]->translation == "") {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_YELLOW), world->raws.entities[site_info->neighbors[i]]->code }); // Kobolds have an empty translation field
}
else
{
state->embark_info.push_back({ Screen::Pen(' ', COLOR_YELLOW), world->raws.entities[site_info->neighbors[i]]->translation });
}
}
if (site_info->necro_neighbors > 0) {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTRED), "Towers: " + std::to_string(site_info->necro_neighbors) });
}
}
//====================================================================

@ -16,11 +16,14 @@
#include "df/creature_interaction_effect.h"
#include "df/creature_interaction_effect_display_symbolst.h"
#include "df/creature_interaction_effect_type.h"
#include "df/entity_raw.h"
#include "df/feature_init.h"
#include "df/feature_init_deep_special_tubest.h"
#include "df/feature_init_magma_poolst.h"
#include "df/feature_init_volcanost.h"
#include "df/feature_type.h"
#include "df/historical_entity.h"
#include "df/historical_entity_type.h"
#include "df/inorganic_flags.h"
#include "df/inorganic_raw.h"
#include "df/interaction.h"
@ -504,13 +507,7 @@ namespace embark_assist {
int16_t elevation,
uint16_t x,
uint16_t y) {
if (mlt->aquifer) {
site_info->aquifer = true;
}
else {
site_info->aquifer_full = false;
}
site_info->aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(mlt->aquifer) | static_cast<int8_t>(site_info->aquifer));
if (mlt->soil_depth < site_info->min_soil) {
site_info->min_soil = mlt->soil_depth;
@ -760,6 +757,34 @@ void embark_assist::survey::clear_results(embark_assist::defs::match_results *ma
//=================================================================================
embark_assist::defs::tree_levels tree_level_of(df::world_region_type region_type, int16_t vegetation) {
if (region_type == df::world_region_type::Glacier ||
region_type == df::world_region_type::Lake ||
region_type == df::world_region_type::Mountains ||
region_type == df::world_region_type::Ocean) {
return embark_assist::defs::tree_levels::None;
}
else {
if (vegetation == 0) {
return embark_assist::defs::tree_levels::None;
}
else if (vegetation <= 9) {
return embark_assist::defs::tree_levels::Very_Scarce;
}
else if (vegetation <= 32) {
return embark_assist::defs::tree_levels::Scarce;
}
else if (vegetation <= 65) {
return embark_assist::defs::tree_levels::Woodland;
}
else {
return embark_assist::defs::tree_levels::Heavily_Forested;
}
}
}
//=================================================================================
void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_data *geo_summary,
embark_assist::defs::world_tile_data *survey_results) {
// color_ostream_proxy out(Core::getInstance().getConsole());
@ -777,7 +802,7 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
uint8_t offset_count = 0;
auto &results = survey_results->at(i).at(k);
results.surveyed = false;
results.aquifer_count = 0;
results.aquifer = embark_assist::defs::aquifer_sizes::NA;
results.clay_count = 0;
results.sand_count = 0;
results.flux_count = 0;
@ -785,6 +810,8 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
results.min_region_soil = 10;
results.max_region_soil = 0;
results.max_waterfall = 0;
results.min_tree_level = embark_assist::defs::tree_levels::Heavily_Forested;
results.max_tree_level = embark_assist::defs::tree_levels::None;
results.savagery_count[0] = 0;
results.savagery_count[1] = 0;
results.savagery_count[2] = 0;
@ -823,7 +850,16 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
results.min_temperature[l] = min_temperature(results.max_temperature[l], adjusted.y);
geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index;
if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++;
if (geo_summary->at(geo_index).aquifer_absent) {
results.aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(results.aquifer) | 1);
}
else if (world_data->region_map[adjusted.x][adjusted.y].drainage % 20 == 7) {
results.aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(results.aquifer) | 4);
}
else {
results.aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(results.aquifer) | 2);
}
if (!geo_summary->at(geo_index).clay_absent) results.clay_count++;
if (!geo_summary->at(geo_index).sand_absent) results.sand_count++;
if (!geo_summary->at(geo_index).flux_absent) results.flux_count++;
@ -848,6 +884,12 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
if (geo_summary->at(geo_index).possible_economics[m]) results.economics[m] = true;
if (geo_summary->at(geo_index).possible_minerals[m]) results.minerals[m] = true;
}
embark_assist::defs::tree_levels tree_level = tree_level_of(world_data->regions[results.biome_index[l]]->type,
world_data->region_map[adjusted.x][adjusted.y].vegetation);
if (tree_level < results.min_tree_level) results.min_tree_level = tree_level;
if (tree_level > results.max_tree_level) results.max_tree_level = tree_level;
}
else {
results.biome_index[l] = -1;
@ -862,7 +904,6 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
if (results.biome[l] != -1) results.biome_count++;
}
if (results.aquifer_count == offset_count) results.aquifer_count = 256;
if (results.clay_count == offset_count) results.clay_count = 256;
if (results.sand_count == offset_count) results.sand_count = 256;
if (results.flux_count == offset_count) results.flux_count = 256;
@ -906,6 +947,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
uint16_t end_check_l;
uint16_t end_check_m;
uint16_t end_check_n;
bool aquifer;
for (uint16_t i = 0; i < state->max_inorganic; i++) {
tile->metals[i] = 0;
@ -1026,7 +1068,8 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
int16_t layer_shift[16];
int16_t cur_shift = elevation + soil_erosion - 1;
mlt->at(i).at(k).aquifer = false;
aquifer = false;
mlt->at(i).at(k).aquifer = embark_assist::defs::aquifer_sizes::NA;
mlt->at(i).at(k).clay = false;
mlt->at(i).at(k).sand = false;
mlt->at(i).at(k).flux = false;
@ -1129,7 +1172,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
}
for (uint16_t m = 0; m < state->coals.size(); m++) {
if (layer->mat_index == state->coals [m]) {
if (layer->mat_index == state->coals[m]) {
mlt->at(i).at(k).coal = true;
break;
}
@ -1162,7 +1205,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
}
for (uint16_t n = 0; n < state->coals.size(); n++) {
if (layer->vein_mat [m] == state->coals[n]) {
if (layer->vein_mat[m] == state->coals[n]) {
mlt->at(i).at(k).coal = true;
break;
}
@ -1172,10 +1215,22 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
if (bottom_z <= elevation - 3 &&
world->raws.inorganics[layer->mat_index]->flags.is_set(df::inorganic_flags::AQUIFER)) {
mlt->at(i).at(k).aquifer = true;
aquifer = true;
}
}
}
if (!aquifer) {
mlt->at(i).at(k).aquifer = embark_assist::defs::aquifer_sizes::None;
}
else if (world_data->region_map[adjusted.x][adjusted.y].drainage % 20 == 7) {
mlt->at(i).at(k).aquifer = embark_assist::defs::aquifer_sizes::Heavy;
}
else {
mlt->at(i).at(k).aquifer = embark_assist::defs::aquifer_sizes::Light;
}
mlt->at(i).at(k).trees = tree_level_of(world_data->regions[world_data->region_map[adjusted.x][adjusted.y].region_id]->type,
world_data->region_map[adjusted.x][adjusted.y].vegetation);
}
}
@ -1224,7 +1279,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
}
}
survey_results->at(x).at(y).aquifer_count = 0;
survey_results->at(x).at(y).aquifer = embark_assist::defs::aquifer_sizes::NA;
survey_results->at(x).at(y).clay_count = 0;
survey_results->at(x).at(y).sand_count = 0;
survey_results->at(x).at(y).flux_count = 0;
@ -1240,7 +1295,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
for (uint8_t i = 0; i < 16; i++) {
for (uint8_t k = 0; k < 16; k++) {
if (mlt->at(i).at(k).aquifer) { survey_results->at(x).at(y).aquifer_count++; }
survey_results->at(x).at(y).aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(survey_results->at(x).at(y).aquifer) | static_cast<int8_t>(mlt->at(i).at(k).aquifer));
if (mlt->at(i).at(k).clay) { survey_results->at(x).at(y).clay_count++; }
if (mlt->at(i).at(k).sand) { survey_results->at(x).at(y).sand_count++; }
if (mlt->at(i).at(k).flux) { survey_results->at(x).at(y).flux_count++; }
@ -1279,6 +1334,9 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
// reanimating handled separately
// thralling handled separately
if (survey_results->at(x).at(y).min_tree_level > mlt->at(i).at(k).trees) survey_results->at(x).at(y).min_tree_level = mlt->at(i).at(k).trees;
if (survey_results->at(x).at(y).max_tree_level < mlt->at(i).at(k).trees) survey_results->at(x).at(y).max_tree_level = mlt->at(i).at(k).trees;
survey_results->at(x).at(y).savagery_count[mlt->at(i).at(k).savagery_level]++;
survey_results->at(x).at(y).evilness_count[mlt->at(i).at(k).evilness_level]++;
@ -1380,6 +1438,11 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
tile->west_column[i].biome_offset = mlt->at(0).at(i).biome_offset;
tile->east_column[i].biome_offset = mlt->at(15).at(i).biome_offset;
tile->north_row[i].trees = mlt->at(i).at(0).trees;
tile->south_row[i].trees = mlt->at(i).at(15).trees;
tile->west_column[i].trees = mlt->at(0).at(i).trees;
tile->east_column[i].trees = mlt->at(15).at(i).trees;
tile->north_row[i].savagery_level = mlt->at(i).at(0).savagery_level;
tile->south_row[i].savagery_level = mlt->at(i).at(15).savagery_level;
tile->west_column[i].savagery_level = mlt->at(0).at(i).savagery_level;
@ -1415,6 +1478,22 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
}
}
// Focus has to be at the world tile to get neighbor info
//
if (!tile->surveyed) {
for (uint16_t i = 0; i < world->entities.all.size(); i++) {
if (world->entities.all[i]->flags.bits.neighbor) {
if (world->entities.all[i]->type == df::historical_entity_type::SiteGovernment) {
tile->necro_neighbors++;
}
else
{
tile->neighbors.push_back(world->entities.all[i]->entity_raw->index);
}
}
}
}
tile->surveyed = true;
}
@ -2068,7 +2147,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
bool incursion_processing_failed = false;
df::world_data *world_data = world->world_data;
if (!use_cache) { // For some reason DF scrambles these values on world tile movements (at least in Lua...).
if (!use_cache) { // DF scrambles these values on world tile movements, while embark-tools stabilizes the movement, but its changes to the value are done after we've read them.
state->local_min_x = screen->location.embark_pos_min.x;
state->local_min_y = screen->location.embark_pos_min.y;
state->local_max_x = screen->location.embark_pos_max.x;
@ -2079,8 +2158,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
state->y = y;
site_info->incursions_processed = true;
site_info->aquifer = false;
site_info->aquifer_full = true;
site_info->aquifer = embark_assist::defs::aquifer_sizes::NA;
site_info->min_soil = 10;
site_info->max_soil = 0;
site_info->flat = true;
@ -2097,15 +2175,11 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
site_info->metals.clear();
site_info->economics.clear();
site_info->metals.clear();
site_info->neighbors.clear();
for (uint8_t i = state->local_min_x; i <= state->local_max_x; i++) {
for (uint8_t k = state->local_min_y; k <= state->local_max_y; k++) {
if (mlt->at(i).at(k).aquifer) {
site_info->aquifer = true;
}
else {
site_info->aquifer_full = false;
}
site_info->aquifer = static_cast<embark_assist::defs::aquifer_sizes>(static_cast<int8_t>(site_info->aquifer) | static_cast<int8_t>(mlt->at(i).at(k).aquifer));
if (mlt->at(i).at(k).soil_depth < site_info->min_soil) {
site_info->min_soil = mlt->at(i).at(k).soil_depth;
@ -2441,6 +2515,12 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
}
if (incursion_processing_failed) site_info->incursions_processed = false;
for (uint16_t i = 0; i < survey_results->at(x).at(y).neighbors.size(); i++) {
site_info->neighbors.push_back(survey_results->at(x).at(y).neighbors[i]);
}
site_info->necro_neighbors = survey_results->at(x).at(y).necro_neighbors;
}
//=================================================================================