#include #include #include #include "Core.h" #include #include #include #include #include "modules/Materials.h" #include "modules/Maps.h" #include "DataDefs.h" #include "df/builtin_mats.h" #include "df/coord2d.h" #include "df/creature_interaction_effect.h" #include "df/creature_interaction_effect_type.h" #include "df/entity_raw.h" #include "df/feature_init.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" #include "df/interaction_effect.h" #include "df/interaction_effect_type.h" #include "df/interaction_instance.h" #include "df/interaction_source.h" #include "df/interaction_source_type.h" #include "df/interaction_target.h" #include "df/interaction_target_materialst.h" #include "df/material_common.h" #include "df/reaction.h" #include "df/reaction_product_itemst.h" #include "df/reaction_product_type.h" #include "df/region_map_entry.h" #include "df/syndrome.h" #include "df/viewscreen.h" #include "df/viewscreen_choose_start_sitest.h" #include "df/world.h" #include "df/world_data.h" #include "df/world_geo_biome.h" #include "df/world_geo_layer.h" #include "df/world_raws.h" #include "df/world_region.h" #include "df/world_region_details.h" #include "df/world_region_feature.h" #include "df/world_region_type.h" #include "df/world_site.h" #include "df/world_site_type.h" #include "df/world_underground_region.h" #include "defs.h" #include "survey.h" using namespace DFHack; using namespace df::enums; using namespace Gui; using df::global::world; namespace embark_assist { namespace survey { struct states { uint16_t clay_reaction = -1; uint16_t flux_reaction = -1; std::vector coals; uint16_t x; uint16_t y; uint8_t local_min_x; uint8_t local_min_y; uint8_t local_max_x; uint8_t local_max_y; uint16_t max_inorganic; }; static states *state = nullptr; //======================================================================================= embark_assist::defs::river_sizes river_size_of(uint8_t width) { if (width < 7) { return embark_assist::defs::river_sizes::Stream; } else if (width < 13) { return embark_assist::defs::river_sizes::Minor; } else if (width < 24) { return embark_assist::defs::river_sizes::Medium; } else { return embark_assist::defs::river_sizes::Major; } } //======================================================================================= bool geo_survey(embark_assist::defs::geo_data *geo_summary) { color_ostream_proxy out(Core::getInstance().getConsole()); df::world_data *world_data = world->world_data; auto reactions = df::reaction::get_vector(); bool non_soil_found; uint16_t size; for (uint16_t i = 0; i < reactions.size(); i++) { if (reactions[i]->code == "MAKE_CLAY_BRICKS") { state->clay_reaction = i; } if (reactions[i]->code == "PIG_IRON_MAKING") { state->flux_reaction = i; } } if (state->clay_reaction == -1) { out.printerr("The reaction 'MAKE_CLAY_BRICKS' was not found, so clay can't be identified.\n"); } if (state->flux_reaction == -1) { out.printerr("The reaction 'PIG_IRON_MAKING' was not found, so flux can't be identified.\n"); } for (uint16_t i = 0; i < world->raws.inorganics.size(); i++) { for (uint16_t k = 0; k < world->raws.inorganics[i]->economic_uses.size(); k++) { for (uint16_t l = 0; l < world->raws.reactions.reactions[world->raws.inorganics[i]->economic_uses[k]]->products.size(); l++) { df::reaction_product_itemst *product = static_cast(world->raws.reactions.reactions[world->raws.inorganics[i]->economic_uses[k]]->products[l]); if (product->mat_type == df::builtin_mats::COAL) { state->coals.push_back(i); break; } } } } for (uint16_t i = 0; i < world_data->geo_biomes.size(); i++) { geo_summary->at(i).possible_metals.resize(state->max_inorganic); geo_summary->at(i).possible_economics.resize(state->max_inorganic); geo_summary->at(i).possible_minerals.resize(state->max_inorganic); non_soil_found = true; df::world_geo_biome *geo = world_data->geo_biomes[i]; for (uint16_t k = 0; k < geo->layers.size() && k < 16; k++) { df::world_geo_layer *layer = geo->layers[k]; if (layer->type == df::geo_layer_type::SOIL || layer->type == df::geo_layer_type::SOIL_SAND) { geo_summary->at(i).soil_size += layer->top_height - layer->bottom_height + 1; if (world->raws.inorganics[layer->mat_index]->flags.is_set(df::inorganic_flags::SOIL_SAND)) { geo_summary->at(i).sand_absent = false; } if (non_soil_found) { geo_summary->at(i).top_soil_only = false; } } else { non_soil_found = true; } geo_summary->at(i).possible_minerals[layer->mat_index] = true; size = (uint16_t)world->raws.inorganics[layer->mat_index]->metal_ore.mat_index.size(); for (uint16_t l = 0; l < size; l++) { geo_summary->at(i).possible_metals.at(world->raws.inorganics[layer->mat_index]->metal_ore.mat_index[l]) = true; } size = (uint16_t)world->raws.inorganics[layer->mat_index]->economic_uses.size(); if (size != 0) { geo_summary->at(i).possible_economics[layer->mat_index] = true; for (uint16_t l = 0; l < size; l++) { if (world->raws.inorganics[layer->mat_index]->economic_uses[l] == state->clay_reaction) { geo_summary->at(i).clay_absent = false; } if (world->raws.inorganics[layer->mat_index]->economic_uses[l] == state->flux_reaction) { geo_summary->at(i).flux_absent = false; } } } for (uint16_t l = 0; l < state->coals.size(); l++) { if (layer->mat_index == state->coals[l]) { geo_summary->at(i).coal_absent = false; break; } } size = (uint16_t)layer->vein_mat.size(); for (uint16_t l = 0; l < size; l++) { auto vein = layer->vein_mat[l]; geo_summary->at(i).possible_minerals[vein] = true; for (uint16_t m = 0; m < world->raws.inorganics[vein]->metal_ore.mat_index.size(); m++) { geo_summary->at(i).possible_metals.at(world->raws.inorganics[vein]->metal_ore.mat_index[m]) = true; } if (world->raws.inorganics[vein]->economic_uses.size() != 0) { geo_summary->at(i).possible_economics[vein] = true; for (uint16_t m = 0; m < world->raws.inorganics[vein]->economic_uses.size(); m++) { if (world->raws.inorganics[vein]->economic_uses[m] == state->clay_reaction) { geo_summary->at(i).clay_absent = false; } if (world->raws.inorganics[vein]->economic_uses[m] == state->flux_reaction) { geo_summary->at(i).flux_absent = false; } } for (uint16_t m = 0; m < state->coals.size(); m++) { if (vein == state->coals[m]) { geo_summary->at(i).coal_absent = false; break; } } } } if (layer->bottom_height <= -3 && world->raws.inorganics[layer->mat_index]->flags.is_set(df::inorganic_flags::AQUIFER)) { geo_summary->at(i).aquifer_absent = false; } if (non_soil_found == true) { geo_summary->at(i).top_soil_aquifer_only = false; } } } return true; } //================================================================================= void survey_evil_weather(embark_assist::defs::world_tile_data *survey_results) { df::world_data *world_data = world->world_data; for (uint16_t i = 0; i < world->interaction_instances.all.size(); i++) { auto interaction = world->raws.interactions[world->interaction_instances.all[i]->interaction_id]; uint16_t region_index = world->interaction_instances.all[i]->region_index; bool blood_rain = false; bool permanent_syndrome_rain = false; bool temporary_syndrome_rain = false; bool thralling = false; bool reanimating = false; if (interaction->sources.size() && interaction->sources[0]->getType() == df::interaction_source_type::REGION) { for (uint16_t k = 0; k < interaction->targets.size(); k++) { if (interaction->targets[k]->getType() == df::interaction_target_type::CORPSE) { for (uint16_t l = 0; l < interaction->effects.size(); l++) { if (interaction->effects[l]->getType() == df::interaction_effect_type::ANIMATE) { reanimating = true; break; } } } else if (interaction->targets[k]->getType() == df::interaction_target_type::MATERIAL) { df::interaction_target_materialst* material = virtual_cast(interaction->targets[k]); if (material && DFHack::MaterialInfo(material->mat_type, material->mat_index).isInorganic()) { for (const auto &syndrome : world->raws.inorganics[material->mat_index]->material.syndrome) { for (const auto &ce : syndrome->ce) { df::creature_interaction_effect_type ce_type = ce->getType(); if (ce_type == df::creature_interaction_effect_type::FLASH_TILE) { // Using this as a proxy. There seems to be a group of 4 effects for thralls: // display symbol, flash symbol, phys att change and one more. thralling = true; } else if (ce_type == df::creature_interaction_effect_type::PAIN || ce_type == df::creature_interaction_effect_type::SWELLING || ce_type == df::creature_interaction_effect_type::OOZING || ce_type == df::creature_interaction_effect_type::BRUISING || ce_type == df::creature_interaction_effect_type::BLISTERS || ce_type == df::creature_interaction_effect_type::NUMBNESS || ce_type == df::creature_interaction_effect_type::PARALYSIS || ce_type == df::creature_interaction_effect_type::FEVER || ce_type == df::creature_interaction_effect_type::BLEEDING || ce_type == df::creature_interaction_effect_type::COUGH_BLOOD || ce_type == df::creature_interaction_effect_type::VOMIT_BLOOD || ce_type == df::creature_interaction_effect_type::NAUSEA || ce_type == df::creature_interaction_effect_type::UNCONSCIOUSNESS || ce_type == df::creature_interaction_effect_type::NECROSIS || ce_type == df::creature_interaction_effect_type::IMPAIR_FUNCTION || ce_type == df::creature_interaction_effect_type::DROWSINESS || ce_type == df::creature_interaction_effect_type::DIZZINESS || ce_type == df::creature_interaction_effect_type::ERRATIC_BEHAVIOR) { // Doubtful if possible for region. if (ce->end == -1) { permanent_syndrome_rain = true; } else { temporary_syndrome_rain = true; } } } } } else { // If not inorganic it's always blood, as far as known. blood_rain = true; } } } } for (uint16_t k = 0; k < world_data->regions[region_index]->region_coords.size(); k++) { auto &results = survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y); results.blood_rain[5] = blood_rain; results.permanent_syndrome_rain[5] = permanent_syndrome_rain; results.temporary_syndrome_rain[5] = temporary_syndrome_rain; results.reanimating[5] = reanimating; results.thralling[5] = thralling; } } for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) { auto &results = survey_results->at(i).at(k); results.blood_rain_possible = false; results.permanent_syndrome_rain_possible = false; results.temporary_syndrome_rain_possible = false; results.reanimating_possible = false; results.thralling_possible = false; results.blood_rain_full = true; results.permanent_syndrome_rain_full = true; results.temporary_syndrome_rain_full = true; results.reanimating_full = true; results.thralling_full = true; for (uint8_t l = 1; l < 10; l++) { if (results.biome_index[l] != -1) { df::coord2d adjusted = apply_offset(i, k, l); results.blood_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).blood_rain[5]; results.permanent_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).permanent_syndrome_rain[5]; results.temporary_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).temporary_syndrome_rain[5]; results.reanimating[l] = survey_results->at(adjusted.x).at(adjusted.y).reanimating[5]; results.thralling[l] = survey_results->at(adjusted.x).at(adjusted.y).thralling[5]; if (results.blood_rain[l]) { results.blood_rain_possible = true; } else { results.blood_rain_full = false; } if (results.permanent_syndrome_rain[l]) { results.permanent_syndrome_rain_possible = true; } else { results.permanent_syndrome_rain_full = false; } if (results.temporary_syndrome_rain[l]) { results.temporary_syndrome_rain_possible = true; } else { results.temporary_syndrome_rain_full = false; } if (results.reanimating[l]) { results.reanimating_possible = true; } else { results.reanimating_full = false; } if (results.thralling[l]) { results.thralling_possible = true; } else { results.thralling_full = false; } } } } } } //================================================================================= int16_t min_temperature(int16_t max_temperature, uint16_t latitude) { uint16_t divisor; uint16_t steps; uint16_t lat; if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::None) { return max_temperature; } else if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::North || world->world_data->flip_latitude == df::world_data::T_flip_latitude::South) { steps = world->world_data->world_height / 2; if (latitude > steps) { lat = world->world_data->world_height - 1 - latitude; } else { lat = latitude; } } else { // Both steps = world->world_data->world_height / 4; if (latitude < steps) { lat = latitude; } else if (latitude <= steps * 2) { lat = steps * 2 - latitude; } else if (latitude <= steps * 3) { lat = latitude - steps * 2; } else { lat = world->world_data->world_height - latitude; } } if (world->world_data->world_height == 17) { divisor = ((lat * 57) / steps + 0.4); } else if (world->world_data->world_height == 33) { divisor = ((lat * 61) / steps + 0.1); } else if (world->world_data->world_height == 65) { divisor = ((lat * 63) / steps); } else if (world->world_data->world_height == 129 || world->world_data->world_height == 257) { divisor = ((lat * 64) / steps); } else { return max_temperature; // Not any standard world height. No formula available } return max_temperature - ceil(divisor * 3 / 4); } //================================================================================= void process_embark_incursion(embark_assist::defs::site_infos *site_info, embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::mid_level_tile_incursion_base *mlt, // Note this is a single tile, as opposed to most usages of this variable name. int16_t elevation, uint16_t x, uint16_t y) { site_info->aquifer |= mlt->aquifer; if (mlt->soil_depth < site_info->min_soil) { site_info->min_soil = mlt->soil_depth; } if (mlt->soil_depth > site_info->max_soil) { site_info->max_soil = mlt->soil_depth; } if (elevation != mlt->elevation) { site_info->flat = false; } if (mlt->clay) { site_info->clay = true; } if (mlt->sand) { site_info->sand = true; } if (survey_results->at(x).at(y).blood_rain [mlt->biome_offset]) { site_info->blood_rain = true; } if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->biome_offset]) { site_info->permanent_syndrome_rain = true; } if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->biome_offset]) { site_info->temporary_syndrome_rain = true; } if (survey_results->at(x).at(y).reanimating[mlt->biome_offset]) { site_info->reanimating = true; } if (survey_results->at(x).at(y).thralling[mlt->biome_offset]) { site_info->thralling = true; } } //================================================================================= void process_embark_incursion_mid_level_tile(uint8_t from_direction, embark_assist::defs::site_infos *site_info, embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::mid_level_tiles *mlt, uint8_t i, uint8_t k) { int8_t fetch_i = i; int8_t fetch_k = k; int16_t fetch_x = state->x; int16_t fetch_y = state->y; df::world_data *world_data = world->world_data; // Logic can be implemented with modulo and division, but that's harder to read. switch (from_direction) { case embark_assist::defs::directions::Northwest: fetch_i = i - 1; fetch_k = k - 1; break; case embark_assist::defs::directions::North: fetch_k = k - 1; break; case embark_assist::defs::directions::Northeast: fetch_i = i + 1; fetch_k = k - 1; break; case embark_assist::defs::directions::West: fetch_i = i - 1; break; case embark_assist::defs::directions::Center: return; // Own tile provides the data, so there's no incursion. break; case embark_assist::defs::directions::East: fetch_i = i + 1; break; case embark_assist::defs::directions::Southwest: fetch_i = i - 1; fetch_k = k + 1; break; case embark_assist::defs::directions::South: fetch_k = k + 1; break; case embark_assist::defs::directions::Southeast: fetch_i = i + 1; fetch_k = k + 1; break; } if (fetch_i < 0) { fetch_x = state->x - 1; } else if (fetch_i > 15) { fetch_x = state->x + 1; } if (fetch_k < 0) { fetch_y = state->y - 1; } else if (fetch_k > 15) { fetch_y = state->y + 1; } if (fetch_x < 0 || fetch_x == world_data->world_width || fetch_y < 0 || fetch_y == world_data->world_height) { return; // We're at the world edge, so no incursions from the outside. } if (fetch_k < 0) { if (fetch_i < 0) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).south_row[15], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else if (fetch_i > 15) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).south_row[0], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).south_row[fetch_i], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } } else if (fetch_k > 15) { if (fetch_i < 0) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).north_row[15], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else if (fetch_i > 15) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).north_row[0], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).north_row[fetch_i], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } } else { if (fetch_i < 0) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).east_column[fetch_k], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else if (fetch_i > 15) { process_embark_incursion(site_info, survey_results, &survey_results->at(fetch_x).at(fetch_y).west_column[fetch_k], mlt->at(i).at(k).elevation, fetch_x, fetch_y); } else { process_embark_incursion(site_info, survey_results, &mlt->at(fetch_i).at(fetch_k), mlt->at(i).at(k).elevation, fetch_x, fetch_y); } } } } } //================================================================================= // Exported operations //================================================================================= void embark_assist::survey::setup(uint16_t max_inorganic) { embark_assist::survey::state = new(embark_assist::survey::states); embark_assist::survey::state->max_inorganic = max_inorganic; } //================================================================================= df::coord2d embark_assist::survey::get_last_pos() { return{ embark_assist::survey::state->x, embark_assist::survey::state->y }; } //================================================================================= void embark_assist::survey::initiate(embark_assist::defs::mid_level_tiles *mlt) { for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { mlt->at(i).at(k).metals.resize(state->max_inorganic); mlt->at(i).at(k).economics.resize(state->max_inorganic); mlt->at(i).at(k).minerals.resize(state->max_inorganic); } } } //================================================================================= void embark_assist::survey::clear_results(embark_assist::defs::match_results *match_results) { for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) { match_results->at(i).at(k).preliminary_match = false; match_results->at(i).at(k).contains_match = false; for (uint16_t l = 0; l < 16; l++) { for (uint16_t m = 0; m < 16; m++) { match_results->at(i).at(k).mlt_match[l][m] = false; } } } } } //================================================================================= 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()); int16_t temperature; bool negative; embark_assist::survey::geo_survey(geo_summary); for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) { df::coord2d adjusted; df::world_data *world_data = world->world_data; uint16_t geo_index; uint16_t sav_ev; uint8_t offset_count = 0; auto &results = survey_results->at(i).at(k); results.surveyed = false; results.survey_completed = false; results.neighboring_clay = false; results.neighboring_sand = false; for (uint8_t l = 0; l <= ENUM_LAST_ITEM(biome_type); l++) { results.neighboring_biomes[l] = false; } for (uint8_t l = 0; l <= ENUM_LAST_ITEM(world_region_type); l++) { results.neighboring_region_types[l] = false; } for (uint8_t l = 0; l < 2; l++) { results.neighboring_savagery[l] = false; results.neighboring_evilness[l] = false; } results.aquifer = embark_assist::defs::Clear_Aquifer_Bits; results.clay_count = 0; results.sand_count = 0; results.flux_count = 0; results.coal_count = 0; 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; results.evilness_count[0] = 0; results.evilness_count[1] = 0; results.evilness_count[2] = 0; results.metals.resize(state->max_inorganic); results.economics.resize(state->max_inorganic); results.minerals.resize(state->max_inorganic); // Evil weather and rivers are handled in later operations. Should probably be merged into one. for (uint8_t l = 1; l < 10; l++) { adjusted = apply_offset(i, k, l); if (adjusted.x != i || adjusted.y != k || l == 5) { offset_count++; results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id; results.biome[l] = DFHack::Maps::getBiomeTypeWithRef(adjusted.x, adjusted.y, k); temperature = world_data->region_map[adjusted.x][adjusted.y].temperature; negative = temperature < 0; if (negative) { temperature = -temperature; } results.max_temperature[l] = (temperature / 4) * 3; if (temperature % 4 > 1) { results.max_temperature[l] = results.max_temperature[l] + temperature % 4 - 1; } if (negative) { results.max_temperature[l] = -results.max_temperature[l]; } 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 |= embark_assist::defs::None_Aquifer_Bit; } else if (world_data->region_map[adjusted.x][adjusted.y].drainage % 20 == 7) { results.aquifer |= embark_assist::defs::Heavy_Aquifer_Bit; } else { results.aquifer |= embark_assist::defs::Light_Aquifer_Bit; } 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++; if (!geo_summary->at(geo_index).coal_absent) results.coal_count++; if (geo_summary->at(geo_index).soil_size < results.min_region_soil) results.min_region_soil = geo_summary->at(geo_index).soil_size; if (geo_summary->at(geo_index).soil_size > results.max_region_soil) results.max_region_soil = geo_summary->at(geo_index).soil_size; sav_ev = world_data->region_map[adjusted.x][adjusted.y].savagery / 33; if (sav_ev == 3) sav_ev = 2; results.savagery_count[sav_ev]++; sav_ev = world_data->region_map[adjusted.x][adjusted.y].evilness / 33; if (sav_ev == 3) sav_ev = 2; results.evilness_count[sav_ev]++; for (uint16_t m = 0; m < state->max_inorganic; m++) { if (geo_summary->at(geo_index).possible_metals[m]) results.metals[m] = true; 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; results.biome[l] = -1; results.max_temperature[l] = -30000; results.min_temperature[l] = -30000; } } bool biomes[ENUM_LAST_ITEM(biome_type) + 1]; for (uint8_t l = 0; l <= ENUM_LAST_ITEM(biome_type); l++) { biomes[l] = false; } for (uint8_t l = 1; l < 10; l++) { if (results.biome[l] != -1) { biomes[results.biome[l]] = true; } } int count = 0; for (uint8_t l = 0; l <= ENUM_LAST_ITEM(biome_type); l++) { if (biomes[l]) count++; } results.biome_count = count; 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; if (results.coal_count == offset_count) results.coal_count = 256; for (uint8_t l = 0; l < 3; l++) { if (results.savagery_count[l] == offset_count) results.savagery_count[l] = 256; if (results.evilness_count[l] == offset_count) results.evilness_count[l] = 256; } } } embark_assist::survey::survey_evil_weather(survey_results); } //================================================================================= void inline copy_incursion_values(embark_assist::defs::mid_level_tile_incursion_base &target, const embark_assist::defs::mid_level_tile_incursion_base &source) { target.aquifer = source.aquifer; target.clay = source.clay; target.sand = source.sand; target.soil_depth = source.soil_depth; target.elevation = source.elevation; target.biome_offset = source.biome_offset; target.trees = source.trees; target.savagery_level = source.savagery_level; target.evilness_level = source.evilness_level; } //================================================================================= void reset_mlt_inorganics(embark_assist::defs::mid_level_tiles &mlts) { for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { embark_assist::defs::mid_level_tile &mlt = mlts[i][k]; // std::memset is much faster than std::fill and also faster than direct assignment - also std::fill might be compiled to std::memset but is not guaranteed to happen // have a look here for why: https://travisdowns.github.io/blog/2020/01/20/zero.html std::memset(&mlt.metals[0], false, mlt.metals.size() * sizeof(mlt.metals[0])); std::memset(&mlt.economics[0], false, mlt.economics.size() * sizeof(mlt.economics[0])); std::memset(&mlt.minerals[0], false, mlt.minerals.size() * sizeof(mlt.minerals[0])); } } } //================================================================================= void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data *geo_summary, embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::mid_level_tiles *mlt) { // color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); int16_t x = screen->location.region_pos.x; int16_t y = screen->location.region_pos.y; embark_assist::defs::region_tile_datum &tile = survey_results->at(x).at(y); int8_t max_soil_depth; int8_t offset; int16_t elevation; int16_t last_bottom; int16_t top_z; int16_t min_z = 0; // Initialized to silence warning about potential usage of uninitialized data. int16_t bottom_z; df::coord2d adjusted; df::world_data *world_data = world->world_data; df::world_region_details *details = world_data->region_details[0]; df::region_map_entry *world_tile = &world_data->region_map[x][y]; std::vector features; uint8_t soil_erosion; 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; tile.economics[i] = 0; tile.minerals[i] = 0; } reset_mlt_inorganics(*mlt); for (uint8_t i = 1; i < 10; i++) tile.biome_index[i] = -1; for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { embark_assist::defs::mid_level_tile &mid_level_tile = mlt->at(i).at(k); max_soil_depth = -1; offset = details->biome[i][k]; adjusted = apply_offset(x, y, offset); if (adjusted.x != x || adjusted.y != y) { mid_level_tile.biome_offset = offset; } else { mid_level_tile.biome_offset = 5; } const df::region_map_entry ®ion_map_entry = world_data->region_map[adjusted.x][adjusted.y]; tile.biome_index[mid_level_tile.biome_offset] = region_map_entry.region_id; mid_level_tile.savagery_level = region_map_entry.savagery / 33; if (mid_level_tile.savagery_level == 3) { mid_level_tile.savagery_level = 2; } mid_level_tile.evilness_level = region_map_entry.evilness / 33; if (mid_level_tile.evilness_level == 3) { mid_level_tile.evilness_level = 2; } elevation = details->elevation[i][k]; const df::world_geo_biome* world_geo_biome = world_data->geo_biomes[region_map_entry.geo_index]; // Special biome adjustments if (!region_map_entry.flags.is_set(region_map_entry_flags::is_lake)) { if (region_map_entry.elevation >= 150) { // Mountain max_soil_depth = 0; } else if (region_map_entry.elevation < 100) { // Ocean if (elevation == 99) { elevation = 98; } if ((world_geo_biome->unk1 == 4 || world_geo_biome->unk1 == 5) && details->unk12e8 < 500) { max_soil_depth = 0; } } } features = details->features[i][k]; mid_level_tile.adamantine_level = -1; mid_level_tile.magma_level = -1; end_check_l = static_cast(features.size()); for (size_t l = 0; l < end_check_l; l++) { auto feature = features[l]; if (feature->feature_idx != -1) { auto feature_init = world_data->feature_map[x / 16][y / 16].features->feature_init[x % 16][y % 16][feature->feature_idx]; switch (feature_init->getType()) { case df::feature_type::deep_special_tube: mid_level_tile.adamantine_level = feature_init->start_depth; break; case df::feature_type::magma_pool: mid_level_tile.magma_level = 2 - feature_init->start_depth; break; case df::feature_type::volcano: mid_level_tile.magma_level = 3; break; default: break; } } else if (feature->layer != -1 && feature->min_z != -30000) { auto layer = world_data->underground_regions[feature->layer]; if (layer->type == df::world_underground_region::MagmaSea) { min_z = feature->min_z; // The features are individual per region tile } } } // Compute shifts for layers in the stack. if (max_soil_depth == -1) { // Not set to zero by the biome max_soil_depth = std::max((154 - elevation) / 5, 1); } auto geo_datum = geo_summary->at(region_map_entry.geo_index); soil_erosion = geo_datum.soil_size - std::min((int)geo_datum.soil_size, (int)max_soil_depth); int16_t layer_shift[16]; int16_t cur_shift = elevation + soil_erosion - 1; aquifer = false; mid_level_tile.aquifer = embark_assist::defs::Clear_Aquifer_Bits; mid_level_tile.clay = false; mid_level_tile.sand = false; mid_level_tile.flux = false; mid_level_tile.coal = false; if (max_soil_depth == 0) { mid_level_tile.soil_depth = 0; } else { mid_level_tile.soil_depth = geo_datum.soil_size - soil_erosion; } mid_level_tile.offset = offset; mid_level_tile.elevation = details->elevation[i][k]; mid_level_tile.river_size = embark_assist::defs::river_sizes::None; mid_level_tile.river_elevation = 100; if (details->rivers_vertical.active[i][k] != 0) { mid_level_tile.river_size = river_size_of (details->rivers_vertical.x_max[i][k] - details->rivers_vertical.x_min[i][k] + 1); mid_level_tile.river_elevation = details->rivers_vertical.elevation[i][k]; } else if (details->rivers_horizontal.active[i][k] != 0) { mid_level_tile.river_size = river_size_of (details->rivers_horizontal.y_max[i][k] - details->rivers_horizontal.y_min[i][k] + 1); mid_level_tile.river_elevation = details->rivers_horizontal.elevation[i][k]; } if (mid_level_tile.river_size != embark_assist::defs::river_sizes::None && world_tile->flags.is_set(df::region_map_entry_flags::is_brook)) { mid_level_tile.river_size = embark_assist::defs::river_sizes::Brook; } if (tile.min_region_soil > mid_level_tile.soil_depth) { tile.min_region_soil = mid_level_tile.soil_depth; } if (tile.max_region_soil < mid_level_tile.soil_depth) { tile.max_region_soil = mid_level_tile.soil_depth; } end_check_l = static_cast(world_geo_biome->layers.size()); if (end_check_l > 16) end_check_l = 16; for (uint16_t l = 0; l < end_check_l; l++) { auto layer = world_geo_biome->layers[l]; layer_shift[l] = cur_shift; if (layer->type == df::geo_layer_type::SOIL || layer->type == df::geo_layer_type::SOIL_SAND) { int16_t size = layer->top_height - layer->bottom_height - 1; // Comment copied from prospector.cpp (like the logic)... // This is to replicate the behavior of a probable bug in the // map generation code : if a layer is partially eroded, the // removed levels are in fact transferred to the layer below, // because unlike the case of removing the whole layer, the code // does not execute a loop to shift the lower part of the stack up. if (size > soil_erosion) { cur_shift = cur_shift - soil_erosion; } soil_erosion -= std::min((int)soil_erosion, (int)size); } } last_bottom = elevation; // Don't have to set up the end_check as we can reuse the one above. for (uint16_t l = 0; l < end_check_l; l++) { auto layer = world_geo_biome->layers[l]; top_z = last_bottom - 1; bottom_z = std::max((int)layer->bottom_height + layer_shift[l], (int)min_z); if (l == 15) { bottom_z = min_z; // stretch layer if needed } if (top_z >= bottom_z) { last_bottom = bottom_z; mid_level_tile.minerals[layer->mat_index] = true; const df::inorganic_raw* inorganic_layer = world->raws.inorganics[layer->mat_index]; end_check_m = static_cast(inorganic_layer->metal_ore.mat_index.size()); for (uint16_t m = 0; m < end_check_m; m++) { mid_level_tile.metals[inorganic_layer->metal_ore.mat_index[m]] = true; } if (layer->type == df::geo_layer_type::SOIL || layer->type == df::geo_layer_type::SOIL_SAND) { if (inorganic_layer->flags.is_set(df::inorganic_flags::SOIL_SAND)) { mid_level_tile.sand = true; } } if (inorganic_layer->economic_uses.size() > 0) { mid_level_tile.economics[layer->mat_index] = true; end_check_m = static_cast(inorganic_layer->economic_uses.size()); for (uint16_t m = 0; m < end_check_m; m++) { if (inorganic_layer->economic_uses[m] == state->clay_reaction) { mid_level_tile.clay = true; } else if (inorganic_layer->economic_uses[m] == state->flux_reaction) { mid_level_tile.flux = true; } } for (uint16_t m = 0; m < state->coals.size(); m++) { if (layer->mat_index == state->coals[m]) { mid_level_tile.coal = true; break; } } } end_check_m = static_cast(layer->vein_mat.size()); for (uint16_t m = 0; m < end_check_m; m++) { const int vein_mat_index = layer->vein_mat[m]; mid_level_tile.minerals[vein_mat_index] = true; const df::inorganic_raw* inorganic_vein = world->raws.inorganics[vein_mat_index]; end_check_n = static_cast(inorganic_vein->metal_ore.mat_index.size()); for (uint16_t n = 0; n < end_check_n; n++) { mid_level_tile.metals[inorganic_vein->metal_ore.mat_index[n]] = true; } if (inorganic_vein->economic_uses.size() > 0) { mid_level_tile.economics[vein_mat_index] = true; end_check_n = static_cast(inorganic_vein->economic_uses.size()); for (uint16_t n = 0; n < end_check_n; n++) { if (inorganic_vein->economic_uses[n] == state->clay_reaction) { mid_level_tile.clay = true; } else if (inorganic_vein->economic_uses[n] == state->flux_reaction) { mid_level_tile.flux = true; } } for (uint16_t n = 0; n < state->coals.size(); n++) { if (vein_mat_index == state->coals[n]) { mid_level_tile.coal = true; break; } } } } if (bottom_z <= elevation - 3 && inorganic_layer->flags.is_set(df::inorganic_flags::AQUIFER)) { aquifer = true; } } } if (!aquifer) { mid_level_tile.aquifer = embark_assist::defs::None_Aquifer_Bit; } else if (region_map_entry.drainage % 20 == 7) { mid_level_tile.aquifer = embark_assist::defs::Heavy_Aquifer_Bit; } else { mid_level_tile.aquifer = embark_assist::defs::Light_Aquifer_Bit; } mid_level_tile.trees = tree_level_of(world_data->regions[region_map_entry.region_id]->type, region_map_entry.vegetation); } } // This is messy. DF has some weird logic to leave out river bends with a South and an East connection, as well // as river sources (and presumably sinks) that are to the North or the West of the connecting river. // Experiments indicate these implicit river bends inherit their River Elevation from the lower of the two // "parents", and it's assumed river sources and sinks similarly inherit it from their sole "parent". // Two issues are known: // - Lake and Ocean tiles may be marked as having a river when DF doesn't. However, DF does allow for rivers to // exist in Ocean/Lake tiles, as well as sources/sinks. // - DF generates rivers on/under glaciers, but does not display them (as they're frozen), nor are their names // displayed. // for (uint8_t i = 1; i < 16; i++) { for (uint8_t k = 0; k < 15; k++) { auto western_neighbour = mlt->at(i - 1).at(k); if (details->rivers_horizontal.active[i][k] != 0 && details->rivers_vertical.active[i - 1][k + 1] != 0 && western_neighbour.river_size == embark_assist::defs::river_sizes::None) { // Probably never true western_neighbour.river_size = mlt->at(i).at(k).river_size; western_neighbour.river_elevation = mlt->at(i).at(k).river_elevation; if (western_neighbour.river_elevation > mlt->at(i - 1).at(k + 1).river_elevation) { western_neighbour.river_elevation = mlt->at(i - 1).at(k + 1).river_elevation; } } } } for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 1; k < 16; k++) { auto northern_neighbour = mlt->at(i).at(k - 1); if (details->rivers_vertical.active[i][k] != 0 && northern_neighbour.river_size == embark_assist::defs::river_sizes::None) { northern_neighbour.river_size = mlt->at(i).at(k).river_size; northern_neighbour.river_elevation = mlt->at(i).at(k).river_elevation; } } } for (uint8_t i = 1; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { auto western_neighbour = mlt->at(i - 1).at(k); if (details->rivers_horizontal.active[i][k] != 0 && western_neighbour.river_size == embark_assist::defs::river_sizes::None) { western_neighbour.river_size = mlt->at(i).at(k).river_size; western_neighbour.river_elevation = mlt->at(i).at(k).river_elevation; } } } tile.aquifer = embark_assist::defs::Clear_Aquifer_Bits; tile.clay_count = 0; tile.sand_count = 0; tile.flux_count = 0; tile.coal_count = 0; tile.min_region_soil = 10; tile.max_region_soil = 0; tile.savagery_count[0] = 0; tile.savagery_count[1] = 0; tile.savagery_count[2] = 0; tile.evilness_count[0] = 0; tile.evilness_count[1] = 0; tile.evilness_count[2] = 0; for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { const embark_assist::defs::mid_level_tile &mid_level_tile = mlt->at(i).at(k); tile.aquifer |= mid_level_tile.aquifer; if (mid_level_tile.clay) { tile.clay_count++; } if (mid_level_tile.sand) { tile.sand_count++; } if (mid_level_tile.flux) { tile.flux_count++; } if (mid_level_tile.coal) { tile.coal_count++; } if (mid_level_tile.soil_depth < tile.min_region_soil) { tile.min_region_soil = mid_level_tile.soil_depth; } if (mid_level_tile.soil_depth > tile.max_region_soil) { tile.max_region_soil = mid_level_tile.soil_depth; } if (mid_level_tile.river_size != embark_assist::defs::river_sizes::None) { if (tile.min_river_size == embark_assist::defs::river_sizes::None || mid_level_tile.river_size < tile.min_river_size) { tile.min_river_size = mid_level_tile.river_size; } if (tile.max_river_size < mid_level_tile.river_size) { tile.max_river_size = mid_level_tile.river_size; } if (i < 15) { const embark_assist::defs::mid_level_tile &eastern_neighbour = mlt->at(i + 1).at(k); if (eastern_neighbour.river_size != embark_assist::defs::river_sizes::None && abs(mid_level_tile.river_elevation - eastern_neighbour.river_elevation) > tile.max_waterfall) { tile.max_waterfall = abs(mid_level_tile.river_elevation - eastern_neighbour.river_elevation); } } if (k < 15) { const embark_assist::defs::mid_level_tile &southern_neighbour = mlt->at(i).at(k + 1); if (southern_neighbour.river_size != embark_assist::defs::river_sizes::None && abs(mid_level_tile.river_elevation - southern_neighbour.river_elevation) > tile.max_waterfall) { tile.max_waterfall = abs(mid_level_tile.river_elevation - southern_neighbour.river_elevation); } } } // biome_index handled above // biome handled below // evil weather handled separately // reanimating handled separately // thralling handled separately if (tile.min_tree_level > mid_level_tile.trees) tile.min_tree_level = mid_level_tile.trees; if (tile.max_tree_level < mid_level_tile.trees) tile.max_tree_level = mid_level_tile.trees; tile.savagery_count[mid_level_tile.savagery_level]++; tile.evilness_count[mid_level_tile.evilness_level]++; for (uint16_t l = 0; l < state->max_inorganic; l++) { if (mid_level_tile.metals[l]) { tile.metals[l] = true; } if (mid_level_tile.economics[l]) { tile.economics[l] = true; } if (mid_level_tile.minerals[l]) { tile.minerals[l] = true; } } } } for (uint8_t i = 1; i < 10; i++) { if (tile.biome_index[i] == -1) { tile.biome[i] = -1; } } bool biomes[ENUM_LAST_ITEM(biome_type) + 1]; for (uint8_t i = 0; i <= ENUM_LAST_ITEM(biome_type); i++) { biomes[i] = false; } for (uint8_t i = 1; i < 10; i++) { if (tile.biome[i] != -1) { biomes[tile.biome[i]] = true; } } int count = 0; for (uint8_t i = 0; i <= ENUM_LAST_ITEM(biome_type); i++) { if (biomes[i]) count++; } tile.biome_count = count; for (uint8_t i = 0; i < 16; i++) { copy_incursion_values(tile.north_row[i], mlt->at(i).at(0)); copy_incursion_values(tile.south_row[i], mlt->at(i).at(15)); copy_incursion_values(tile.west_column[i], mlt->at(0).at(i)); copy_incursion_values(tile.east_column[i], mlt->at(15).at(i)); tile.north_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[i][0]; tile.west_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[0][i]; tile.north_row_biome_x[i] = world_data->region_details[0]->edges.biome_x[i][0]; tile.west_column_biome_y[i] = world_data->region_details[0]->edges.biome_y[0][i]; } for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { tile.region_type[i][k] = world_data->regions[tile.biome_index[mlt->at(i).at(k).biome_offset]]->type; } } // 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; } //================================================================================= df::coord2d embark_assist::survey::apply_offset(uint16_t x, uint16_t y, int8_t offset) { df::coord2d result; result.x = x; result.y = y; switch (offset) { case embark_assist::defs::offset_directions::Southwest: result.x--; result.y++; break; case embark_assist::defs::offset_directions::South: result.y++; break; case embark_assist::defs::offset_directions::Southeast: result.x++; result.y++; break; case embark_assist::defs::offset_directions::West: result.x--; break; case embark_assist::defs::offset_directions::Center: break; // Center. No change case embark_assist::defs::offset_directions::East: result.x++; break; case embark_assist::defs::offset_directions::Northwest: result.x--; result.y--; break; case embark_assist::defs::offset_directions::North: result.y--; break; case embark_assist::defs::offset_directions::Northeast: result.x++; result.y--; break; default: // Bug. Just act as if it's the center... break; } if (result.x < 0) { result.x = 0; } else if (result.x >= world->worldgen.worldgen_parms.dim_x) { result.x = world->worldgen.worldgen_parms.dim_x - 1; } if (result.y < 0) { result.y = 0; } else if (result.y >= world->worldgen.worldgen_parms.dim_y) { result.y = world->worldgen.worldgen_parms.dim_y - 1; } return result; } //================================================================================= void adjust_coordinates(int16_t *x, int16_t *y, int8_t *i, int8_t *k) { if (*i < 0) { *x = *x - 1; *i = *i + 16; } else if (*i > 15) { *x = *x + 1; *i = *i - 16; } if (*k < 0) { *y = *y - 1; *k = *k + 16; } else if (*k > 15) { *y = *y + 1; *k = *k - 16; } } //================================================================================= df::world_region_type embark_assist::survey::region_type_of(embark_assist::defs::world_tile_data *survey_results, int16_t x, int16_t y, int8_t i, int8_t k) { df::world_data *world_data = world->world_data; int16_t effective_x = x; int16_t effective_y = y; int8_t effective_i = i; int8_t effective_k = k; adjust_coordinates(&effective_x, &effective_y, &effective_i, &effective_k); if (effective_x < 0 || effective_x >= world_data->world_width || effective_y < 0 || effective_y >= world_data->world_height) { return df::world_region_type::Lake; // Essentially dummy value, yielding to everything. It will // be handled properly later anyway. } return survey_results->at(effective_x).at(effective_y).region_type[effective_i][effective_k]; } //================================================================================= uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile_data *survey_results, uint8_t corner_location, uint16_t x, uint16_t y, uint8_t i, uint8_t k) { df::world_data *world_data = world->world_data; df::world_region_type nw_region_type; df::world_region_type n_region_type; df::world_region_type w_region_type; df::world_region_type home_region_type; int16_t effective_x = x; int16_t effective_y = y; int8_t effective_i = i; int8_t effective_k = k; uint8_t effective_corner; bool nw_region_type_active; bool n_region_type_active; bool w_region_type_active; bool home_region_type_active; int8_t nw_region_type_level; int8_t n_region_type_level; int8_t w_region_type_level; int8_t home_region_type_level; if (corner_location == embark_assist::defs::directions::Center) { // We're the reference. No change. } else if (corner_location == embark_assist::defs::directions::East) { // Tile to the east is the reference effective_i = i + 1; } else if (corner_location == embark_assist::defs::directions::South) { // Tile to the south is the reference effective_k = k + 1; } else { // 8, tile to the southeast is the reference. effective_i = i + 1; effective_k = k + 1; } adjust_coordinates(&effective_x, &effective_y, &effective_i, &effective_k); if (effective_x == world_data->world_width) { if (effective_y == world_data->world_height) { // Only the SE corner of the SE most tile of the world can reference this. return embark_assist::defs::directions::Center; } else { // East side corners of the east edge of the world if (effective_y == 0 && effective_k == 0) { // North edge of the world. Only one alternative exists. return embark_assist::defs::directions::Center; } nw_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i - 1, effective_k - 1); w_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i - 1, effective_k); if (nw_region_type == df::world_region_type::Lake || nw_region_type == df::world_region_type::Ocean) { nw_region_type_level = 0; } else if (nw_region_type == df::world_region_type::Mountains) { nw_region_type_level = 1; } else { nw_region_type_level = 2; } if (w_region_type == df::world_region_type::Lake || w_region_type == df::world_region_type::Ocean) { w_region_type_level = 0; } else if (w_region_type == df::world_region_type::Mountains) { w_region_type_level = 1; } else { w_region_type_level = 2; } if (nw_region_type_level < w_region_type_level) { return embark_assist::defs::directions::Center; } else if (nw_region_type_level > w_region_type_level) { return embark_assist::defs::directions::North; } // Neither tile will automatically yield to the other if (corner_location == embark_assist::defs::directions::East) { return embark_assist::defs::directions::North; } else { // Can only be corner_location == embark_assist::defs::SE return embark_assist::defs::directions::Center; } } } else if (effective_y == world_data->world_height) { nw_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i - 1, effective_k - 1); n_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i, effective_k - 1); if (nw_region_type == df::world_region_type::Lake || nw_region_type == df::world_region_type::Ocean) { nw_region_type_level = 0; } else if (nw_region_type == df::world_region_type::Mountains) { nw_region_type_level = 1; } else { nw_region_type_level = 2; } if (n_region_type == df::world_region_type::Lake || n_region_type == df::world_region_type::Ocean) { n_region_type_level = 0; } else if (n_region_type == df::world_region_type::Mountains) { n_region_type_level = 1; } else { n_region_type_level = 2; } if (nw_region_type_level < n_region_type_level) { return embark_assist::defs::directions::Center; } else if (nw_region_type_level > n_region_type_level) { return embark_assist::defs::directions::East; } // Neither tile will automatically yield to the other if (corner_location == embark_assist::defs::directions::South) { return embark_assist::defs::directions::Center; } else { // Can only be corner_location == embark_assist::defs::directions::Southeast return embark_assist::defs::directions::East; } } if (effective_x == x && effective_y == y) { effective_corner = world_data->region_details[0]->edges.biome_corner[effective_i][effective_k]; } else if (effective_y != y) { effective_corner = survey_results->at(effective_x).at(effective_y).north_corner_selection[effective_i]; } else { effective_corner = survey_results->at(effective_x).at(effective_y).west_corner_selection[effective_k]; } nw_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i - 1, effective_k - 1); n_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i, effective_k - 1); w_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i - 1, effective_k); home_region_type = embark_assist::survey::region_type_of(survey_results, effective_x, effective_y, effective_i, effective_k); if (nw_region_type == df::world_region_type::Lake || nw_region_type == df::world_region_type::Ocean) { nw_region_type_level = 0; } else if (nw_region_type == df::world_region_type::Mountains) { nw_region_type_level = 1; } else { nw_region_type_level = 2; } if (n_region_type == df::world_region_type::Lake || n_region_type == df::world_region_type::Ocean) { n_region_type_level = 0; } else if (n_region_type == df::world_region_type::Mountains) { n_region_type_level = 1; } else { n_region_type_level = 2; } if (w_region_type == df::world_region_type::Lake || w_region_type == df::world_region_type::Ocean) { w_region_type_level = 0; } else if (w_region_type == df::world_region_type::Mountains) { w_region_type_level = 1; } else { w_region_type_level = 2; } if (home_region_type == df::world_region_type::Lake || home_region_type == df::world_region_type::Ocean) { home_region_type_level = 0; } else if (n_region_type == df::world_region_type::Mountains) { home_region_type_level = 1; } else { home_region_type_level = 2; } if (effective_x == 0 && effective_i == 0) { // West edge of the world if (effective_y == 0 && effective_k == 0) { return embark_assist::defs::directions::Center; // Only a single reference to this info, the own tile. } else { nw_region_type_level = -1; // Knock out the unreachable corners w_region_type_level = -1; } } if (effective_y == 0 && effective_k == 0) { // North edge of the world nw_region_type_level = -1; // Knock out the unreachable corners n_region_type_level = -1; if (corner_location == embark_assist::defs::directions::Center && effective_corner == embark_assist::defs::directions::North) { // The logic below would select the wrong alternative. effective_corner = embark_assist::defs::directions::West; } } nw_region_type_active = nw_region_type_level >= n_region_type_level && nw_region_type_level >= w_region_type_level && nw_region_type_level >= home_region_type_level; n_region_type_active = n_region_type_level >= nw_region_type_level && n_region_type_level >= w_region_type_level && n_region_type_level >= home_region_type_level; w_region_type_active = w_region_type_level >= nw_region_type_level && w_region_type_level >= n_region_type_level && w_region_type_level >= home_region_type_level; home_region_type_active = home_region_type_level >= nw_region_type_level && home_region_type_level >= n_region_type_level && home_region_type_level >= w_region_type_level; if ((effective_corner == 0 && !nw_region_type_active) || (effective_corner == 1 && !n_region_type_active) || (effective_corner == 2 && !w_region_type_active) || (effective_corner == 3 && !home_region_type_active)) { // The designated corner is suppressed. The precedence list below seems // to match what DF produces except in the case adjusted above. if (nw_region_type_active) { effective_corner = 0; } else if (n_region_type_active) { effective_corner = 1; } else if (w_region_type_active) { effective_corner = 2; } else { effective_corner = 3; } } switch (effective_corner) { case 0: return corner_location - 4; break; case 1: return corner_location - 3; break; case 2: return corner_location - 1; break; case 3: return corner_location; break; } return -1; // Should never happen /* The logic that's reduced to the code above. switch (corner_location) { case 0: // N/A Not to the north or west case 1: // N/A case 2: // N/A case 3: // N/A case 6: // N/A return -1; // Should never happen break; case 4: // Self switch (corner) { case 0: return 0; // Northwest break; case 1: return 1; // North break; case 2: return 3; // West break; case 3: return 4; // Self } case 5: // East switch (corner) { case 0: return 1; // North break; case 1: return 2; // Northeast break; case 2: return 4; // Self break; case 3: return 5; // East } case 7: // South switch (corner) { case 0: return 3; // West break; case 1: return 4; // Self break; case 2: return 6; // Southwest break; case 3: return 7; // South } case 8: // Southeast switch (corner) { case 0: return 4; // Self break; case 1: return 5; // East break; case 2: return 7; // South break; case 3: return 8; // Southeast } } */ } //================================================================================= uint8_t embark_assist::survey::translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, bool own_edge, uint16_t x, uint16_t y, uint8_t i, uint8_t k) { df::world_data *world_data = world->world_data; uint8_t effective_edge; df::world_region_type north_region_type; df::world_region_type south_region_type; if (own_edge) { if (y == 0 && k == 0) return embark_assist::defs::directions::Center; // There's nothing to the north, so we fall back on our own tile. effective_edge = world_data->region_details[0]->edges.biome_x[i][k]; south_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); north_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k - 1); } else { if (k < 15) { // We're still within the same world tile effective_edge = world_data->region_details[0]->edges.biome_x[i][k + 1]; } else { // Getting the data from the world tile to the south if (y + 1 == world_data->world_height) { return embark_assist::defs::directions::Center; // There's nothing to the south, so we fall back on our own tile. } effective_edge = survey_results->at(x).at(y + 1).north_row_biome_x[i]; } north_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); south_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k + 1); } // Apply rules for Ocean && Lake to yield to everything else, // and Mountain to everything but those. // if ((north_region_type == df::world_region_type::Lake || north_region_type == df::world_region_type::Ocean) && south_region_type != df::world_region_type::Lake && south_region_type != df::world_region_type::Ocean) { effective_edge = 1; } if ((south_region_type == df::world_region_type::Lake || south_region_type == df::world_region_type::Ocean) && north_region_type != df::world_region_type::Lake && north_region_type != df::world_region_type::Ocean) { effective_edge = 0; } if (north_region_type == df::world_region_type::Mountains && south_region_type != df::world_region_type::Lake && south_region_type != df::world_region_type::Ocean && south_region_type != df::world_region_type::Mountains) { effective_edge = 1; } if (south_region_type == df::world_region_type::Mountains && north_region_type != df::world_region_type::Lake && north_region_type != df::world_region_type::Ocean && north_region_type != df::world_region_type::Mountains) { effective_edge = 0; } if (effective_edge == 0) { if (own_edge) { return embark_assist::defs::directions::North; } else { return embark_assist::defs::directions::Center; } } else { if (own_edge) { return embark_assist::defs::directions::Center; } else { return embark_assist::defs::directions::South; } } } //================================================================================= uint8_t embark_assist::survey::translate_ew_edge(embark_assist::defs::world_tile_data *survey_results, bool own_edge, uint16_t x, uint16_t y, uint8_t i, uint8_t k) { df::world_data *world_data = world->world_data; uint8_t effective_edge; df::world_region_type west_region_type; df::world_region_type east_region_type; if (own_edge) { if (x == 0 && i == 0) return embark_assist::defs::directions::Center; // There's nothing to the west, so we fall back on our own tile. effective_edge = world_data->region_details[0]->edges.biome_y[i][k]; east_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); west_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i - 1, k); } else { if (i < 15) { // We're still within the same world tile effective_edge = world_data->region_details[0]->edges.biome_y[i + 1][k]; } else { // Getting the data from the world tile to the east if (x + 1 == world_data->world_width) { return embark_assist::defs::directions::Center; // There's nothing to the east, so we fall back on our own tile. } effective_edge = survey_results->at(x + 1).at(y).west_column_biome_y[k]; } west_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); east_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i + 1, k); } // Apply rules for Ocean && Lake to yield to everything else, // and Mountain to everything but those. // if ((west_region_type == df::world_region_type::Lake || west_region_type == df::world_region_type::Ocean) && east_region_type != df::world_region_type::Lake && east_region_type != df::world_region_type::Ocean) { effective_edge = 1; } if ((east_region_type == df::world_region_type::Lake || east_region_type == df::world_region_type::Ocean) && west_region_type != df::world_region_type::Lake && west_region_type != df::world_region_type::Ocean) { effective_edge = 0; } if (west_region_type == df::world_region_type::Mountains && west_region_type != df::world_region_type::Lake && east_region_type != df::world_region_type::Ocean && east_region_type != df::world_region_type::Mountains) { effective_edge = 1; } if (east_region_type == df::world_region_type::Mountains && east_region_type != df::world_region_type::Lake && west_region_type != df::world_region_type::Ocean && west_region_type != df::world_region_type::Mountains) { effective_edge = 0; } if (effective_edge == 0) { if (own_edge) { return embark_assist::defs::directions::West; } else { return embark_assist::defs::directions::Center; } } else { if (own_edge) { return embark_assist::defs::directions::Center; } else { return embark_assist::defs::directions::East; } } } //================================================================================= void embark_assist::survey::survey_region_sites(embark_assist::defs::site_lists *site_list) { // color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); df::world_data *world_data = world->world_data; site_list->clear(); for (uint32_t i = 0; i < world_data->region_map[screen->location.region_pos.x][screen->location.region_pos.y].sites.size(); i++) { auto site = world_data->region_map[screen->location.region_pos.x][screen->location.region_pos.y].sites[i]; switch (site->type) { case df::world_site_type::PlayerFortress: case df::world_site_type::DarkFortress: case df::world_site_type::MountainHalls: case df::world_site_type::ForestRetreat: case df::world_site_type::Town: case df::world_site_type::Fortress: break; // Already visible case df::world_site_type::Cave: if (!world->worldgen.worldgen_parms.all_caves_visible) { site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'c' }); // Cave } break; case df::world_site_type::Monument: switch (site->subtype_info->monument_type) { case df::monument_type::NONE: case df::monument_type::TOMB: break; // NONE shouldn't appear, and Tombs are visible already case df::monument_type::VAULT: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'V' }); break; default: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'M' }); // Any other Monument type. Pyramid? break; } break; case df::world_site_type::ImportantLocation: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'i' }); // Don't really know what that is... break; case df::world_site_type::LairShrine: switch (site->subtype_info->lair_type) { case df::lair_type::NONE: break; case df::lair_type::SIMPLE_MOUND: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'm' }); break; case df::lair_type::SIMPLE_BURROW: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'b' }); break; case df::lair_type::WILDERNESS_LOCATION: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'l' }); break; case df::lair_type::LABYRINTH: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'L' }); break; case df::lair_type::SHRINE: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'S' }); break; default: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, '?' }); // Can these exist? break; } break; case df::world_site_type::Camp: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, 'C' }); // Camp break; default: site_list->push_back({ (uint8_t)site->rgn_min_x , (uint8_t)site->rgn_min_y, '!' }); // Not even in the enum... break; } } } //================================================================================= void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *mlt, embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::site_infos *site_info, bool use_cache) { //color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); int16_t elevation = 0; uint16_t x = screen->location.region_pos.x; uint16_t y = screen->location.region_pos.y; std::vector metals(state->max_inorganic); std::vector economics(state->max_inorganic); std::vector minerals(state->max_inorganic); bool incursion_processing_failed = false; df::world_data *world_data = world->world_data; 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; state->local_max_y = screen->location.embark_pos_max.y; } state->x = x; state->y = y; site_info->incursions_processed = true; site_info->aquifer = embark_assist::defs::Clear_Aquifer_Bits; site_info->min_soil = 10; site_info->max_soil = 0; site_info->flat = true; site_info->max_waterfall = 0; site_info->clay = false; site_info->sand = false; site_info->flux = false; site_info->coal = false; site_info->blood_rain = false; site_info->permanent_syndrome_rain = false; site_info->temporary_syndrome_rain = false; site_info->reanimating = false; site_info->thralling = false; 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++) { site_info->aquifer |= 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; } if (mlt->at(i).at(k).soil_depth > site_info->max_soil) { site_info->max_soil = mlt->at(i).at(k).soil_depth; } if (i == state->local_min_x && k == state->local_min_y) { elevation = mlt->at(i).at(k).elevation; } else if (elevation != mlt->at(i).at(k).elevation) { site_info->flat = false; } if (mlt->at(i).at(k).river_size != embark_assist::defs::river_sizes::None) { if (i < 15 && mlt->at(i + 1).at(k).river_size != embark_assist::defs::river_sizes::None && abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > site_info->max_waterfall) { site_info->max_waterfall = abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation); } if (k < 15 && mlt->at(i).at(k + 1).river_size != embark_assist::defs::river_sizes::None && abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > site_info->max_waterfall) { site_info->max_waterfall = abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation); } } if (mlt->at(i).at(k).clay) { site_info->clay = true; } if (mlt->at(i).at(k).sand) { site_info->sand = true; } if (mlt->at(i).at(k).flux) { site_info->flux = true; } if (mlt->at(i).at(k).coal) { site_info->coal = true; } if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) { site_info->blood_rain = true; } if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->at(i).at(k).biome_offset]) { site_info->permanent_syndrome_rain = true; } if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->at(i).at(k).biome_offset]) { site_info->temporary_syndrome_rain = true; } if (survey_results->at(x).at(y).reanimating[mlt->at(i).at(k).biome_offset]) { site_info->reanimating = true; } if (survey_results->at(x).at(y).thralling[mlt->at(i).at(k).biome_offset]) { site_info->thralling = true; } for (uint16_t l = 0; l < state->max_inorganic; l++) { metals[l] = metals[l] || mlt->at(i).at(k).metals[l]; economics[l] = economics[l] || mlt->at(i).at(k).economics[l]; minerals[l] = minerals[l] || mlt->at(i).at(k).minerals[l]; } } } for (uint16_t l = 0; l < state->max_inorganic; l++) { if (metals[l]) { site_info->metals.push_back(l); } if (economics[l]) { site_info->economics.push_back(l); } if (minerals[l]) { site_info->minerals.push_back(l); } } // Take incursions into account. for (int8_t i = state->local_min_x; i <= state->local_max_x; i++) { // NW corner, north row if ((i == 0 && state->local_min_y == 0 && x - 1 >= 0 && y - 1 >= 0 && !survey_results->at(x - 1).at (y - 1).surveyed) || (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::Center, x, y, i, state->local_min_y), site_info, survey_results, mlt, i, state->local_min_y); } // N edge, north row if (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_ns_edge(survey_results, true, x, y, i, state->local_min_y), site_info, survey_results, mlt, i, state->local_min_y); } // NE corner, north row if ((i == 15 && state->local_min_y == 0 && x + 1 < world_data->world_width && y - 1 >= 0 && !survey_results->at(x + 1).at(y - 1).surveyed) || (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::East, x, y, i, state->local_min_y), site_info, survey_results, mlt, i, state->local_min_y); } // SW corner, south row if ((i == 0 && state->local_max_y == 15 && x - 1 >= 0 && y + 1 < world_data->world_height && !survey_results->at(x - 1).at(y + 1).surveyed) || (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::South, x, y, i, state->local_max_y), site_info, survey_results, mlt, i, state->local_max_y); } // S edge, south row if (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_ns_edge(survey_results, false, x, y, i, state->local_max_y), site_info, survey_results, mlt, i, state->local_max_y); } // SE corner south row if ((i == 15 && state->local_max_y == 15 && x + 1 < world_data->world_width && y + 1 < world_data->world_height && !survey_results->at(x + 1).at(y + 1).surveyed) || (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::Southeast, x, y, i, state->local_max_y), site_info, survey_results, mlt, i, state->local_max_y); } } for (int8_t k = state->local_min_y; k <= state->local_max_y; k++) { // NW corner, west side if ((state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed)) { incursion_processing_failed = true; } else if (k > state->local_min_y) { // We've already covered the NW corner of the NW, with its complications. process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::Center, x, y, state->local_min_x, k), site_info, survey_results, mlt, state->local_min_x, k); } // W edge, west side if (state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_ew_edge(survey_results, true, x, y, state->local_min_x, k), site_info, survey_results, mlt, state->local_min_x, k); } // SW corner, west side if (state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { incursion_processing_failed = true; } else if (k < state->local_max_y) { // We've already covered the SW corner of the SW tile, with its complicatinons. process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::South, x, y, state->local_min_x, k), site_info, survey_results, mlt, state->local_min_x, k); } // NE corner, east side if ((state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed)) { incursion_processing_failed = true; } else if (k > state->local_min_y) { // We've already covered the NE tile's NE corner, with its complications. process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::East, x, y, state->local_max_x, k), site_info, survey_results, mlt, state->local_max_x, k); } // E edge, east side if (state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { incursion_processing_failed = true; } else { process_embark_incursion_mid_level_tile (translate_ew_edge(survey_results, false, x, y, state->local_max_x, k), site_info, survey_results, mlt, state->local_max_x, k); } // SE corner, east side if (state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { incursion_processing_failed = true; } else if (k < state->local_max_y) { // We've already covered the SE tile's SE corner, with its complications. process_embark_incursion_mid_level_tile (translate_corner(survey_results, embark_assist::defs::directions::Southeast, x, y, state->local_max_x, k), site_info, survey_results, mlt, state->local_max_x, k); } } 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; } //================================================================================= void embark_assist::survey::shutdown() { delete state; state = nullptr; }