Merge remote-tracking branch 'remotes/DFHack/develop' into RemoteServerUnsafe
# Conflicts: # plugins/proto/RemoteFortressReader.proto # plugins/remotefortressreader/remotefortressreader.cpp # scriptsdevelop
						commit
						7b19c9b8f0
					
				| @ -0,0 +1,14 @@ | |||||||
|  | ############### | ||||||
|  | Removed scripts | ||||||
|  | ############### | ||||||
|  | 
 | ||||||
|  | The following scripts were removed for various reasons. | ||||||
|  | 
 | ||||||
|  | .. contents:: | ||||||
|  |    :depth: 2 | ||||||
|  | 
 | ||||||
|  | .. _warn-stuck-trees: | ||||||
|  | 
 | ||||||
|  | warn-stuck-trees | ||||||
|  | ================ | ||||||
|  | The corresponding DF bug, :bug:`9252` was fixed in DF 0.44.01. | ||||||
| @ -0,0 +1,43 @@ | |||||||
|  | #include "Error.h" | ||||||
|  | #include "MiscUtils.h" | ||||||
|  | 
 | ||||||
|  | using namespace DFHack::Error; | ||||||
|  | 
 | ||||||
|  | inline std::string safe_str(const char *s) | ||||||
|  | { | ||||||
|  |     return s ? s : "(NULL)"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | NullPointer::NullPointer(const char *varname) | ||||||
|  |     :All("NULL pointer: " + safe_str(varname)), | ||||||
|  |     varname(varname) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | InvalidArgument::InvalidArgument(const char *expr) | ||||||
|  |     :All("Invalid argument; expected: " + safe_str(expr)), | ||||||
|  |     expr(expr) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | VTableMissing::VTableMissing(const char *name) | ||||||
|  |     :All("Missing vtable address: " + safe_str(name)), | ||||||
|  |     name(name) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | SymbolsXmlParse::SymbolsXmlParse(const char* desc, int id, int row, int col) | ||||||
|  |     :AllSymbols(stl_sprintf("error %d: %s, at row %d col %d", id, desc, row, col)), | ||||||
|  |     desc(safe_str(desc)), id(id), row(row), col(col) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | SymbolsXmlBadAttribute::SymbolsXmlBadAttribute(const char *attr) | ||||||
|  |     :AllSymbols("attribute is either missing or invalid: " + safe_str(attr)), | ||||||
|  |     attr(safe_str(attr)) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | SymbolsXmlNoRoot::SymbolsXmlNoRoot() | ||||||
|  |     :AllSymbols("no root element") | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | SymbolsXmlUnderspecifiedEntry::SymbolsXmlUnderspecifiedEntry(const char *where) | ||||||
|  |     :AllSymbols("Underspecified symbol file entry, each entry needs to set both the name attribute and have a value. parent: " + safe_str(where)), | ||||||
|  |     where(safe_str(where)) | ||||||
|  | {} | ||||||
| @ -0,0 +1,48 @@ | |||||||
|  | PROJECT (embark-assistant) | ||||||
|  | # A list of source files | ||||||
|  | SET(PROJECT_SRCS | ||||||
|  |     biome_type.cpp | ||||||
|  |     embark-assistant.cpp | ||||||
|  |     finder_ui.cpp | ||||||
|  |     help_ui.cpp | ||||||
|  |     matcher.cpp | ||||||
|  |     overlay.cpp | ||||||
|  |     screen.cpp | ||||||
|  |     survey.cpp | ||||||
|  | ) | ||||||
|  | # A list of headers | ||||||
|  | SET(PROJECT_HDRS | ||||||
|  |     biome_type.h | ||||||
|  |     defs.h | ||||||
|  |     embark-assistant.h | ||||||
|  |     finder_ui.h | ||||||
|  |     help_ui.h | ||||||
|  |     matcher.h | ||||||
|  |     overlay.h | ||||||
|  |     screen.h | ||||||
|  |     survey.h | ||||||
|  | ) | ||||||
|  | SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) | ||||||
|  | 
 | ||||||
|  | # mash them together (headers are marked as headers and nothing will try to compile them) | ||||||
|  | LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) | ||||||
|  | 
 | ||||||
|  | # option to use a thread for no particular reason | ||||||
|  | #OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON) | ||||||
|  | #linux | ||||||
|  | IF(UNIX) | ||||||
|  |     add_definitions(-DLINUX_BUILD) | ||||||
|  |     SET(PROJECT_LIBS | ||||||
|  |         # add any extra linux libs here | ||||||
|  |         ${PROJECT_LIBS} | ||||||
|  |     ) | ||||||
|  | # windows | ||||||
|  | ELSE(UNIX) | ||||||
|  |     SET(PROJECT_LIBS | ||||||
|  |         # add any extra windows libs here | ||||||
|  |         ${PROJECT_LIBS} | ||||||
|  |         $(NOINHERIT) | ||||||
|  |     ) | ||||||
|  | ENDIF(UNIX) | ||||||
|  | # this makes sure all the stuff is put in proper places and linked to dfhack | ||||||
|  | DFHACK_PLUGIN(embark-assistant ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) | ||||||
| @ -0,0 +1,754 @@ | |||||||
|  | /* The code is copied from Ragundo's repo referenced below.
 | ||||||
|  | The changes are: | ||||||
|  | - The addition of a .h file reference. | ||||||
|  | - The simplification of the code using ofsub to remove the use of (and | ||||||
|  |   .h reference to) that function (analysis of the code showed the | ||||||
|  |   simplified code is the result, as the ofsub expressions will never be | ||||||
|  |   true given the range of the values it can be passed in these functions). | ||||||
|  | - The change of the main function to take a separate y coordinate for | ||||||
|  |   use in the tropicality determination to allow proper determination of | ||||||
|  |   the tropicality of mid level tiles ("region tiles") referencing a | ||||||
|  |   neighboring world tile's biome. | ||||||
|  | */ | ||||||
|  | /*
 | ||||||
|  | This software is provided 'as-is', without any express or implied | ||||||
|  | warranty.  In no event will the authors be held liable for any damages | ||||||
|  | arising from the use of this software. | ||||||
|  | Permission is granted to anyone to use this software for any purpose, | ||||||
|  | including commercial applications, and to alter it and redistribute it | ||||||
|  | freely, subject to the following restrictions: | ||||||
|  | 1. The origin of this software must not be misrepresented; you must not | ||||||
|  | claim that you wrote the original software. If you use this software | ||||||
|  | in a product, an acknowledgment in the product documentation would be | ||||||
|  | appreciated but is not required. | ||||||
|  | 2. Altered source versions must be plainly marked as such, and must not be | ||||||
|  | misrepresented as being the original software. | ||||||
|  | 3. This notice may not be removed or altered from any source distribution. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | // You can always find the latest version of this plugin in Github
 | ||||||
|  | // https://github.com/ragundo/exportmaps
 | ||||||
|  | 
 | ||||||
|  | #include <utility> | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | #include <df/region_map_entry.h> | ||||||
|  | #include <df/world.h> | ||||||
|  | #include <df/world_data.h> | ||||||
|  | #include <df/biome_type.h> | ||||||
|  | 
 | ||||||
|  | #include "biome_type.h" | ||||||
|  | 
 | ||||||
|  | /*****************************************************************************
 | ||||||
|  | Local functions forward declaration | ||||||
|  | *****************************************************************************/ | ||||||
|  | std::pair<bool, bool> check_tropicality(df::region_map_entry& region, | ||||||
|  |     int a1 | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_lake_biome(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_ocean_biome(df::region_map_entry& region, | ||||||
|  |     bool is_tropical_area_by_latitude | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_desert_biome(df::region_map_entry& region); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_grassland(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_savanna(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_shrubland(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_marsh(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_forest(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_swamp(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_desert_or_grassland_or_savanna(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | int                  get_biome_shrubland_or_marsh(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | /*****************************************************************************
 | ||||||
|  | Module main function. | ||||||
|  | Return the biome type, given a position coordinate expressed in world_tiles | ||||||
|  | The world ref coordinates are used for tropicality determination and may refer | ||||||
|  | to a tile neighboring the "official" one. | ||||||
|  | *****************************************************************************/ | ||||||
|  | int get_biome_type(int world_coord_x, | ||||||
|  |     int world_coord_y, | ||||||
|  |     int world_ref_coord_y | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     // Biome is per region, so get the region where this biome exists
 | ||||||
|  |     df::region_map_entry& region = df::global::world->world_data->region_map[world_coord_x][world_coord_y]; | ||||||
|  | 
 | ||||||
|  |     // Check if the y reference position coordinate belongs to a tropical area
 | ||||||
|  |     std::pair<bool, bool> p = check_tropicality(region, | ||||||
|  |         world_ref_coord_y | ||||||
|  |     ); | ||||||
|  |     bool is_possible_tropical_area_by_latitude = p.first; | ||||||
|  |     bool is_tropical_area_by_latitude = p.second; | ||||||
|  | 
 | ||||||
|  |     // Begin the discrimination
 | ||||||
|  |     if (region.flags.is_set(df::region_map_entry_flags::is_lake)) // is it a lake?
 | ||||||
|  |         return get_lake_biome(region, | ||||||
|  |             is_possible_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     // Not a lake. Check elevation
 | ||||||
|  |     // Elevation greater then 149 means a mountain biome
 | ||||||
|  |     // Elevation below 100 means a ocean biome
 | ||||||
|  |     // Elevation between 100 and 149 are land biomes
 | ||||||
|  | 
 | ||||||
|  |     if (region.elevation >= 150) // is it a mountain?
 | ||||||
|  |         return df::enums::biome_type::biome_type::MOUNTAIN; // 0
 | ||||||
|  | 
 | ||||||
|  |     if (region.elevation < 100) // is it a ocean?
 | ||||||
|  |         return get_ocean_biome(region, | ||||||
|  |             is_possible_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     // land biome. Elevation between 100 and 149
 | ||||||
|  |     if (region.temperature <= -5) | ||||||
|  |     { | ||||||
|  |         if (region.drainage < 75) | ||||||
|  |             return df::enums::biome_type::biome_type::TUNDRA; // 2
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::GLACIER; // 1
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Not a lake, mountain, ocean, glacier or tundra
 | ||||||
|  |     // Vegetation determines the biome type
 | ||||||
|  |     if (region.vegetation < 66) | ||||||
|  |     { | ||||||
|  |         if (region.vegetation < 33) | ||||||
|  |             return get_biome_desert_or_grassland_or_savanna(region, | ||||||
|  |                 is_possible_tropical_area_by_latitude, | ||||||
|  |                 is_tropical_area_by_latitude, | ||||||
|  |                 world_coord_y, | ||||||
|  |                 world_coord_x | ||||||
|  |             ); | ||||||
|  |         else // vegetation between 33 and 65
 | ||||||
|  |             return get_biome_shrubland_or_marsh(region, | ||||||
|  |                 is_possible_tropical_area_by_latitude, | ||||||
|  |                 is_tropical_area_by_latitude, | ||||||
|  |                 world_coord_y, | ||||||
|  |                 world_coord_x | ||||||
|  |             ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Not a lake, mountain, ocean, glacier, tundra, desert, grassland or savanna
 | ||||||
|  |     // vegetation >= 66
 | ||||||
|  |     if (region.drainage >= 33) | ||||||
|  |         return get_biome_forest(region, | ||||||
|  |             is_possible_tropical_area_by_latitude, | ||||||
|  |             is_tropical_area_by_latitude, | ||||||
|  |             world_coord_y, | ||||||
|  |             world_coord_x | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     // Not a lake, mountain, ocean, glacier, tundra, desert, grassland, savanna or forest
 | ||||||
|  |     // vegetation >= 66, drainage < 33
 | ||||||
|  |     return get_biome_swamp(region, | ||||||
|  |         is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude, | ||||||
|  |         world_coord_y, | ||||||
|  |         world_coord_x); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | std::pair<bool, bool> check_tropicality_no_poles_world(df::region_map_entry& region, | ||||||
|  |     int y_pos | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     bool is_possible_tropical_area_by_latitude = false; | ||||||
|  |     bool is_tropical_area_by_latitude = false; | ||||||
|  | 
 | ||||||
|  |     // If there're no poles, tropical area is determined by temperature
 | ||||||
|  |     if (region.temperature >= 75) | ||||||
|  |         is_possible_tropical_area_by_latitude = true; | ||||||
|  |     is_tropical_area_by_latitude = region.temperature >= 85; | ||||||
|  | 
 | ||||||
|  |     return std::pair<bool, bool>(is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | std::pair<bool, bool> check_tropicality_north_pole_only_world(df::region_map_entry& region, | ||||||
|  |     int y_pos | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int v6; | ||||||
|  |     bool is_possible_tropical_area_by_latitude = false; | ||||||
|  |     bool is_tropical_area_by_latitude = false; | ||||||
|  |     df::world_data* wdata = df::global::world->world_data; | ||||||
|  | 
 | ||||||
|  |     // Scale the smaller worlds to the big one
 | ||||||
|  |     if (wdata->world_height == 17) | ||||||
|  |         v6 = 16 * y_pos; | ||||||
|  |     else if (wdata->world_height == 33) | ||||||
|  |         v6 = 8 * y_pos; | ||||||
|  |     else if (wdata->world_height == 65) | ||||||
|  |         v6 = 4 * y_pos; | ||||||
|  |     else if (wdata->world_height == 129) | ||||||
|  |         v6 = 2 * y_pos; | ||||||
|  |     else | ||||||
|  |         v6 = y_pos; | ||||||
|  | 
 | ||||||
|  |     is_possible_tropical_area_by_latitude = v6 > 170; | ||||||
|  |     is_tropical_area_by_latitude = v6 >= 200; | ||||||
|  | 
 | ||||||
|  |     return std::pair<bool, bool>(is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | std::pair<bool, bool> check_tropicality_south_pole_only_world(df::region_map_entry& region, | ||||||
|  |     int y_pos | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int v6 = df::global::world->world_data->world_height - y_pos - 1; | ||||||
|  |     bool is_possible_tropical_area_by_latitude = false; | ||||||
|  |     bool is_tropical_area_by_latitude = false; | ||||||
|  |     df::world_data* wdata = df::global::world->world_data; | ||||||
|  | 
 | ||||||
|  |     if (wdata->world_height == 17) | ||||||
|  |         v6 *= 16; | ||||||
|  |     else if (wdata->world_height == 33) | ||||||
|  |         v6 *= 8; | ||||||
|  |     else if (wdata->world_height == 65) | ||||||
|  |         v6 *= 4; | ||||||
|  |     else if (wdata->world_height == 129) | ||||||
|  |         v6 *= 2; | ||||||
|  |     else | ||||||
|  |         v6 *= 1; | ||||||
|  | 
 | ||||||
|  |     is_possible_tropical_area_by_latitude = v6 > 170; | ||||||
|  |     is_tropical_area_by_latitude = v6 >= 200; | ||||||
|  | 
 | ||||||
|  |     return std::pair<bool, bool>(is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | std::pair<bool, bool> check_tropicality_both_poles_world(df::region_map_entry& region, | ||||||
|  |     int y_pos | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int v6; | ||||||
|  |     bool is_possible_tropical_area_by_latitude = false; | ||||||
|  |     bool is_tropical_area_by_latitude = false; | ||||||
|  |     df::world_data* wdata = df::global::world->world_data; | ||||||
|  | 
 | ||||||
|  |     if (y_pos < wdata->world_height / 2) | ||||||
|  |         v6 = 2 * y_pos; | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         v6 = wdata->world_height + 2 * (wdata->world_height / 2 - y_pos) - 1; | ||||||
|  |         if (v6 < 0) | ||||||
|  |             v6 = 0; | ||||||
|  |         if (v6 >= wdata->world_height) | ||||||
|  |             v6 = wdata->world_height - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (wdata->world_height == 17) | ||||||
|  |         v6 *= 16; | ||||||
|  |     else if (wdata->world_height == 33) | ||||||
|  |         v6 *= 8; | ||||||
|  |     else if (wdata->world_height == 65) | ||||||
|  |         v6 *= 4; | ||||||
|  |     else if (wdata->world_height == 129) | ||||||
|  |         v6 *= 2; | ||||||
|  |     else | ||||||
|  |         v6 *= 1; | ||||||
|  | 
 | ||||||
|  |     is_possible_tropical_area_by_latitude = v6 > 170; | ||||||
|  |     is_tropical_area_by_latitude = v6 >= 200; | ||||||
|  | 
 | ||||||
|  |     return std::pair<bool, bool>(is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude | ||||||
|  |         ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | std::pair<bool, bool> check_tropicality(df::region_map_entry& region, | ||||||
|  |     int y_pos | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int flip_latitude = df::global::world->world_data->flip_latitude; | ||||||
|  | 
 | ||||||
|  |     if (flip_latitude == -1)  // NO POLES
 | ||||||
|  |         return check_tropicality_no_poles_world(region, | ||||||
|  |             y_pos | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     else if (flip_latitude == 0) // NORTH POLE ONLY
 | ||||||
|  |         return check_tropicality_north_pole_only_world(region, | ||||||
|  |             y_pos | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     else if (flip_latitude == 1) // SOUTH_POLE ONLY
 | ||||||
|  |         return check_tropicality_south_pole_only_world(region, | ||||||
|  |             y_pos | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     else if (flip_latitude == 2) // BOTH POLES
 | ||||||
|  |         return check_tropicality_both_poles_world(region, | ||||||
|  |             y_pos | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     return std::pair<bool, bool>(false, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_parameter_percentage(int flip_latitude, | ||||||
|  |     int y_pos, | ||||||
|  |     int rainfall, | ||||||
|  |     int world_height | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int result; | ||||||
|  |     int ypos = y_pos; | ||||||
|  | 
 | ||||||
|  |     if (flip_latitude == -1) // NO POLES
 | ||||||
|  |         return 100; | ||||||
|  | 
 | ||||||
|  |     else if (flip_latitude == 1) // SOUTH POLE
 | ||||||
|  |         ypos = world_height - y_pos - 1; | ||||||
|  |     else if (flip_latitude == 2) // NORTH & SOUTH POLE
 | ||||||
|  |     { | ||||||
|  |         if (ypos < world_height / 2) | ||||||
|  |             ypos *= 2; | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             ypos = world_height + 2 * (world_height / 2 - ypos) - 1; | ||||||
|  |             if (ypos < 0) | ||||||
|  |                 ypos = 0; | ||||||
|  |             if (ypos >= world_height) | ||||||
|  |                 ypos = world_height - 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int latitude; // 0 - 256 (size of a large world)
 | ||||||
|  |     switch (world_height) | ||||||
|  |     { | ||||||
|  |     case 17:                                    // Pocket world
 | ||||||
|  |         latitude = 16 * ypos; | ||||||
|  |         break; | ||||||
|  |     case 33:                                    // Smaller world
 | ||||||
|  |         latitude = 8 * ypos; | ||||||
|  |         break; | ||||||
|  |     case 65:                                    // Small world
 | ||||||
|  |         latitude = 4 * ypos; | ||||||
|  |         break; | ||||||
|  |     case 129:                                  // Medium world
 | ||||||
|  |         latitude = 2 * ypos; | ||||||
|  |         break; | ||||||
|  |     default:                                  // Large world
 | ||||||
|  |         latitude = ypos; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // latitude > 220
 | ||||||
|  |     if ((latitude - 171) > 49) | ||||||
|  |         return 100; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // Latitude between 191 and 200
 | ||||||
|  |     if ((latitude > 190) && (latitude <  201)) | ||||||
|  |         return 0; | ||||||
|  | 
 | ||||||
|  |     // Latitude between 201 and 220
 | ||||||
|  |     if ((latitude > 190) && (latitude >= 201)) | ||||||
|  |         result = rainfall + 16 * (latitude - 207); | ||||||
|  |     else | ||||||
|  |         // Latitude between 0 and 190
 | ||||||
|  |         result = (16 * (184 - latitude) - rainfall); | ||||||
|  | 
 | ||||||
|  |     if (result < 0) | ||||||
|  |         return 0; | ||||||
|  | 
 | ||||||
|  |     if (result > 100) | ||||||
|  |         return 100; | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | // return some unknow parameter as a percentage
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_region_parameter(int y, | ||||||
|  |     int x, | ||||||
|  |     char a4 | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int result = 100; | ||||||
|  | 
 | ||||||
|  |     if ((df::global::cur_season && *df::global::cur_season != 1) || !a4) | ||||||
|  |     { | ||||||
|  |         int world_height = df::global::world->world_data->world_height; | ||||||
|  |         if (world_height > 65) // Medium and large worlds
 | ||||||
|  |         { | ||||||
|  |             // access to region 2D array
 | ||||||
|  |             df::region_map_entry& region = df::global::world->world_data->region_map[x][y]; | ||||||
|  |             return get_parameter_percentage(df::global::world->world_data->flip_latitude, | ||||||
|  |                 y, | ||||||
|  |                 region.rainfall, | ||||||
|  |                 world_height | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_lake_biome(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     // salinity values tell us the lake type
 | ||||||
|  |     // greater than 66 is a salt water lake
 | ||||||
|  |     // between 33 and 65 is a brackish water lake
 | ||||||
|  |     // less than 33 is a fresh water lake
 | ||||||
|  |     if (region.salinity < 66) | ||||||
|  |     { | ||||||
|  |         if (region.salinity < 33) | ||||||
|  |             if (is_possible_tropical_area_by_latitude) | ||||||
|  |                 return df::enums::biome_type::biome_type::LAKE_TROPICAL_FRESHWATER; // 39
 | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::LAKE_TEMPERATE_FRESHWATER; // 36
 | ||||||
|  |         else // salinity >= 33
 | ||||||
|  |             if (is_possible_tropical_area_by_latitude) | ||||||
|  |                 return df::enums::biome_type::biome_type::LAKE_TROPICAL_BRACKISHWATER; // 40
 | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::LAKE_TEMPERATE_BRACKISHWATER; // 37
 | ||||||
|  |     } | ||||||
|  |     else // salinity >= 66
 | ||||||
|  |     { | ||||||
|  |         if (is_possible_tropical_area_by_latitude) | ||||||
|  |             return df::enums::biome_type::biome_type::LAKE_TROPICAL_SALTWATER;// 41
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::LAKE_TEMPERATE_SALTWATER; // 38
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_ocean_biome(df::region_map_entry& region, | ||||||
|  |     bool is_tropical_area_by_latitude | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (is_tropical_area_by_latitude) | ||||||
|  |         return df::enums::biome_type::biome_type::OCEAN_TROPICAL; // 27
 | ||||||
|  |     else | ||||||
|  |         if (region.temperature <= -5) | ||||||
|  |             return df::enums::biome_type::biome_type::OCEAN_ARCTIC; // 29
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::OCEAN_TEMPERATE; // 28
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_desert_biome(df::region_map_entry& region) | ||||||
|  | { | ||||||
|  |     if (region.drainage < 66) | ||||||
|  |     { | ||||||
|  |         if (region.drainage < 33) | ||||||
|  |             return df::enums::biome_type::biome_type::DESERT_SAND; // 26
 | ||||||
|  |         else // drainage between 33 and 65
 | ||||||
|  |             return df::enums::biome_type::biome_type::DESERT_ROCK; // 25
 | ||||||
|  |     } | ||||||
|  |     // drainage >= 66
 | ||||||
|  |     return df::enums::biome_type::biome_type::DESERT_BADLAND; // 24
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_grassland(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 66)) || is_tropical_area_by_latitude) | ||||||
|  |         return df::enums::biome_type::biome_type::GRASSLAND_TROPICAL; // 21
 | ||||||
|  |     else | ||||||
|  |         return df::enums::biome_type::biome_type::GRASSLAND_TEMPERATE; //18;
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_savanna(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) <= 6)) || is_tropical_area_by_latitude) | ||||||
|  |         return df::enums::biome_type::biome_type::SAVANNA_TROPICAL; // 22
 | ||||||
|  |     else | ||||||
|  |         return df::enums::biome_type::biome_type::SAVANNA_TEMPERATE; //19;
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_desert_or_grassland_or_savanna(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (region.vegetation < 20) | ||||||
|  |     { | ||||||
|  |         if (region.vegetation < 10) | ||||||
|  |             return get_desert_biome(region); | ||||||
|  |         else // vegetation between 10 and 19
 | ||||||
|  |             return get_biome_grassland(is_possible_tropical_area_by_latitude, is_tropical_area_by_latitude, y, x); | ||||||
|  |     } | ||||||
|  |     // vegetation between 20 and 32
 | ||||||
|  |     return get_biome_savanna(is_possible_tropical_area_by_latitude, is_tropical_area_by_latitude, y, x); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_shrubland(bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 66 || is_tropical_area_by_latitude)) | ||||||
|  |         return df::enums::biome_type::biome_type::SHRUBLAND_TROPICAL; // 23
 | ||||||
|  |     else | ||||||
|  |         return df::enums::biome_type::biome_type::SHRUBLAND_TEMPERATE; // 20
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_marsh(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (region.salinity < 66) | ||||||
|  |     { | ||||||
|  |         if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 66)) || is_tropical_area_by_latitude) | ||||||
|  |             return df::enums::biome_type::biome_type::MARSH_TROPICAL_FRESHWATER; // 10
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::MARSH_TEMPERATE_FRESHWATER; // 5
 | ||||||
|  |     } | ||||||
|  |     else // drainage < 33, salinity >= 66
 | ||||||
|  |     { | ||||||
|  |         if ((is_possible_tropical_area_by_latitude && (get_region_parameter(y, x, 0) < 66)) || is_tropical_area_by_latitude) | ||||||
|  |             return df::enums::biome_type::biome_type::MARSH_TROPICAL_SALTWATER; // 11
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::MARSH_TEMPERATE_SALTWATER; // 6
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_shrubland_or_marsh(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     if (region.drainage >= 33) | ||||||
|  |         return get_biome_shrubland(is_possible_tropical_area_by_latitude, | ||||||
|  |             is_tropical_area_by_latitude, | ||||||
|  |             y, | ||||||
|  |             x | ||||||
|  |         ); | ||||||
|  |     // drainage < 33
 | ||||||
|  |     return get_biome_marsh(region, | ||||||
|  |         is_possible_tropical_area_by_latitude, | ||||||
|  |         is_tropical_area_by_latitude, | ||||||
|  |         y, | ||||||
|  |         x | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_forest(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int parameter = get_region_parameter(y, x, 0); | ||||||
|  | 
 | ||||||
|  |     // drainage >= 33, not tropical area
 | ||||||
|  |     if (!is_possible_tropical_area_by_latitude) | ||||||
|  |     { | ||||||
|  |         if ((region.rainfall < 75) || (region.temperature < 65)) | ||||||
|  |         { | ||||||
|  |             if (region.temperature >= 10) | ||||||
|  |                 return df::enums::biome_type::biome_type::FOREST_TEMPERATE_CONIFER; // 13
 | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::FOREST_TAIGA; // 12
 | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::FOREST_TEMPERATE_BROADLEAF; // 14
 | ||||||
|  |     } | ||||||
|  |     else // drainage >= 33, tropical area
 | ||||||
|  |     { | ||||||
|  |         if (((parameter < 66) || is_tropical_area_by_latitude) && (region.rainfall < 75)) | ||||||
|  |             return df::enums::biome_type::biome_type::FOREST_TROPICAL_CONIFER; // 15
 | ||||||
|  |         if (parameter < 66) | ||||||
|  |             return df::enums::biome_type::biome_type::FOREST_TROPICAL_DRY_BROADLEAF; // 16
 | ||||||
|  |         if (is_tropical_area_by_latitude) | ||||||
|  |             return df::enums::biome_type::biome_type::FOREST_TROPICAL_MOIST_BROADLEAF; // 17
 | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if ((region.rainfall < 75) || (region.temperature < 65)) | ||||||
|  |             { | ||||||
|  |                 if (region.temperature >= 10) | ||||||
|  |                     return df::enums::biome_type::biome_type::FOREST_TEMPERATE_CONIFER; // 13
 | ||||||
|  |                 else | ||||||
|  |                     return df::enums::biome_type::biome_type::FOREST_TAIGA; // 12
 | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::FOREST_TEMPERATE_BROADLEAF; // 14
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | // Utility function
 | ||||||
|  | //
 | ||||||
|  | //----------------------------------------------------------------------------//
 | ||||||
|  | int get_biome_swamp(df::region_map_entry& region, | ||||||
|  |     bool is_possible_tropical_area_by_latitude, | ||||||
|  |     bool is_tropical_area_by_latitude, | ||||||
|  |     int y, | ||||||
|  |     int x | ||||||
|  | ) | ||||||
|  | { | ||||||
|  |     int parameter = get_region_parameter(y, x, 0); | ||||||
|  | 
 | ||||||
|  |     if (is_possible_tropical_area_by_latitude) | ||||||
|  |     { | ||||||
|  |         if (region.salinity < 66) | ||||||
|  |         { | ||||||
|  |             if ((parameter < 66) || is_tropical_area_by_latitude) | ||||||
|  |                 return df::enums::biome_type::biome_type::SWAMP_TROPICAL_FRESHWATER; // 7
 | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_FRESHWATER;// 3
 | ||||||
|  |         } | ||||||
|  |         else // elevation between 100 and 149, vegetation >= 66, drainage < 33, salinity >= 66
 | ||||||
|  |         { | ||||||
|  |             if ((parameter < 66) || is_tropical_area_by_latitude) | ||||||
|  |             { | ||||||
|  |                 if (region.drainage < 10) | ||||||
|  |                     return df::enums::biome_type::biome_type::SWAMP_MANGROVE; //9
 | ||||||
|  |                 else // drainage >= 10
 | ||||||
|  |                     return df::enums::biome_type::biome_type::SWAMP_TROPICAL_SALTWATER; // 8
 | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_SALTWATER; // 4
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else // elevation between 100 and 149, vegetation >= 66, drainage < 33, not tropical area
 | ||||||
|  |     { | ||||||
|  |         if (region.salinity >= 66) | ||||||
|  |             return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_SALTWATER; // 4
 | ||||||
|  |         else | ||||||
|  |             return df::enums::biome_type::biome_type::SWAMP_TEMPERATE_FRESHWATER; // 3
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | //  world_coord_x/y is the location of the tile "owning" the biome, while world_ref_coord_y is the
 | ||||||
|  | //  location of the tile the biome appears on. They differ when a mid level tile ("region tile")
 | ||||||
|  | //  refers to a neighboring tile for the biome parameters. The difference can affect the tropicality
 | ||||||
|  | //  determination. Since Tropicality is determined by latitude, the x coordinate of the reference is
 | ||||||
|  | //  omitted.
 | ||||||
|  | //
 | ||||||
|  | int get_biome_type(int world_coord_x, int world_coord_y, int world_ref_coord_y); | ||||||
| @ -0,0 +1,256 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using std::array; | ||||||
|  | using std::ostringstream; | ||||||
|  | using std::string; | ||||||
|  | using std::vector; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace defs { | ||||||
|  |         //  Survey types
 | ||||||
|  |         //
 | ||||||
|  |         enum class river_sizes { | ||||||
|  |             None, | ||||||
|  |             Brook, | ||||||
|  |             Stream, | ||||||
|  |             Minor, | ||||||
|  |             Medium, | ||||||
|  |             Major | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct mid_level_tile { | ||||||
|  |             bool aquifer = false; | ||||||
|  |             bool clay = false; | ||||||
|  |             bool sand = false; | ||||||
|  |             bool flux = false; | ||||||
|  |             int8_t soil_depth; | ||||||
|  |             int8_t offset; | ||||||
|  |             int16_t elevation; | ||||||
|  |             bool river_present = false; | ||||||
|  |             int16_t river_elevation = 100; | ||||||
|  |             int8_t biome_offset; | ||||||
|  |             uint8_t savagery_level;  // 0 - 2
 | ||||||
|  |             uint8_t evilness_level;  // 0 - 2
 | ||||||
|  |             std::vector<bool> metals; | ||||||
|  |             std::vector<bool> economics; | ||||||
|  |             std::vector<bool> minerals; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef std::array<std::array<mid_level_tile, 16>, 16> mid_level_tiles; | ||||||
|  | //        typedef mid_level_tile mid_level_tiles[16][16];
 | ||||||
|  | 
 | ||||||
|  |         struct region_tile_datum { | ||||||
|  |             bool surveyed = false; | ||||||
|  |             uint16_t aquifer_count = 0; | ||||||
|  |             uint16_t clay_count = 0; | ||||||
|  |             uint16_t sand_count = 0; | ||||||
|  |             uint16_t flux_count = 0; | ||||||
|  |             uint8_t min_region_soil = 10; | ||||||
|  |             uint8_t max_region_soil = 0; | ||||||
|  |             bool waterfall = false; | ||||||
|  | 
 | ||||||
|  |             river_sizes river_size; | ||||||
|  |             int16_t biome_index[10];  // Indexed through biome_offset; -1 = null, Index of region, [0] not used
 | ||||||
|  |             int16_t biome[10];        // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used
 | ||||||
|  |             uint8_t biome_count; | ||||||
|  |             bool evil_weather[10]; | ||||||
|  |             bool evil_weather_possible; | ||||||
|  |             bool evil_weather_full; | ||||||
|  |             bool reanimating[10]; | ||||||
|  |             bool reanimating_possible; | ||||||
|  |             bool reanimating_full; | ||||||
|  |             bool thralling[10]; | ||||||
|  |             bool thralling_possible; | ||||||
|  |             bool thralling_full; | ||||||
|  |             uint16_t savagery_count[3]; | ||||||
|  |             uint16_t evilness_count[3]; | ||||||
|  |             std::vector<bool> metals; | ||||||
|  |             std::vector<bool> economics; | ||||||
|  |             std::vector<bool> minerals; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct geo_datum { | ||||||
|  |             uint8_t soil_size = 0; | ||||||
|  |             bool top_soil_only = true; | ||||||
|  |             bool top_soil_aquifer_only = true; | ||||||
|  |             bool aquifer_absent = true; | ||||||
|  |             bool clay_absent = true; | ||||||
|  |             bool sand_absent = true; | ||||||
|  |             bool flux_absent = true; | ||||||
|  |             std::vector<bool> possible_metals; | ||||||
|  |             std::vector<bool> possible_economics; | ||||||
|  |             std::vector<bool> possible_minerals; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef std::vector<geo_datum> geo_data; | ||||||
|  | 
 | ||||||
|  |         struct sites { | ||||||
|  |             uint8_t x; | ||||||
|  |             uint8_t y; | ||||||
|  |             char type; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct site_infos { | ||||||
|  |             bool aquifer; | ||||||
|  |             bool aquifer_full; | ||||||
|  |             uint8_t min_soil; | ||||||
|  |             uint8_t max_soil; | ||||||
|  |             bool flat; | ||||||
|  |             bool waterfall; | ||||||
|  |             bool clay; | ||||||
|  |             bool sand; | ||||||
|  |             bool flux; | ||||||
|  |             std::vector<uint16_t> metals; | ||||||
|  |             std::vector<uint16_t> economics; | ||||||
|  |             std::vector<uint16_t> minerals; | ||||||
|  |             //  Could add savagery, evilness, and biomes, but DF provides those easily.
 | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef std::vector<sites> site_lists; | ||||||
|  | 
 | ||||||
|  |         typedef std::vector<std::vector<region_tile_datum>> world_tile_data; | ||||||
|  | 
 | ||||||
|  |         typedef bool mlt_matches[16][16]; | ||||||
|  |         //  An embark region match is indicated by marking the top left corner
 | ||||||
|  |         //  tile as a match. Thus, the bottom and right side won't show matches
 | ||||||
|  |         //  unless the appropriate dimension has a width of 1.
 | ||||||
|  | 
 | ||||||
|  |         struct matches { | ||||||
|  |             bool preliminary_match; | ||||||
|  |             bool contains_match; | ||||||
|  |             mlt_matches mlt_match; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef std::vector<std::vector<matches>> match_results; | ||||||
|  | 
 | ||||||
|  |         //  matcher types
 | ||||||
|  |         //
 | ||||||
|  |         enum class evil_savagery_values : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             All, | ||||||
|  |             Present, | ||||||
|  |             Absent | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class evil_savagery_ranges : int8_t { | ||||||
|  |             Low, | ||||||
|  |             Medium, | ||||||
|  |             High | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class aquifer_ranges : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             All, | ||||||
|  |             Present, | ||||||
|  |             Partial, | ||||||
|  |             Not_All, | ||||||
|  |             Absent | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class river_ranges : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             None, | ||||||
|  |             Brook, | ||||||
|  |             Stream, | ||||||
|  |             Minor, | ||||||
|  |             Medium, | ||||||
|  |             Major | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class yes_no_ranges : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             Yes, | ||||||
|  |             No | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class all_present_ranges : int8_t { | ||||||
|  |             All, | ||||||
|  |             Present | ||||||
|  |         }; | ||||||
|  |         enum class present_absent_ranges : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             Present, | ||||||
|  |             Absent | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         enum class soil_ranges : int8_t { | ||||||
|  |             NA = -1, | ||||||
|  |             None, | ||||||
|  |             Very_Shallow, | ||||||
|  |             Shallow, | ||||||
|  |             Deep, | ||||||
|  |             Very_Deep | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         /*  //  Future possible enhancement
 | ||||||
|  |         enum class freezing_ranges : int8_t { | ||||||
|  |         NA = -1, | ||||||
|  |         Permanent, | ||||||
|  |         At_Least_Partial, | ||||||
|  |         Partial, | ||||||
|  |         At_Most_Partial, | ||||||
|  |         Never | ||||||
|  |         }; | ||||||
|  |         */ | ||||||
|  | 
 | ||||||
|  |         struct finders { | ||||||
|  |             uint16_t x_dim; | ||||||
|  |             uint16_t y_dim; | ||||||
|  |             evil_savagery_values savagery[static_cast<int8_t>(evil_savagery_ranges::High) + 1]; | ||||||
|  |             evil_savagery_values evilness[static_cast<int8_t>(evil_savagery_ranges::High) + 1]; | ||||||
|  |             aquifer_ranges aquifer; | ||||||
|  |             river_ranges min_river; | ||||||
|  |             river_ranges max_river; | ||||||
|  |             yes_no_ranges waterfall; | ||||||
|  |             yes_no_ranges flat; | ||||||
|  |             present_absent_ranges clay; | ||||||
|  |             present_absent_ranges sand; | ||||||
|  |             present_absent_ranges flux; | ||||||
|  |             soil_ranges soil_min; | ||||||
|  |             all_present_ranges soil_min_everywhere; | ||||||
|  |             soil_ranges soil_max; | ||||||
|  |             /*freezing_ranges freezing;*/ | ||||||
|  |             yes_no_ranges evil_weather;  //  Will probably blow up with the magic release arcs...
 | ||||||
|  |             yes_no_ranges reanimation; | ||||||
|  |             yes_no_ranges thralling; | ||||||
|  |             int8_t biome_count_min; // N/A(-1), 1-9
 | ||||||
|  |             int8_t biome_count_max; // N/A(-1), 1-9
 | ||||||
|  |             int8_t region_type_1;   // N/A(-1), df::world_region_type
 | ||||||
|  |             int8_t region_type_2;   // N/A(-1), df::world_region_type
 | ||||||
|  |             int8_t region_type_3;   // N/A(-1), df::world_region_type
 | ||||||
|  |             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
 | ||||||
|  |             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;
 | ||||||
|  |             int16_t economic_1;     // N/A(-1), 0-max_inorganic;
 | ||||||
|  |             int16_t economic_2;     // N/A(-1), 0-max_inorganic;
 | ||||||
|  |             int16_t economic_3;     // N/A(-1), 0-max_inorganic;
 | ||||||
|  |             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;
 | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct match_iterators { | ||||||
|  |             bool active; | ||||||
|  |             uint16_t x;  //  x position of focus when iteration started so we can return it.
 | ||||||
|  |             uint16_t y;  //  y
 | ||||||
|  |             uint16_t i; | ||||||
|  |             uint16_t k; | ||||||
|  |             bool x_right; | ||||||
|  |             bool y_down; | ||||||
|  |             bool inhibit_x_turn; | ||||||
|  |             bool inhibit_y_turn; | ||||||
|  |             uint16_t count; | ||||||
|  |             finders finder; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef void(*find_callbacks) (embark_assist::defs::finders finder); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,296 @@ | |||||||
|  | #include "Core.h" | ||||||
|  | #include <Console.h> | ||||||
|  | #include <Export.h> | ||||||
|  | #include <PluginManager.h> | ||||||
|  | 
 | ||||||
|  | #include <time.h> | ||||||
|  | #include <modules/Gui.h> | ||||||
|  | #include <modules/Screen.h> | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | #include "df/coord2d.h" | ||||||
|  | #include "df/inorganic_flags.h" | ||||||
|  | #include "df/inorganic_raw.h" | ||||||
|  | #include "df/interfacest.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_raws.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | #include "embark-assistant.h" | ||||||
|  | #include "finder_ui.h" | ||||||
|  | #include "matcher.h" | ||||||
|  | #include "overlay.h" | ||||||
|  | #include "survey.h" | ||||||
|  | 
 | ||||||
|  | DFHACK_PLUGIN("embark-assistant"); | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | using namespace df::enums; | ||||||
|  | using namespace Gui; | ||||||
|  | 
 | ||||||
|  | REQUIRE_GLOBAL(world); | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace main { | ||||||
|  |         struct states { | ||||||
|  |             embark_assist::defs::geo_data geo_summary; | ||||||
|  |             embark_assist::defs::world_tile_data survey_results; | ||||||
|  |             embark_assist::defs::site_lists region_sites; | ||||||
|  |             embark_assist::defs::site_infos site_info; | ||||||
|  |             embark_assist::defs::match_results match_results; | ||||||
|  |             embark_assist::defs::match_iterators match_iterator; | ||||||
|  |             uint16_t max_inorganic; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         static states *state = nullptr; | ||||||
|  | 
 | ||||||
|  |         void embark_update (); | ||||||
|  |         void shutdown(); | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void embark_update() { | ||||||
|  |             auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0); | ||||||
|  |             embark_assist::defs::mid_level_tiles mlt; | ||||||
|  |             embark_assist::survey::initiate(&mlt); | ||||||
|  | 
 | ||||||
|  |             embark_assist::survey::survey_mid_level_tile(&state->geo_summary, | ||||||
|  |                 &state->survey_results, | ||||||
|  |                 &mlt); | ||||||
|  | 
 | ||||||
|  |             embark_assist::survey::survey_embark(&mlt, &state->site_info, false); | ||||||
|  |             embark_assist::overlay::set_embark(&state->site_info); | ||||||
|  | 
 | ||||||
|  |             embark_assist::survey::survey_region_sites(&state->region_sites); | ||||||
|  |             embark_assist::overlay::set_sites(&state->region_sites); | ||||||
|  | 
 | ||||||
|  |             embark_assist::overlay::set_mid_level_tile_match(state->match_results.at(screen->location.region_pos.x).at(screen->location.region_pos.y).mlt_match); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void match() { | ||||||
|  | //            color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  | 
 | ||||||
|  |             uint16_t count = embark_assist::matcher::find(&state->match_iterator, | ||||||
|  |                 &state->geo_summary, | ||||||
|  |                 &state->survey_results, | ||||||
|  |                 &state->match_results); | ||||||
|  | 
 | ||||||
|  |             embark_assist::overlay::match_progress(count, &state->match_results, !state->match_iterator.active); | ||||||
|  | 
 | ||||||
|  |             if (!state->match_iterator.active) { | ||||||
|  |                 auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0); | ||||||
|  |                 embark_assist::overlay::set_mid_level_tile_match(state->match_results.at(screen->location.region_pos.x).at(screen->location.region_pos.y).mlt_match); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void clear_match() { | ||||||
|  | //            color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |             if (state->match_iterator.active) { | ||||||
|  |                 embark_assist::matcher::move_cursor(state->match_iterator.x, state->match_iterator.y); | ||||||
|  |             } | ||||||
|  |             embark_assist::survey::clear_results(&state->match_results); | ||||||
|  |             embark_assist::overlay::clear_match_results(); | ||||||
|  |             embark_assist::main::state->match_iterator.active = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void find(embark_assist::defs::finders finder) { | ||||||
|  | //            color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  | 
 | ||||||
|  |             state->match_iterator.x = embark_assist::survey::get_last_pos().x; | ||||||
|  |             state->match_iterator.y = embark_assist::survey::get_last_pos().y; | ||||||
|  |             state->match_iterator.finder = finder; | ||||||
|  |             embark_assist::overlay::initiate_match(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void shutdown() { | ||||||
|  | //            color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |             embark_assist::survey::shutdown(); | ||||||
|  |             embark_assist::finder_ui::shutdown(); | ||||||
|  |             embark_assist::overlay::shutdown(); | ||||||
|  |             delete state; | ||||||
|  |             state = nullptr; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //=======================================================================================
 | ||||||
|  | 
 | ||||||
|  | command_result embark_assistant (color_ostream &out, std::vector <std::string> & parameters); | ||||||
|  | 
 | ||||||
|  | //=======================================================================================
 | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) | ||||||
|  | { | ||||||
|  |     commands.push_back(PluginCommand( | ||||||
|  |         "embark-assistant", "Embark site selection support.", | ||||||
|  |         embark_assistant, true, /* true means that the command can't be used from non-interactive user interface */ | ||||||
|  |         // Extended help string. Used by CR_WRONG_USAGE and the help command:
 | ||||||
|  |         "  This command starts the embark-assist plugin that provides embark site\n" | ||||||
|  |         "  selection help. It has to be called while the pre-embark screen is\n" | ||||||
|  |         "  displayed and shows extended (and correct(?)) resource information for\n" | ||||||
|  |         "  the embark rectangle as well as normally undisplayed sites in the\n" | ||||||
|  |         "  current embark region. It also has a site selection tool with more\n" | ||||||
|  |         "  options than DF's vanilla search tool. For detailed help invoke the\n" | ||||||
|  |         "  in game info screen. Requires 42 lines to display properly.\n" | ||||||
|  |     )); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //=======================================================================================
 | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_shutdown (color_ostream &out) | ||||||
|  | { | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //=======================================================================================
 | ||||||
|  | 
 | ||||||
|  | DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) | ||||||
|  | { | ||||||
|  |     switch (event) { | ||||||
|  |       case DFHack::SC_UNKNOWN: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_WORLD_LOADED: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_WORLD_UNLOADED: | ||||||
|  |       case DFHack::SC_MAP_LOADED: | ||||||
|  |           if (embark_assist::main::state) { | ||||||
|  |               embark_assist::main::shutdown(); | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_MAP_UNLOADED: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_VIEWSCREEN_CHANGED: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_CORE_INITIALIZED: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_BEGIN_UNLOAD: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_PAUSED: | ||||||
|  |           break; | ||||||
|  | 
 | ||||||
|  |       case DFHack::SC_UNPAUSED: | ||||||
|  |           break; | ||||||
|  |     } | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //=======================================================================================
 | ||||||
|  | 
 | ||||||
|  | command_result embark_assistant(color_ostream &out, std::vector <std::string> & parameters) | ||||||
|  | { | ||||||
|  |     if (!parameters.empty()) | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  | 
 | ||||||
|  |     CoreSuspender suspend; | ||||||
|  | 
 | ||||||
|  |     auto screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0); | ||||||
|  |     if (!screen) { | ||||||
|  |         out.printerr("This plugin works only in the embark site selection phase.\n"); | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     df::world_data *world_data = world->world_data; | ||||||
|  | 
 | ||||||
|  |     if (embark_assist::main::state) { | ||||||
|  |         out.printerr("You can't invoke the embark assistant while it's already active.\n"); | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     embark_assist::main::state = new embark_assist::main::states; | ||||||
|  | 
 | ||||||
|  |     embark_assist::main::state->match_iterator.active = false; | ||||||
|  | 
 | ||||||
|  |     //  Find the end of the normal inorganic definitions.
 | ||||||
|  |     embark_assist::main::state->max_inorganic = 0; | ||||||
|  |     for (uint16_t i = 0; i < world->raws.inorganics.size(); i++) { | ||||||
|  |         if (world->raws.inorganics[i]->flags.is_set(df::inorganic_flags::GENERATED)) embark_assist::main::state->max_inorganic = i; | ||||||
|  |     } | ||||||
|  |     embark_assist::main::state->max_inorganic++;  //  To allow it to be used as size() replacement
 | ||||||
|  | 
 | ||||||
|  |     if (!embark_assist::overlay::setup(plugin_self, | ||||||
|  |         embark_assist::main::embark_update, | ||||||
|  |         embark_assist::main::match, | ||||||
|  |         embark_assist::main::clear_match, | ||||||
|  |         embark_assist::main::find, | ||||||
|  |         embark_assist::main::shutdown, | ||||||
|  |         embark_assist::main::state->max_inorganic)) { | ||||||
|  |         return CR_FAILURE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     embark_assist::survey::setup(embark_assist::main::state->max_inorganic); | ||||||
|  |     embark_assist::main::state->geo_summary.resize(world_data->geo_biomes.size()); | ||||||
|  |     embark_assist::main::state->survey_results.resize(world->worldgen.worldgen_parms.dim_x); | ||||||
|  | 
 | ||||||
|  |     for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { | ||||||
|  |         embark_assist::main::state->survey_results[i].resize(world->worldgen.worldgen_parms.dim_y); | ||||||
|  | 
 | ||||||
|  |         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].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; | ||||||
|  |             embark_assist::main::state->survey_results[i][k].min_region_soil = 10; | ||||||
|  |             embark_assist::main::state->survey_results[i][k].max_region_soil = 0; | ||||||
|  |             embark_assist::main::state->survey_results[i][k].waterfall = false; | ||||||
|  |             embark_assist::main::state->survey_results[i][k].river_size = embark_assist::defs::river_sizes::None; | ||||||
|  | 
 | ||||||
|  |             for (uint8_t l = 1; l < 10; l++) { | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].biome_index[l] = -1; | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].biome[l] = -1; | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].evil_weather[l] = false; | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].reanimating[l] = false; | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].thralling[l] = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for (uint8_t l = 0; l < 2; l++) { | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].savagery_count[l] = 0; | ||||||
|  |                 embark_assist::main::state->survey_results[i][k].evilness_count[l] = 0; | ||||||
|  |             } | ||||||
|  |             embark_assist::main::state->survey_results[i][k].metals.resize(embark_assist::main::state->max_inorganic); | ||||||
|  |             embark_assist::main::state->survey_results[i][k].economics.resize(embark_assist::main::state->max_inorganic); | ||||||
|  |             embark_assist::main::state->survey_results[i][k].minerals.resize(embark_assist::main::state->max_inorganic); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     embark_assist::survey::high_level_world_survey(&embark_assist::main::state->geo_summary, | ||||||
|  |         &embark_assist::main::state->survey_results); | ||||||
|  | 
 | ||||||
|  |     embark_assist::main::state->match_results.resize(world->worldgen.worldgen_parms.dim_x); | ||||||
|  | 
 | ||||||
|  |     for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { | ||||||
|  |         embark_assist::main::state->match_results[i].resize(world->worldgen.worldgen_parms.dim_y); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     embark_assist::survey::clear_results(&embark_assist::main::state->match_results); | ||||||
|  |     embark_assist::survey::survey_region_sites(&embark_assist::main::state->region_sites); | ||||||
|  |     embark_assist::overlay::set_sites(&embark_assist::main::state->region_sites); | ||||||
|  | 
 | ||||||
|  |     embark_assist::defs::mid_level_tiles mlt; | ||||||
|  |     embark_assist::survey::survey_mid_level_tile(&embark_assist::main::state->geo_summary, &embark_assist::main::state->survey_results, &mlt); | ||||||
|  |     embark_assist::survey::survey_embark(&mlt, &embark_assist::main::state->site_info, false); | ||||||
|  |     embark_assist::overlay::set_embark(&embark_assist::main::state->site_info); | ||||||
|  | 
 | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
| @ -0,0 +1 @@ | |||||||
|  | #pragma once | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,17 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "PluginManager.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace finder_ui { | ||||||
|  |         void init(DFHack::Plugin *plugin_self, embark_assist::defs::find_callbacks find_callback, uint16_t max_inorganic); | ||||||
|  |         void activate(); | ||||||
|  |         void shutdown(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,314 @@ | |||||||
|  | #include "Core.h" | ||||||
|  | #include <Console.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <set> | ||||||
|  | #include <modules/Gui.h> | ||||||
|  | 
 | ||||||
|  | #include "Types.h" | ||||||
|  | 
 | ||||||
|  | #include "help_ui.h" | ||||||
|  | #include "screen.h" | ||||||
|  | 
 | ||||||
|  | using std::vector; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist{ | ||||||
|  |     namespace help_ui { | ||||||
|  |         enum class pages { | ||||||
|  |             Intro, | ||||||
|  |             General, | ||||||
|  |             Finder, | ||||||
|  |             Caveats | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         class ViewscreenHelpUi : public dfhack_viewscreen | ||||||
|  |         { | ||||||
|  |         public: | ||||||
|  |             ViewscreenHelpUi(); | ||||||
|  | 
 | ||||||
|  |             void feed(std::set<df::interface_key> *input); | ||||||
|  | 
 | ||||||
|  |             void render(); | ||||||
|  | 
 | ||||||
|  |             std::string getFocusString() { return "Help UI"; } | ||||||
|  | 
 | ||||||
|  |         private: | ||||||
|  |             pages current_page = pages::Intro; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void ViewscreenHelpUi::feed(std::set<df::interface_key> *input) { | ||||||
|  |             if (input->count(df::interface_key::LEAVESCREEN)) | ||||||
|  |             { | ||||||
|  |                 input->clear(); | ||||||
|  |                 Screen::dismiss(this); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else if (input->count(df::interface_key::CHANGETAB)) { | ||||||
|  |                 switch (current_page) { | ||||||
|  |                 case pages::Intro: | ||||||
|  |                     current_page = pages::General; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::General: | ||||||
|  |                     current_page = pages::Finder; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::Finder: | ||||||
|  |                     current_page = pages::Caveats; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::Caveats: | ||||||
|  |                     current_page = pages::Intro; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (input->count(df::interface_key::SEC_CHANGETAB)) { | ||||||
|  |                 switch (current_page) { | ||||||
|  |                 case pages::Intro: | ||||||
|  |                     current_page = pages::Caveats; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::General: | ||||||
|  |                     current_page = pages::Intro; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::Finder: | ||||||
|  |                     current_page = pages::General; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case pages::Caveats: | ||||||
|  |                     current_page = pages::Intro; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         void ViewscreenHelpUi::render() { | ||||||
|  |             color_ostream_proxy out(Core::getInstance().getConsole()); | ||||||
|  |             auto screen_size = DFHack::Screen::getWindowSize(); | ||||||
|  |             Screen::Pen pen(' ', COLOR_WHITE); | ||||||
|  |             Screen::Pen site_pen = Screen::Pen(' ', COLOR_YELLOW, COLOR_BLACK, false); | ||||||
|  |             Screen::Pen pen_lr(' ', COLOR_LIGHTRED); | ||||||
|  | 
 | ||||||
|  |             std::vector<std::string> help_text; | ||||||
|  | 
 | ||||||
|  |             Screen::clear(); | ||||||
|  | 
 | ||||||
|  |             switch (current_page) { | ||||||
|  |             case pages::Intro: | ||||||
|  |                 Screen::drawBorder("Embark Assistant Help/Info Introduction Page"); | ||||||
|  | 
 | ||||||
|  |                 help_text.push_back("Embark Assistant is used on the embark selection screen to provide information"); | ||||||
|  |                 help_text.push_back("to help selecting a suitable embark site. It provides three services:"); | ||||||
|  |                 help_text.push_back("- Display of normally invisible sites overlayed on the region map."); | ||||||
|  |                 help_text.push_back("- Embark rectangle resources. More detailed and correct than vanilla DF."); | ||||||
|  |                 help_text.push_back("- Site find search. Richer set of selection criteria than the vanilla"); | ||||||
|  |                 help_text.push_back("  DF Find that Embark Assistant suppresses (by using the same key)."); | ||||||
|  |                 help_text.push_back(""); | ||||||
|  |                 help_text.push_back("The functionality requires a screen height of at least 42 lines to display"); | ||||||
|  |                 help_text.push_back("correctly (that's the height of the Finder screen), as fitting everything"); | ||||||
|  |                 help_text.push_back("onto a standard 80*25 screen would be too challenging. The help is adjusted"); | ||||||
|  |                 help_text.push_back("to fit into onto an 80*42 screen."); | ||||||
|  |                 help_text.push_back("This help/info is split over several screens, and you can move between them"); | ||||||
|  |                 help_text.push_back("using the TAB/Shift-TAB keys, and leave the help from any screen using ESC."); | ||||||
|  |                 help_text.push_back(""); | ||||||
|  |                 help_text.push_back("When the Embark Assistant is started it provides site information (if any)"); | ||||||
|  |                 help_text.push_back("as an overlay over the region map. Beneath that you will find a summary of"); | ||||||
|  |                 help_text.push_back("the resources in the current embark rectangle (explained in more detail on"); | ||||||
|  |                 help_text.push_back("the the next screen)."); | ||||||
|  |                 help_text.push_back("On the right side the command keys the Embark Assistant uses are listed"); | ||||||
|  |                 help_text.push_back("(this partially overwrites the DFHack Embark Tools information until the"); | ||||||
|  |                 help_text.push_back("screen is resized). It can also be mentioned that the DF 'f'ind key help"); | ||||||
|  |                 help_text.push_back("at the bottom of the screen is masked as the functionality is overridden by"); | ||||||
|  |                 help_text.push_back("the Embark Assistant."); | ||||||
|  |                 help_text.push_back("Main screen control keys used by the Embark Assistant:"); | ||||||
|  |                 help_text.push_back("i: Info/Help. Brings up this display."); | ||||||
|  |                 help_text.push_back("f: Brings up the Find Embark screen. See the Find page for more information."); | ||||||
|  |                 help_text.push_back("c: Clears the results of a Find operation, and also cancels an operation if"); | ||||||
|  |                 help_text.push_back("   one is under way."); | ||||||
|  |                 help_text.push_back("q: Quits the Embark Assistant and brings you back to the vanilla DF interface."); | ||||||
|  |                 help_text.push_back("   It can be noted that the Embark Assistant automatically cancels itself"); | ||||||
|  |                 help_text.push_back("   when DF leaves the embark screen either through <ESC>Abort Game or by"); | ||||||
|  |                 help_text.push_back("   embarking."); | ||||||
|  |                 help_text.push_back("Below this a Matching World Tiles count is displayed. It shows the number"); | ||||||
|  |                 help_text.push_back("of World Tiles that have at least one embark matching the Find criteria."); | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::General: | ||||||
|  |                 Screen::drawBorder("Embark Assistant Help/Info General Page"); | ||||||
|  | 
 | ||||||
|  |                 help_text.push_back("The Embark Assistant overlays the region map with characters indicating sites"); | ||||||
|  |                 help_text.push_back("normally not displayed by DF. The following key is used:"); | ||||||
|  |                 help_text.push_back("C: Camp"); | ||||||
|  |                 help_text.push_back("c: Cave. Only displayed if the DF worldgen parameter does not display caves."); | ||||||
|  |                 help_text.push_back("i: Important Location. The author doesn't actually know what those are."); | ||||||
|  |                 help_text.push_back("l: Lair"); | ||||||
|  |                 help_text.push_back("L: Labyrinth"); | ||||||
|  |                 help_text.push_back("M: Monument. The author is unsure how/if this is broken down further."); | ||||||
|  |                 help_text.push_back("S: Shrine"); | ||||||
|  |                 help_text.push_back("V: Vault"); | ||||||
|  |                 help_text.push_back("The Embark info below the region map differs from the vanilla DF display in a"); | ||||||
|  |                 help_text.push_back("few respects. Firstly, it shows resources in the embark rectangle, rather than"); | ||||||
|  |                 help_text.push_back("DF's display of resources in the region DF currently displays. Secondly, the"); | ||||||
|  |                 help_text.push_back("DF display doesn't take elevation based soil erosion or the magma sea depth"); | ||||||
|  |                 help_text.push_back("into consideration, so it can display resources that actually are cut away."); | ||||||
|  |                 help_text.push_back("(It can be noted that the DFHack Sand indicator does take these elements into"); | ||||||
|  |                 help_text.push_back("account)."); | ||||||
|  |                 help_text.push_back("The info the Embark Assistant displays is:"); | ||||||
|  |                 help_text.push_back("Sand, if present"); | ||||||
|  |                 help_text.push_back("Clay, if present"); | ||||||
|  |                 help_text.push_back("Min and Max soil depth in the embark rectangle."); | ||||||
|  |                 help_text.push_back("Flat indicator if all the tiles in the embark have the same elevation."); | ||||||
|  |                 help_text.push_back("Aquifer indicator, color coded as blue if all tiles have an aquifer and light"); | ||||||
|  |                 help_text.push_back("blue if some, but not all, tiles have one."); | ||||||
|  |                 help_text.push_back("Waterfall, if the embark has river elevation differences."); | ||||||
|  |                 help_text.push_back("Flux, if present"); | ||||||
|  |                 help_text.push_back("A list of all metals present in the embark."); | ||||||
|  |                 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."); | ||||||
|  |                 help_text.push_back("In addition to the above, the Find functionality can also produce blinking"); | ||||||
|  |                 help_text.push_back("overlays over the region map and the middle world map to indicate where"); | ||||||
|  |                 help_text.push_back("matching embarks are found. The region display marks the top left corner of"); | ||||||
|  |                 help_text.push_back("a matching embark rectangle as a matching tile."); | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::Finder: | ||||||
|  |                 Screen::drawBorder("Embark Assistant Help/Info Find Page"); | ||||||
|  | 
 | ||||||
|  |                 help_text.push_back("The Embark Assist Finder page is brought up with the f command key."); | ||||||
|  |                 help_text.push_back("The top of the Finder page lists the command keys available on the page:"); | ||||||
|  |                 help_text.push_back("4/6 or horizontal arrow keys to move between the element and element values."); | ||||||
|  |                 help_text.push_back("8/2 or vertical arrow keys to move up/down in the current list."); | ||||||
|  |                 help_text.push_back("ENTER to select a value in the value list, entering it among the selections."); | ||||||
|  |                 help_text.push_back("f to activate the Find functionality using the values in the middle column."); | ||||||
|  |                 help_text.push_back("ESC to leave the screen without activating a Find operation."); | ||||||
|  |                 help_text.push_back("The X and Y dimensions are those of the embark to search for. Unlike DF"); | ||||||
|  |                 help_text.push_back("itself these parameters are initiated to match the actual embark rectangle"); | ||||||
|  |                 help_text.push_back("when a new search is initiated (prior results are cleared."); | ||||||
|  |                 help_text.push_back("The 6 Savagery and Evilness parameters takes some getting used to. They"); | ||||||
|  |                 help_text.push_back("allow for searching for embarks with e.g. has both Good and Evil tiles."); | ||||||
|  |                 help_text.push_back("All as a parameter means every embark tile has to have this value."); | ||||||
|  |                 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("Min/Max rivers should be self explanatory. The Yes and No values of"); | ||||||
|  |                 help_text.push_back("Waterfall, Flat, etc. means one has to be Present and Absent respectivey."); | ||||||
|  |                 help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil"); | ||||||
|  |                 help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and"); | ||||||
|  |                 help_text.push_back("and Present."); | ||||||
|  |                 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 (is it is in vanilla)."); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::Caveats: | ||||||
|  |                 Screen::drawBorder("Embark Assistant Help/Info Caveats Page"); | ||||||
|  | 
 | ||||||
|  |                 help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough"); | ||||||
|  |                 help_text.push_back("world tile match to find tiles that may have matching embarks. This results"); | ||||||
|  |                 help_text.push_back("in an overlay of inverted yellow X on top of the middle world map. Then"); | ||||||
|  |                 help_text.push_back("those tiles are scanned in detail, one feature shell (16*16 world tile"); | ||||||
|  |                 help_text.push_back("block) at a time, and the results are displayed as green inverted X on"); | ||||||
|  |                 help_text.push_back("the same map (replacing or erasing the yellow ones). region map overlay"); | ||||||
|  |                 help_text.push_back("data is generated as well."); | ||||||
|  |                 help_text.push_back(""); | ||||||
|  |                 help_text.push_back("Caveats & technical stuff:"); | ||||||
|  |                 help_text.push_back("- The Find searching uses simulated cursor movement input to DF to get it"); | ||||||
|  |                 help_text.push_back("  to load feature shells and detailed region data, and this costs the"); | ||||||
|  |                 help_text.push_back("  least when done one feature shell at a time."); | ||||||
|  |                 help_text.push_back("- The search strategy causes detailed region data to update surveyed"); | ||||||
|  |                 help_text.push_back("  world info, and this can cause a subsequent search to generate a smaller"); | ||||||
|  |                 help_text.push_back("  set of preliminary matches (yellow tiles) than a previous search."); | ||||||
|  |                 help_text.push_back("  However, this is a bug only if it causes the search to fail to find"); | ||||||
|  |                 help_text.push_back("  actual existing matches."); | ||||||
|  |                 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("- The biome determination logic comes from code provided by Ragundo,"); | ||||||
|  |                 help_text.push_back("  with only marginal changes by the author. References can be found in"); | ||||||
|  |                 help_text.push_back("  the source file."); | ||||||
|  |                 help_text.push_back("- Thralling is determined by weather material interactions causing"); | ||||||
|  |                 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"); | ||||||
|  |                 help_text.push_back("  copy of parts of prospector's code adapted for this plugin."); | ||||||
|  |                 help_text.push_back("- Clay determination is made by finding the reaction MAKE_CLAY_BRICKS."); | ||||||
|  |                 help_text.push_back("  Flux determination is made by finding the reaction PIG_IRON_MAKING."); | ||||||
|  |                 help_text.push_back("- Right world map overlay not implemented as author has failed to"); | ||||||
|  |                 help_text.push_back("  emulate the sizing logic exactly."); | ||||||
|  |                 help_text.push_back("Version 0.1 2017-08-30"); | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             //  Add control keys to first line.
 | ||||||
|  |             embark_assist::screen::paintString(pen_lr, 1, 1, "TAB/Shift-TAB"); | ||||||
|  |             embark_assist::screen::paintString(pen, 14, 1, ":Next/Previous Page"); | ||||||
|  |             embark_assist::screen::paintString(pen_lr, 34, 1, "ESC"); | ||||||
|  |             embark_assist::screen::paintString(pen, 37, 1, ":Leave Info/Help"); | ||||||
|  | 
 | ||||||
|  |             for (uint16_t i = 0; i < help_text.size(); i++) { | ||||||
|  |                 embark_assist::screen::paintString(pen, 1, 2 + i, help_text[i]); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (current_page) { | ||||||
|  |             case pages::Intro: | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 26, "i"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 27, "f"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 28, "c"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 30, "q"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::General: | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 4, "C"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 5, "c"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 6, "i"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 7, "l"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 8, "L"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 9, "M"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 10, "S"); | ||||||
|  |                 embark_assist::screen::paintString(site_pen, 1, 11, "V"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::Finder: | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 4, "4/6"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 5, "8/2"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 6, "ENTER"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 7, "f"); | ||||||
|  |                 embark_assist::screen::paintString(pen_lr, 1, 8, "ESC"); | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             case pages::Caveats: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             dfhack_viewscreen::render(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //===============================================================================
 | ||||||
|  | 
 | ||||||
|  |         ViewscreenHelpUi::ViewscreenHelpUi() { | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //===============================================================================
 | ||||||
|  | //  Exported operations
 | ||||||
|  | //===============================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::help_ui::init(DFHack::Plugin *plugin_self) { | ||||||
|  |     Screen::show(new embark_assist::help_ui::ViewscreenHelpUi(), plugin_self); | ||||||
|  | } | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "PluginManager.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace help_ui { | ||||||
|  |         void init(DFHack::Plugin *plugin_self); | ||||||
|  |     } | ||||||
|  | } | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,21 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace matcher { | ||||||
|  |         void move_cursor(uint16_t x, uint16_t y); | ||||||
|  | 
 | ||||||
|  |         //  Used to iterate over the whole world to generate a map of world tiles
 | ||||||
|  |         //  that contain matching embarks.
 | ||||||
|  |         //
 | ||||||
|  |         uint16_t find(embark_assist::defs::match_iterators *iterator, | ||||||
|  |             embark_assist::defs::geo_data *geo_summary, | ||||||
|  |             embark_assist::defs::world_tile_data *survey_results, | ||||||
|  |             embark_assist::defs::match_results *match_results); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,439 @@ | |||||||
|  | #include <modules/Gui.h> | ||||||
|  | 
 | ||||||
|  | #include "df/coord2d.h" | ||||||
|  | #include "df/inorganic_raw.h" | ||||||
|  | #include "df/dfhack_material_category.h" | ||||||
|  | #include "df/interface_key.h" | ||||||
|  | #include "df/viewscreen.h" | ||||||
|  | #include "df/viewscreen_choose_start_sitest.h" | ||||||
|  | #include "df/world.h" | ||||||
|  | #include "df/world_raws.h" | ||||||
|  | 
 | ||||||
|  | #include "finder_ui.h" | ||||||
|  | #include "help_ui.h" | ||||||
|  | #include "overlay.h" | ||||||
|  | #include "screen.h" | ||||||
|  | 
 | ||||||
|  | using df::global::world; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace overlay { | ||||||
|  |         DFHack::Plugin *plugin_self; | ||||||
|  |         const Screen::Pen empty_pen = Screen::Pen('\0', COLOR_YELLOW, COLOR_BLACK, false); | ||||||
|  |         const Screen::Pen yellow_x_pen = Screen::Pen('X', COLOR_BLACK, COLOR_YELLOW, false); | ||||||
|  |         const Screen::Pen green_x_pen = Screen::Pen('X', COLOR_BLACK, COLOR_GREEN, false); | ||||||
|  | 
 | ||||||
|  |         struct display_strings { | ||||||
|  |             Screen::Pen pen; | ||||||
|  |             std::string text; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef Screen::Pen *pen_column; | ||||||
|  | 
 | ||||||
|  |         struct states { | ||||||
|  |             int blink_count = 0; | ||||||
|  |             bool show = true; | ||||||
|  | 
 | ||||||
|  |             bool matching = false; | ||||||
|  |             bool match_active = false; | ||||||
|  | 
 | ||||||
|  |             embark_update_callbacks embark_update; | ||||||
|  |             match_callbacks match_callback; | ||||||
|  |             clear_match_callbacks clear_match_callback; | ||||||
|  |             embark_assist::defs::find_callbacks find_callback; | ||||||
|  |             shutdown_callbacks shutdown_callback; | ||||||
|  | 
 | ||||||
|  |             Screen::Pen site_grid[16][16]; | ||||||
|  |             uint8_t current_site_grid = 0; | ||||||
|  | 
 | ||||||
|  |             std::vector<display_strings> embark_info; | ||||||
|  | 
 | ||||||
|  |             Screen::Pen region_match_grid[16][16]; | ||||||
|  | 
 | ||||||
|  |             pen_column *world_match_grid = nullptr; | ||||||
|  |             uint16_t match_count = 0; | ||||||
|  | 
 | ||||||
|  |             uint16_t max_inorganic; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         static states *state = nullptr; | ||||||
|  | 
 | ||||||
|  |         //====================================================================
 | ||||||
|  | 
 | ||||||
|  | /*     //  Attempt to replicate the DF logic for sizing the right world map. This
 | ||||||
|  |        //  code seems to compute the values correctly, but the author hasn't been
 | ||||||
|  |        //  able to apply them at the same time as DF does to 100%.
 | ||||||
|  |        //  DF seems to round down on 0.5 values.
 | ||||||
|  |        df::coord2d  world_dimension_size(uint16_t available_screen, uint16_t map_size) { | ||||||
|  |             uint16_t result; | ||||||
|  | 
 | ||||||
|  |             for (uint16_t factor = 1; factor < 17; factor++) { | ||||||
|  |                 result = map_size / factor; | ||||||
|  |                 if ((map_size - result * factor) * 2 != factor) { | ||||||
|  |                     result = (map_size + factor / 2) / factor; | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 if (result <= available_screen) { | ||||||
|  |                     return {result, factor}; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return{16, 16};  //  Should never get here.
 | ||||||
|  |         } | ||||||
|  | */ | ||||||
|  |         //====================================================================
 | ||||||
|  | 
 | ||||||
|  |         class ViewscreenOverlay : public df::viewscreen_choose_start_sitest | ||||||
|  |         { | ||||||
|  |         public: | ||||||
|  |             typedef df::viewscreen_choose_start_sitest interpose_base; | ||||||
|  | 
 | ||||||
|  |             void send_key(const df::interface_key &key) | ||||||
|  |             { | ||||||
|  |                 std::set< df::interface_key > keys; | ||||||
|  |                 keys.insert(key); | ||||||
|  |                 this->feed(&keys); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input)) | ||||||
|  |             { | ||||||
|  | //                color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |                 if (input->count(df::interface_key::CUSTOM_Q)) { | ||||||
|  |                     state->shutdown_callback(); | ||||||
|  |                     return; | ||||||
|  | 
 | ||||||
|  |                 } | ||||||
|  |                 else if (input->count(df::interface_key::SETUP_LOCAL_X_MUP) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_X_MDOWN) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_Y_MUP) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_Y_MDOWN) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_X_UP) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_X_DOWN) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_Y_UP) || | ||||||
|  |                     input->count(df::interface_key::SETUP_LOCAL_Y_DOWN)) { | ||||||
|  |                     INTERPOSE_NEXT(feed)(input); | ||||||
|  |                     state->embark_update(); | ||||||
|  |                 } | ||||||
|  |                 else if (input->count(df::interface_key::CUSTOM_C)) { | ||||||
|  |                     state->match_active = false; | ||||||
|  |                     state->matching = false; | ||||||
|  |                     state->clear_match_callback(); | ||||||
|  |                 } | ||||||
|  |                 else if (input->count(df::interface_key::CUSTOM_F)) { | ||||||
|  |                     if (!state->match_active && !state->matching) { | ||||||
|  |                         embark_assist::finder_ui::init(embark_assist::overlay::plugin_self, state->find_callback, state->max_inorganic); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else if (input->count(df::interface_key::CUSTOM_I)) { | ||||||
|  |                     embark_assist::help_ui::init(embark_assist::overlay::plugin_self); | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     INTERPOSE_NEXT(feed)(input); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             //====================================================================
 | ||||||
|  | 
 | ||||||
|  |             DEFINE_VMETHOD_INTERPOSE(void, render, ()) | ||||||
|  |             { | ||||||
|  |                 INTERPOSE_NEXT(render)(); | ||||||
|  | //                color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |                 auto current_screen = Gui::getViewscreenByType<df::viewscreen_choose_start_sitest>(0); | ||||||
|  |                 int16_t x = current_screen->location.region_pos.x; | ||||||
|  |                 int16_t y = current_screen->location.region_pos.y; | ||||||
|  |                 auto width = Screen::getWindowSize().x; | ||||||
|  |                 auto height = Screen::getWindowSize().y; | ||||||
|  | 
 | ||||||
|  |                 state->blink_count++; | ||||||
|  |                 if (state->blink_count == 35) { | ||||||
|  |                     state->blink_count = 0; | ||||||
|  |                     state->show = !state->show; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (state->matching) state->show = true; | ||||||
|  | 
 | ||||||
|  |                 Screen::drawBorder("Embark Assistant"); | ||||||
|  | 
 | ||||||
|  |                 Screen::Pen pen_lr(' ', COLOR_LIGHTRED); | ||||||
|  |                 Screen::Pen pen_w(' ', COLOR_WHITE); | ||||||
|  | 
 | ||||||
|  |                 Screen::paintString(pen_lr, width - 28, 20, "i", false); | ||||||
|  |                 Screen::paintString(pen_w, width - 27, 20, ":Embark Assistant Info", false); | ||||||
|  |                 Screen::paintString(pen_lr, width - 28, 21, "f", false); | ||||||
|  |                 Screen::paintString(pen_w, width - 27, 21, ":Find Embark ", false); | ||||||
|  |                 Screen::paintString(pen_lr, width - 28, 22, "c", false); | ||||||
|  |                 Screen::paintString(pen_w, width - 27, 22, ":Cancel/Clear Find", false); | ||||||
|  |                 Screen::paintString(pen_lr, width - 28, 23, "q", false); | ||||||
|  |                 Screen::paintString(pen_w, width - 27, 23, ":Quit Embark Assistant", false); | ||||||
|  |                 Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles", false); | ||||||
|  |                 Screen::paintString(empty_pen, width - 7, 25, to_string(state->match_count), false); | ||||||
|  | 
 | ||||||
|  |                 if (height > 25) {  //  Mask the vanilla DF find help as it's overridden.
 | ||||||
|  |                     Screen::paintString(pen_w, 50, height - 2, "                          ", false); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 for (uint8_t i = 0; i < 16; i++) { | ||||||
|  |                     for (uint8_t k = 0; k < 16; k++) { | ||||||
|  |                         if (state->site_grid[i][k].ch) { | ||||||
|  |                             Screen::paintTile(state->site_grid[i][k], i + 1, k + 2); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 for (auto i = 0; i < state->embark_info.size(); i++) { | ||||||
|  |                     embark_assist::screen::paintString(state->embark_info[i].pen, 1, i + 19, state->embark_info[i].text, false); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (state->show) { | ||||||
|  |                     int16_t left_x = x - (width / 2 - 7 - 18 + 1) / 2; | ||||||
|  |                     int16_t right_x; | ||||||
|  |                     int16_t top_y = y - (height - 8 - 2 + 1) / 2; | ||||||
|  |                     int16_t bottom_y; | ||||||
|  | 
 | ||||||
|  |                     if (left_x < 0) { left_x = 0; } | ||||||
|  | 
 | ||||||
|  |                     if (top_y < 0) { top_y = 0; } | ||||||
|  |                      | ||||||
|  |                     right_x = left_x + width / 2 - 7 - 18; | ||||||
|  |                     bottom_y = top_y + height - 8 - 2; | ||||||
|  |                      | ||||||
|  |                     if (right_x >= world->worldgen.worldgen_parms.dim_x) { | ||||||
|  |                         right_x = world->worldgen.worldgen_parms.dim_x - 1; | ||||||
|  |                         left_x = right_x - (width / 2 - 7 - 18); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (bottom_y >= world->worldgen.worldgen_parms.dim_y) { | ||||||
|  |                         bottom_y = world->worldgen.worldgen_parms.dim_y - 1; | ||||||
|  |                         top_y = bottom_y - (height - 8 - 2); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (left_x < 0) { left_x = 0; } | ||||||
|  | 
 | ||||||
|  |                     if (top_y < 0) { top_y = 0; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                     for (uint16_t i = left_x; i <= right_x; i++) { | ||||||
|  |                         for (uint16_t k = top_y; k <= bottom_y; k++) { | ||||||
|  |                             if (state->world_match_grid[i][k].ch) { | ||||||
|  |                                 Screen::paintTile(state->world_match_grid[i][k], i - left_x + 18, k - top_y + 2); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     for (uint8_t i = 0; i < 16; i++) { | ||||||
|  |                         for (uint8_t k = 0; k < 16; k++) { | ||||||
|  |                             if (state->region_match_grid[i][k].ch) { | ||||||
|  |                                 Screen::paintTile(state->region_match_grid[i][k], i + 1, k + 2); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  | /*                    //  Stuff for trying to replicate the DF right world map sizing logic. Close, but not there.
 | ||||||
|  |                     Screen::Pen pen(' ', COLOR_YELLOW); | ||||||
|  |                     //  Boundaries of the top level world map
 | ||||||
|  |                     Screen::paintString(pen, width / 2 - 5, 2, "X", false);  //  Marks UL corner of right world map. Constant
 | ||||||
|  | //                    Screen::paintString(pen, width - 30, 2, "X", false);     //  Marks UR corner of right world map area.
 | ||||||
|  | //                    Screen::paintString(pen, width / 2 - 5, height - 8, "X", false);  //  BL corner of right world map area.
 | ||||||
|  | //                    Screen::paintString(pen, width - 30, height - 8, "X", false);     //  BR corner of right world map area.
 | ||||||
|  |                      | ||||||
|  |                     uint16_t l_width = width - 30 - (width / 2 - 5) + 1;  //  Horizontal space available for right world map.
 | ||||||
|  |                     uint16_t l_height = height - 8 - 2 + 1;               //  Vertical space available for right world map.
 | ||||||
|  |                     df::coord2d size_factor_x = world_dimension_size(l_width, world->worldgen.worldgen_parms.dim_x); | ||||||
|  |                     df::coord2d size_factor_y = world_dimension_size(l_height, world->worldgen.worldgen_parms.dim_y); | ||||||
|  | 
 | ||||||
|  |                     Screen::paintString(pen, width / 2 - 5 + size_factor_x.x - 1, 2, "X", false); | ||||||
|  |                     Screen::paintString(pen, width / 2 - 5, 2 + size_factor_y.x - 1, "X", false); | ||||||
|  |                     Screen::paintString(pen, width / 2 - 5 + size_factor_x.x - 1, 2 + size_factor_y.x - 1, "X", false); | ||||||
|  |                     */ | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (state->matching) { | ||||||
|  |                     embark_assist::overlay::state->match_callback(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         IMPLEMENT_VMETHOD_INTERPOSE(embark_assist::overlay::ViewscreenOverlay, feed); | ||||||
|  |         IMPLEMENT_VMETHOD_INTERPOSE(embark_assist::overlay::ViewscreenOverlay, render); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | bool embark_assist::overlay::setup(DFHack::Plugin *plugin_self, | ||||||
|  |     embark_update_callbacks embark_update_callback, | ||||||
|  |     match_callbacks match_callback, | ||||||
|  |     clear_match_callbacks clear_match_callback, | ||||||
|  |     embark_assist::defs::find_callbacks find_callback, | ||||||
|  |     shutdown_callbacks shutdown_callback, | ||||||
|  |     uint16_t max_inorganic) | ||||||
|  | { | ||||||
|  | //    color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |     state = new(states); | ||||||
|  | 
 | ||||||
|  |     embark_assist::overlay::plugin_self = plugin_self; | ||||||
|  |     embark_assist::overlay::state->embark_update = embark_update_callback; | ||||||
|  |     embark_assist::overlay::state->match_callback = match_callback; | ||||||
|  |     embark_assist::overlay::state->clear_match_callback = clear_match_callback; | ||||||
|  |     embark_assist::overlay::state->find_callback = find_callback; | ||||||
|  |     embark_assist::overlay::state->shutdown_callback = shutdown_callback; | ||||||
|  |     embark_assist::overlay::state->max_inorganic = max_inorganic; | ||||||
|  |     embark_assist::overlay::state->match_active = false; | ||||||
|  | 
 | ||||||
|  |     state->world_match_grid = new pen_column[world->worldgen.worldgen_parms.dim_x]; | ||||||
|  |     if (!state->world_match_grid) { | ||||||
|  |         return false;  //  Out of memory
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { | ||||||
|  |         state->world_match_grid[i] = new Screen::Pen[world->worldgen.worldgen_parms.dim_y]; | ||||||
|  |         if (!state->world_match_grid[i]) {  //  Out of memory.
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     clear_match_results(); | ||||||
|  | 
 | ||||||
|  |     return INTERPOSE_HOOK(embark_assist::overlay::ViewscreenOverlay, feed).apply(true) && | ||||||
|  |         INTERPOSE_HOOK(embark_assist::overlay::ViewscreenOverlay, render).apply(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::set_sites(embark_assist::defs::site_lists *site_list) { | ||||||
|  |     for (uint8_t i = 0; i < 16; i++) { | ||||||
|  |         for (uint8_t k = 0; k < 16; k++) { | ||||||
|  |             state->site_grid[i][k] = empty_pen; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (uint16_t i = 0; i < site_list->size(); i++) { | ||||||
|  |         state->site_grid[site_list->at(i).x][site_list->at(i).y].ch = site_list->at(i).type; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::initiate_match() { | ||||||
|  |     embark_assist::overlay::state->matching = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::match_progress(uint16_t count, embark_assist::defs::match_results *match_results, bool done) { | ||||||
|  | //    color_ostream_proxy out(Core::getInstance().getConsole());
 | ||||||
|  |     state->matching = !done; | ||||||
|  |     state->match_count = count; | ||||||
|  |     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++) { | ||||||
|  |             if (match_results->at(i).at(k).preliminary_match) { | ||||||
|  |                 state->world_match_grid[i][k] = yellow_x_pen; | ||||||
|  | 
 | ||||||
|  |             } else if (match_results->at(i).at(k).contains_match) { | ||||||
|  |                 state->world_match_grid[i][k] = green_x_pen; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 state->world_match_grid[i][k] = empty_pen; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_info) { | ||||||
|  |     state->embark_info.clear(); | ||||||
|  | 
 | ||||||
|  |     if (site_info->sand) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_YELLOW), "Sand" }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (site_info->clay) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_RED), "Clay" }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Soil " + std::to_string(site_info->min_soil) + " - " + std::to_string(site_info->max_soil) }); | ||||||
|  | 
 | ||||||
|  |     if (site_info->flat) { | ||||||
|  |         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_BLUE), "Aquifer" }); | ||||||
|  |          | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Aquifer" }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (site_info->waterfall) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Waterfall" }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (site_info->flux) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), "Flux" }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (auto const& i : site_info->metals) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_GREY), world->raws.inorganics[i]->id }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (auto const& i : site_info->economics) { | ||||||
|  |         state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), world->raws.inorganics[i]->id }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::set_mid_level_tile_match(embark_assist::defs::mlt_matches mlt_matches) { | ||||||
|  |     for (uint8_t i = 0; i < 16; i++) { | ||||||
|  |         for (uint8_t k = 0; k < 16; k++) { | ||||||
|  |             if (mlt_matches[i][k]) { | ||||||
|  |                 state->region_match_grid[i][k] = green_x_pen; | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 state->region_match_grid[i][k] = empty_pen; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::clear_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++) { | ||||||
|  |             state->world_match_grid[i][k] = empty_pen; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (uint8_t i = 0; i < 16; i++) { | ||||||
|  |         for (uint8_t k = 0; k < 16; k++) { | ||||||
|  |             state->region_match_grid[i][k] = empty_pen; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //====================================================================
 | ||||||
|  | 
 | ||||||
|  | void embark_assist::overlay::shutdown() { | ||||||
|  |     if (state && | ||||||
|  |         state->world_match_grid) { | ||||||
|  |         INTERPOSE_HOOK(ViewscreenOverlay, render).remove(); | ||||||
|  |         INTERPOSE_HOOK(ViewscreenOverlay, feed).remove(); | ||||||
|  | 
 | ||||||
|  |         for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { | ||||||
|  |             delete[] state->world_match_grid[i]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         delete[] state->world_match_grid; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (state) { | ||||||
|  |         state->embark_info.clear(); | ||||||
|  |         delete state; | ||||||
|  |         state = nullptr; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,37 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <VTableInterpose.h> | ||||||
|  | #include "PluginManager.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | #include "df/viewscreen_choose_start_sitest.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | 
 | ||||||
|  | using df::global::enabler; | ||||||
|  | using df::global::gps; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace overlay { | ||||||
|  |         typedef void(*embark_update_callbacks)(); | ||||||
|  |         typedef void(*match_callbacks)(); | ||||||
|  |         typedef void(*clear_match_callbacks)(); | ||||||
|  |         typedef void(*shutdown_callbacks)(); | ||||||
|  | 
 | ||||||
|  |         bool setup(DFHack::Plugin *plugin_self, | ||||||
|  |             embark_update_callbacks embark_update_callback, | ||||||
|  |             match_callbacks match_callback, | ||||||
|  |             clear_match_callbacks clear_match_callback, | ||||||
|  |             embark_assist::defs::find_callbacks find_callback, | ||||||
|  |             shutdown_callbacks shutdown_callback, | ||||||
|  |             uint16_t max_inorganic); | ||||||
|  | 
 | ||||||
|  |         void set_sites(embark_assist::defs::site_lists *site_list); | ||||||
|  |         void initiate_match(); | ||||||
|  |         void match_progress(uint16_t count, embark_assist::defs::match_results *match_results, bool done); | ||||||
|  |         void set_embark(embark_assist::defs::site_infos *site_info); | ||||||
|  |         void set_mid_level_tile_match(embark_assist::defs::mlt_matches mlt_matches); | ||||||
|  |         void clear_match_results(); | ||||||
|  |         void shutdown(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,27 @@ | |||||||
|  | #include "screen.h" | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace screen { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool embark_assist::screen::paintString(const DFHack::Screen::Pen &pen, int x, int y, const std::string &text, bool map) { | ||||||
|  |     auto screen_size = DFHack::Screen::getWindowSize(); | ||||||
|  | 
 | ||||||
|  |     if (y < 1 || y + 1 >= screen_size.y || x < 1) | ||||||
|  |     { | ||||||
|  |         return false;  //  Won't paint outside of the screen or on the frame
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (x + text.length() - 1 < screen_size.x - 2) { | ||||||
|  |         DFHack::Screen::paintString(pen, x, y, text, map); | ||||||
|  |     } | ||||||
|  |     else if (x < screen_size.x - 2) { | ||||||
|  |         DFHack::Screen::paintString(pen, x, y, text.substr(0, screen_size.x - 2 - x + 1), map); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | #include "modules/Screen.h" | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace screen { | ||||||
|  |         bool paintString(const DFHack::Screen::Pen &pen, int x, int y, const std::string &text, bool map = false); | ||||||
|  |     } | ||||||
|  | } | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,38 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <map> | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | #include "df/coord2d.h" | ||||||
|  | 
 | ||||||
|  | #include "defs.h" | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | 
 | ||||||
|  | namespace embark_assist { | ||||||
|  |     namespace survey { | ||||||
|  |         void setup(uint16_t max_inorganic); | ||||||
|  | 
 | ||||||
|  |         df::coord2d get_last_pos(); | ||||||
|  | 
 | ||||||
|  |         void initiate(embark_assist::defs::mid_level_tiles *mlt); | ||||||
|  | 
 | ||||||
|  |         void clear_results(embark_assist::defs::match_results *match_results); | ||||||
|  | 
 | ||||||
|  |         void high_level_world_survey(embark_assist::defs::geo_data *geo_summary, | ||||||
|  |             embark_assist::defs::world_tile_data *survey_results); | ||||||
|  | 
 | ||||||
|  |         void 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); | ||||||
|  | 
 | ||||||
|  |         df::coord2d apply_offset(uint16_t x, uint16_t y, int8_t offset); | ||||||
|  | 
 | ||||||
|  |         void survey_region_sites(embark_assist::defs::site_lists *site_list); | ||||||
|  | 
 | ||||||
|  |         void survey_embark(embark_assist::defs::mid_level_tiles *mlt, | ||||||
|  |             embark_assist::defs::site_infos *site_info, | ||||||
|  |             bool use_cache); | ||||||
|  | 
 | ||||||
|  |         void shutdown(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,105 @@ | |||||||
|  | package AdventureControl; | ||||||
|  | 
 | ||||||
|  | //Attempts to provide a complete framework for reading everything from a fortress needed for vizualization | ||||||
|  | option optimize_for = LITE_RUNTIME; | ||||||
|  | 
 | ||||||
|  | import "RemoteFortressReader.proto"; | ||||||
|  | 
 | ||||||
|  | enum AdvmodeMenu | ||||||
|  | { | ||||||
|  |     Default = 0; | ||||||
|  |     Look = 1; | ||||||
|  |     ConversationAddress = 2; | ||||||
|  |     ConversationSelect = 3; | ||||||
|  |     ConversationSpeak = 4; | ||||||
|  |     Inventory = 5; | ||||||
|  |     Drop = 6; | ||||||
|  |     ThrowItem = 7; | ||||||
|  |     Wear = 8; | ||||||
|  |     Remove = 9; | ||||||
|  |     Interact = 10; | ||||||
|  |     Put = 11; | ||||||
|  |     PutContainer = 12; | ||||||
|  |     Eat = 13; | ||||||
|  |     ThrowAim = 14; | ||||||
|  |     Fire = 15; | ||||||
|  |     Get = 16; | ||||||
|  |     Unk17 = 17; | ||||||
|  |     CombatPrefs = 18; | ||||||
|  |     Companions = 19; | ||||||
|  |     MovementPrefs = 20; | ||||||
|  |     SpeedPrefs = 21; | ||||||
|  |     InteractAction = 22; | ||||||
|  |     MoveCarefully = 23; | ||||||
|  |     Announcements = 24; | ||||||
|  |     UseBuilding = 25; | ||||||
|  |     Travel = 26; | ||||||
|  |     Unk27 = 27; | ||||||
|  |     Unk28 = 28; | ||||||
|  |     SleepConfirm = 29; | ||||||
|  |     SelectInteractionTarget = 30; | ||||||
|  |     Unk31 = 31; | ||||||
|  |     Unk32 = 32; | ||||||
|  |     FallAction = 33; | ||||||
|  |     ViewTracks = 34; | ||||||
|  |     Jump = 35; | ||||||
|  |     Unk36 = 36; | ||||||
|  |     AttackConfirm = 37; | ||||||
|  |     AttackType = 38; | ||||||
|  |     AttackBodypart = 39; | ||||||
|  |     AttackStrike = 40; | ||||||
|  |     Unk41 = 41; | ||||||
|  |     Unk42 = 42; | ||||||
|  |     DodgeDirection = 43; | ||||||
|  |     Unk44 = 44; | ||||||
|  |     Unk45 = 45; | ||||||
|  |     Build = 46; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum CarefulMovementType | ||||||
|  | { | ||||||
|  |     DEFAULT_MOVEMENT = 0; | ||||||
|  |     RELEASE_ITEM_HOLD = 1; | ||||||
|  |     RELEASE_TILE_HOLD = 2; | ||||||
|  |     ATTACK_CREATURE = 3; | ||||||
|  |     HOLD_TILE = 4; | ||||||
|  |     MOVE = 5; | ||||||
|  |     CLIMB = 6; | ||||||
|  |     HOLD_ITEM = 7; | ||||||
|  |     BUILDING_INTERACT = 8; | ||||||
|  |     ITEM_INTERACT = 9; | ||||||
|  |     ITEM_INTERACT_GUIDE = 10; | ||||||
|  |     ITEM_INTERACT_RIDE = 11; | ||||||
|  |     ITEM_INTERACT_PUSH = 12; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum MiscMoveType | ||||||
|  | { | ||||||
|  |     SET_CLIMB = 0; | ||||||
|  |     SET_STAND = 1; | ||||||
|  |     SET_CANCEL = 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | message MoveCommandParams | ||||||
|  | { | ||||||
|  |     optional RemoteFortressReader.Coord direction = 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | message MovementOption | ||||||
|  | { | ||||||
|  |     optional RemoteFortressReader.Coord dest = 1; | ||||||
|  |     optional RemoteFortressReader.Coord source = 2; | ||||||
|  |     optional RemoteFortressReader.Coord grab = 3; | ||||||
|  |     optional CarefulMovementType movement_type = 4; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | message MenuContents | ||||||
|  | { | ||||||
|  |     optional AdvmodeMenu current_menu = 1; | ||||||
|  |     repeated MovementOption movements = 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | message MiscMoveParams | ||||||
|  | { | ||||||
|  |     optional MiscMoveType type = 1; | ||||||
|  | } | ||||||
| @ -0,0 +1,317 @@ | |||||||
|  | #include "adventure_control.h" | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | #include "df/adventure_movement_attack_creaturest.h" | ||||||
|  | #include "df/adventure_movement_building_interactst.h" | ||||||
|  | #include "df/adventure_movement_climbst.h" | ||||||
|  | #include "df/adventure_movement_hold_itemst.h" | ||||||
|  | #include "df/adventure_movement_hold_tilest.h" | ||||||
|  | #include "df/adventure_movement_optionst.h" | ||||||
|  | #include "df/ui_advmode.h" | ||||||
|  | #include "df/viewscreen.h" | ||||||
|  | 
 | ||||||
|  | #include "modules/Gui.h" | ||||||
|  | 
 | ||||||
|  | #include <queue> | ||||||
|  | 
 | ||||||
|  | using namespace AdventureControl; | ||||||
|  | using namespace df::enums; | ||||||
|  | using namespace DFHack; | ||||||
|  | using namespace Gui; | ||||||
|  | 
 | ||||||
|  | std::queue<interface_key::interface_key> keyQueue; | ||||||
|  | 
 | ||||||
|  | void KeyUpdate() | ||||||
|  | { | ||||||
|  |     if (!keyQueue.empty()) | ||||||
|  |     { | ||||||
|  |         getCurViewscreen()->feed_key(keyQueue.front()); | ||||||
|  |         keyQueue.pop(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SetCoord(df::coord in, RemoteFortressReader::Coord *out) | ||||||
|  | { | ||||||
|  |     out->set_x(in.x); | ||||||
|  |     out->set_y(in.y); | ||||||
|  |     out->set_z(in.z); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) | ||||||
|  | { | ||||||
|  |     auto viewScreen = getCurViewscreen(); | ||||||
|  |     if (!in->has_direction()) | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) | ||||||
|  |         return CR_OK; | ||||||
|  |     auto dir = in->direction(); | ||||||
|  |     switch (dir.x()) | ||||||
|  |     { | ||||||
|  |     case -1: | ||||||
|  |         switch (dir.y()) | ||||||
|  |         { | ||||||
|  |         case -1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NW_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NW); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NW_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 0: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_W_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_W); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_W_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SW_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SW); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SW_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         switch (dir.y()) | ||||||
|  |         { | ||||||
|  |         case -1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_N_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_N); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_N_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 0: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_S_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_S); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_S_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 1: | ||||||
|  |         switch (dir.y()) | ||||||
|  |         { | ||||||
|  |         case -1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NE_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NE); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_NE_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 0: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_E_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_E); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_E_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 1: | ||||||
|  |             switch (dir.z()) | ||||||
|  |             { | ||||||
|  |             case -1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SE_DOWN); | ||||||
|  |                 break; | ||||||
|  |             case 0: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SE); | ||||||
|  |                 break; | ||||||
|  |             case 1: | ||||||
|  |                 viewScreen->feed_key(interface_key::A_MOVE_SE_UP); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) | ||||||
|  | { | ||||||
|  |     if (!in->has_direction()) | ||||||
|  |         return CR_WRONG_USAGE; | ||||||
|  |     if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) | ||||||
|  |         return CR_OK; | ||||||
|  |     auto dir = in->direction(); | ||||||
|  |     keyQueue.push(interface_key::A_JUMP); | ||||||
|  |     int x = dir.x(); | ||||||
|  |     int y = dir.y(); | ||||||
|  |     if (x > 0) | ||||||
|  |     { | ||||||
|  |         for (int i = 0; i < x; i++) | ||||||
|  |         { | ||||||
|  |             keyQueue.push(interface_key::CURSOR_RIGHT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (x < 0) | ||||||
|  |     { | ||||||
|  |         for (int i = 0; i > x; i--) | ||||||
|  |         { | ||||||
|  |             keyQueue.push(interface_key::CURSOR_LEFT); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (y > 0) | ||||||
|  |     { | ||||||
|  |         for (int i = 0; i < y; i++) | ||||||
|  |         { | ||||||
|  |             keyQueue.push(interface_key::CURSOR_DOWN); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (y < 0) | ||||||
|  |     { | ||||||
|  |         for (int i = 0; i > y; i--) | ||||||
|  |         { | ||||||
|  |             keyQueue.push(interface_key::CURSOR_UP); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     keyQueue.push(interface_key::SELECT); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, MenuContents *out) | ||||||
|  | { | ||||||
|  |     auto advUi = df::global::ui_advmode; | ||||||
|  | 
 | ||||||
|  |     if (advUi == NULL) | ||||||
|  |         return CR_FAILURE; | ||||||
|  | 
 | ||||||
|  |     out->set_current_menu((AdvmodeMenu)advUi->menu); | ||||||
|  | 
 | ||||||
|  |     //Fixme: Needs a proper way to control it, but for now, this is the only way to allow Armok Vision to keep going without the user needing to switch to DF.
 | ||||||
|  |     if (advUi->menu == ui_advmode_menu::FallAction) | ||||||
|  |     { | ||||||
|  |         getCurViewscreen()->feed_key(interface_key::OPTION1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch (advUi->menu) | ||||||
|  |     { | ||||||
|  |     case ui_advmode_menu::MoveCarefully: | ||||||
|  |         for (size_t i = 0; i < advUi->movements.size(); i++) | ||||||
|  |         { | ||||||
|  |             auto movement = advUi->movements[i]; | ||||||
|  |             auto send_movement = out->add_movements(); | ||||||
|  |             SetCoord(movement->source, send_movement->mutable_source()); | ||||||
|  |             SetCoord(movement->dest, send_movement->mutable_dest()); | ||||||
|  | 
 | ||||||
|  |             STRICT_VIRTUAL_CAST_VAR(climbMovement, df::adventure_movement_climbst, movement); | ||||||
|  |             if (climbMovement) | ||||||
|  |             { | ||||||
|  |                 SetCoord(climbMovement->grab, send_movement->mutable_grab()); | ||||||
|  |                 send_movement->set_movement_type(CarefulMovementType::CLIMB); | ||||||
|  |             } | ||||||
|  |             STRICT_VIRTUAL_CAST_VAR(holdTileMovement, df::adventure_movement_hold_tilest, movement); | ||||||
|  |             if (holdTileMovement) | ||||||
|  |             { | ||||||
|  |                 SetCoord(holdTileMovement->grab, send_movement->mutable_grab()); | ||||||
|  |                 send_movement->set_movement_type(CarefulMovementType::HOLD_TILE); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in) | ||||||
|  | { | ||||||
|  |     if (!(df::global::ui_advmode->menu == ui_advmode_menu::MoveCarefully)) | ||||||
|  |         return CR_OK; | ||||||
|  |     int choice = in->value(); | ||||||
|  |     int page = choice / 5; | ||||||
|  |     int select = choice % 5; | ||||||
|  |     for (int i = 0; i < page; i++) | ||||||
|  |     { | ||||||
|  |         keyQueue.push(interface_key::SECONDSCROLL_PAGEDOWN); | ||||||
|  |     } | ||||||
|  |     keyQueue.push((interface_key::interface_key)(interface_key::OPTION1 + select)); | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMoveParams *in) | ||||||
|  | { | ||||||
|  |     if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) | ||||||
|  |         return CR_OK; | ||||||
|  | 
 | ||||||
|  |     auto type = in->type(); | ||||||
|  | 
 | ||||||
|  |     switch (type) | ||||||
|  |     { | ||||||
|  |     case AdventureControl::SET_CLIMB: | ||||||
|  |         getCurViewscreen()->feed_key(interface_key::A_HOLD); | ||||||
|  |         break; | ||||||
|  |     case AdventureControl::SET_STAND: | ||||||
|  |         getCurViewscreen()->feed_key(interface_key::A_STANCE); | ||||||
|  |         break; | ||||||
|  |     case AdventureControl::SET_CANCEL: | ||||||
|  |         getCurViewscreen()->feed_key(interface_key::LEAVESCREEN); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | #ifndef ADVENTURE_CONTROL_H | ||||||
|  | #define ADVENTURE_CONTROL_H | ||||||
|  | 
 | ||||||
|  | #include "RemoteClient.h" | ||||||
|  | #include "AdventureControl.pb.h" | ||||||
|  | 
 | ||||||
|  | DFHack::command_result MoveCommand(DFHack::color_ostream &stream, const AdventureControl::MoveCommandParams *in); | ||||||
|  | DFHack::command_result JumpCommand(DFHack::color_ostream &stream, const AdventureControl::MoveCommandParams *in); | ||||||
|  | DFHack::command_result MenuQuery(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, AdventureControl::MenuContents *out); | ||||||
|  | DFHack::command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in); | ||||||
|  | DFHack::command_result MiscMoveCommand(DFHack::color_ostream &stream, const AdventureControl::MiscMoveParams *in); | ||||||
|  | 
 | ||||||
|  | void KeyUpdate(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif // !ADVENTURE_CONTROL_H
 | ||||||
| @ -1,2 +1,2 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #define DF_VERSION_INT 43005 | #define DF_VERSION_INT 44002 | ||||||
|  | |||||||
| @ -0,0 +1,551 @@ | |||||||
|  | #include "item_reader.h" | ||||||
|  | #include "Core.h" | ||||||
|  | #include "VersionInfo.h" | ||||||
|  | 
 | ||||||
|  | #include "df/art_image.h" | ||||||
|  | #include "df/art_image_chunk.h" | ||||||
|  | #include "df/art_image_element.h" | ||||||
|  | #include "df/art_image_element_creaturest.h" | ||||||
|  | #include "df/art_image_element_itemst.h" | ||||||
|  | #include "df/art_image_element_plantst.h" | ||||||
|  | #include "df/art_image_element_shapest.h" | ||||||
|  | #include "df/art_image_element_treest.h" | ||||||
|  | #include "df/art_image_element_type.h" | ||||||
|  | #include "df/art_image_property.h" | ||||||
|  | #include "df/art_image_property_intransitive_verbst.h" | ||||||
|  | #include "df/art_image_property_transitive_verbst.h" | ||||||
|  | #include "df/art_image_ref.h" | ||||||
|  | #include "df/descriptor_shape.h" | ||||||
|  | #include "df/item_type.h" | ||||||
|  | #include "df/item_constructed.h" | ||||||
|  | #include "df/item_gemst.h" | ||||||
|  | #include "df/item_smallgemst.h" | ||||||
|  | #include "df/item_statuest.h" | ||||||
|  | #include "df/item_threadst.h" | ||||||
|  | #include "df/item_toolst.h" | ||||||
|  | #include "df/itemimprovement.h" | ||||||
|  | #include "df/itemimprovement_art_imagest.h" | ||||||
|  | #include "df/itemimprovement_bandsst.h" | ||||||
|  | #include "df/itemimprovement_coveredst.h" | ||||||
|  | #include "df/itemimprovement_illustrationst.h" | ||||||
|  | #include "df/itemimprovement_itemspecificst.h" | ||||||
|  | #include "df/itemimprovement_sewn_imagest.h" | ||||||
|  | #include "df/itemimprovement_specific_type.h" | ||||||
|  | #include "df/itemimprovement_threadst.h" | ||||||
|  | #include "df/itemdef.h" | ||||||
|  | #include "df/map_block.h" | ||||||
|  | #include "df/vehicle.h" | ||||||
|  | #include "df/world.h" | ||||||
|  | 
 | ||||||
|  | #include "modules/Items.h" | ||||||
|  | #include "modules/MapCache.h" | ||||||
|  | #include "modules/Materials.h" | ||||||
|  | #include "MiscUtils.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using namespace DFHack; | ||||||
|  | using namespace df::enums; | ||||||
|  | using namespace RemoteFortressReader; | ||||||
|  | using namespace std; | ||||||
|  | using namespace df::global; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void CopyImage(const df::art_image * image, ArtImage * netImage) | ||||||
|  | { | ||||||
|  |     auto id = netImage->mutable_id(); | ||||||
|  |     id->set_mat_type(image->id); | ||||||
|  |     id->set_mat_index(image->subid); | ||||||
|  |     for (int i = 0; i < image->elements.size(); i++) | ||||||
|  |     { | ||||||
|  |         auto element = image->elements[i]; | ||||||
|  |         auto netElement = netImage->add_elements(); | ||||||
|  |         auto elementType = element->getType(); | ||||||
|  |         netElement->set_type((ArtImageElementType)elementType); | ||||||
|  | 
 | ||||||
|  |         netElement->set_count(element->count); | ||||||
|  | 
 | ||||||
|  |         switch (elementType) | ||||||
|  |         { | ||||||
|  |         case df::enums::art_image_element_type::CREATURE: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(creature, df::art_image_element_creaturest, element); | ||||||
|  |             auto cret = netElement->mutable_creature_item(); | ||||||
|  |             cret->set_mat_type(creature->race); | ||||||
|  |             cret->set_mat_index(creature->caste); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::art_image_element_type::PLANT: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(plant, df::art_image_element_plantst, element); | ||||||
|  |             netElement->set_id(plant->plant_id); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::art_image_element_type::TREE: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(tree, df::art_image_element_treest, element); | ||||||
|  |             netElement->set_id(tree->plant_id); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::art_image_element_type::SHAPE: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(shape, df::art_image_element_shapest, element); | ||||||
|  |             netElement->set_id(shape->shape_id); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::art_image_element_type::ITEM: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(item, df::art_image_element_itemst, element); | ||||||
|  |             auto it = netElement->mutable_creature_item(); | ||||||
|  |             it->set_mat_type(item->item_type); | ||||||
|  |             it->set_mat_index(item->item_subtype); | ||||||
|  |             netElement->set_id(item->item_id); | ||||||
|  |             auto mat = netElement->mutable_material(); | ||||||
|  |             mat->set_mat_type(item->mat_type); | ||||||
|  |             mat->set_mat_index(item->mat_index); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     for (int i = 0; i < image->properties.size(); i++) | ||||||
|  |     { | ||||||
|  |         auto dfProperty = image->properties[i]; | ||||||
|  |         auto netProperty = netImage->add_properties(); | ||||||
|  |         auto propertyType = dfProperty->getType(); | ||||||
|  | 
 | ||||||
|  |         netProperty->set_type((ArtImagePropertyType)propertyType); | ||||||
|  | 
 | ||||||
|  |         switch (propertyType) | ||||||
|  |         { | ||||||
|  |         case df::enums::art_image_property_type::transitive_verb: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(transitive, df::art_image_property_transitive_verbst, dfProperty); | ||||||
|  |             netProperty->set_subject(transitive->subject); | ||||||
|  |             netProperty->set_object(transitive->object); | ||||||
|  |             netProperty->set_verb((ArtImageVerb)transitive->verb); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::art_image_property_type::intransitive_verb: | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(intransitive, df::art_image_property_intransitive_verbst, dfProperty); | ||||||
|  |             netProperty->set_subject(intransitive->subject); | ||||||
|  |             netProperty->set_verb((ArtImageVerb)intransitive->verb); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CopyItem(RemoteFortressReader::Item * NetItem, df::item * DfItem) | ||||||
|  | { | ||||||
|  |     NetItem->set_id(DfItem->id); | ||||||
|  |     NetItem->set_flags1(DfItem->flags.whole); | ||||||
|  |     NetItem->set_flags2(DfItem->flags2.whole); | ||||||
|  |     auto pos = NetItem->mutable_pos(); | ||||||
|  |     pos->set_x(DfItem->pos.x); | ||||||
|  |     pos->set_y(DfItem->pos.y); | ||||||
|  |     pos->set_z(DfItem->pos.z); | ||||||
|  |     auto mat = NetItem->mutable_material(); | ||||||
|  |     mat->set_mat_index(DfItem->getMaterialIndex()); | ||||||
|  |     mat->set_mat_type(DfItem->getMaterial()); | ||||||
|  |     auto type = NetItem->mutable_type(); | ||||||
|  |     type->set_mat_type(DfItem->getType()); | ||||||
|  |     type->set_mat_index(DfItem->getSubtype()); | ||||||
|  | 
 | ||||||
|  |     bool isProjectile = false; | ||||||
|  | 
 | ||||||
|  |     item_type::item_type itemType = DfItem->getType(); | ||||||
|  | 
 | ||||||
|  |     switch (itemType) | ||||||
|  |     { | ||||||
|  |     case df::enums::item_type::NONE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BAR: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SMALLGEM: | ||||||
|  |     { | ||||||
|  |         VIRTUAL_CAST_VAR(smallgem_item, df::item_smallgemst, DfItem); | ||||||
|  |         type->set_mat_index(smallgem_item->shape); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case df::enums::item_type::BLOCKS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ROUGH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BOULDER: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::WOOD: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::DOOR: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FLOODGATE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BED: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CHAIR: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CHAIN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FLASK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::GOBLET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::INSTRUMENT: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TOY: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::WINDOW: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CAGE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BARREL: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BUCKET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ANIMALTRAP: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TABLE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::COFFIN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::STATUE: | ||||||
|  |     { | ||||||
|  |         VIRTUAL_CAST_VAR(statue, df::item_statuest, DfItem); | ||||||
|  |          | ||||||
|  |         df::art_image_chunk * chunk = NULL; | ||||||
|  |         GET_ART_IMAGE_CHUNK GetArtImageChunk = reinterpret_cast<GET_ART_IMAGE_CHUNK>(Core::getInstance().vinfo->getAddress("get_art_image_chunk")); | ||||||
|  |         if (GetArtImageChunk) | ||||||
|  |         { | ||||||
|  |             chunk = GetArtImageChunk(&(world->art_image_chunks), statue->image.id); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             for (int i = 0; i < world->art_image_chunks.size(); i++) | ||||||
|  |             { | ||||||
|  |                 if (world->art_image_chunks[i]->id == statue->image.id) | ||||||
|  |                     chunk = world->art_image_chunks[i]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (chunk) | ||||||
|  |         { | ||||||
|  |             CopyImage(chunk->images[statue->image.subid], NetItem->mutable_image()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case df::enums::item_type::CORPSE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::WEAPON: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ARMOR: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SHOES: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SHIELD: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::HELM: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::GLOVES: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BOX: | ||||||
|  |         type->set_mat_index(DfItem->isBag()); | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BIN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ARMORSTAND: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::WEAPONRACK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CABINET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FIGURINE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::AMULET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SCEPTER: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::AMMO: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CROWN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::RING: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::EARRING: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BRACELET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::GEM: | ||||||
|  |     { | ||||||
|  |         VIRTUAL_CAST_VAR(gem_item, df::item_gemst, DfItem); | ||||||
|  |         type->set_mat_index(gem_item->shape); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case df::enums::item_type::ANVIL: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CORPSEPIECE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::REMAINS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::MEAT: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FISH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FISH_RAW: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::VERMIN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::PET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SEEDS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::PLANT: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SKIN_TANNED: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::PLANT_GROWTH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::THREAD: | ||||||
|  |     { | ||||||
|  |         VIRTUAL_CAST_VAR(thread, df::item_threadst, DfItem); | ||||||
|  |         if (thread && thread->dye_mat_type >= 0) | ||||||
|  |         { | ||||||
|  |             DFHack::MaterialInfo info; | ||||||
|  |             if (info.decode(thread->dye_mat_type, thread->dye_mat_index)) | ||||||
|  |                 ConvertDFColorDescriptor(info.material->powder_dye, NetItem->mutable_dye()); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case df::enums::item_type::CLOTH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TOTEM: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::PANTS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BACKPACK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::QUIVER: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CATAPULTPARTS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BALLISTAPARTS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SIEGEAMMO: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BALLISTAARROWHEAD: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TRAPPARTS: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TRAPCOMP: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::DRINK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::POWDER_MISC: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CHEESE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::FOOD: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::LIQUID_MISC: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::COIN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::GLOB: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ROCK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::PIPE_SECTION: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::HATCH_COVER: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::GRATE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::QUERN: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::MILLSTONE: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SPLINT: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::CRUTCH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TRACTION_BENCH: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::ORTHOPEDIC_CAST: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::TOOL: | ||||||
|  |         if (!isProjectile) | ||||||
|  |         { | ||||||
|  |             VIRTUAL_CAST_VAR(tool, df::item_toolst, DfItem); | ||||||
|  |             if (tool) | ||||||
|  |             { | ||||||
|  |                 auto vehicle = binsearch_in_vector(world->vehicles.active, tool->vehicle_id); | ||||||
|  |                 if (vehicle) | ||||||
|  |                 { | ||||||
|  |                     NetItem->set_subpos_x(vehicle->offset_x / 100000.0); | ||||||
|  |                     NetItem->set_subpos_y(vehicle->offset_y / 100000.0); | ||||||
|  |                     NetItem->set_subpos_z(vehicle->offset_z / 140000.0); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SLAB: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::EGG: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BOOK: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::SHEET: | ||||||
|  |         break; | ||||||
|  |     case df::enums::item_type::BRANCH: | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     VIRTUAL_CAST_VAR(actual_item, df::item_actual, DfItem); | ||||||
|  |     if (actual_item) | ||||||
|  |     { | ||||||
|  |         NetItem->set_stack_size(actual_item->stack_size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     VIRTUAL_CAST_VAR(constructed_item, df::item_constructed, DfItem); | ||||||
|  |     if (constructed_item) | ||||||
|  |     { | ||||||
|  |         for (int i = 0; i < constructed_item->improvements.size(); i++) | ||||||
|  |         { | ||||||
|  |             auto improvement = constructed_item->improvements[i]; | ||||||
|  | 
 | ||||||
|  |             if (!improvement) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             improvement_type::improvement_type impType = improvement->getType(); | ||||||
|  | 
 | ||||||
|  |             auto netImp = NetItem->add_improvements(); | ||||||
|  | 
 | ||||||
|  |             netImp->set_type((ImprovementType)impType); | ||||||
|  | 
 | ||||||
|  |             auto mat = netImp->mutable_material(); | ||||||
|  |             mat->set_mat_type(improvement->mat_type); | ||||||
|  |             mat->set_mat_index(improvement->mat_index); | ||||||
|  | 
 | ||||||
|  |             switch (impType) | ||||||
|  |             { | ||||||
|  |             case df::enums::improvement_type::ART_IMAGE: | ||||||
|  |             { | ||||||
|  |                 VIRTUAL_CAST_VAR(artImage, df::itemimprovement_art_imagest, improvement); | ||||||
|  |                 CopyImage(artImage->getImage(DfItem), netImp->mutable_image()); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case df::enums::improvement_type::COVERED: | ||||||
|  |             { | ||||||
|  |                 VIRTUAL_CAST_VAR(covered, df::itemimprovement_coveredst, improvement); | ||||||
|  |                 netImp->set_shape(covered->shape); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case df::enums::improvement_type::RINGS_HANGING: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::BANDS: | ||||||
|  |             { | ||||||
|  |                 VIRTUAL_CAST_VAR(bands, df::itemimprovement_bandsst, improvement); | ||||||
|  |                 netImp->set_shape(bands->shape); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case df::enums::improvement_type::ITEMSPECIFIC: | ||||||
|  |             { | ||||||
|  |                 VIRTUAL_CAST_VAR(specific, df::itemimprovement_itemspecificst, improvement); | ||||||
|  |                 netImp->set_specific_type(specific->type); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case df::enums::improvement_type::THREAD: | ||||||
|  |             { | ||||||
|  |                 VIRTUAL_CAST_VAR(improvement_thread, df::itemimprovement_threadst, improvement); | ||||||
|  |                 if (improvement_thread->dye.mat_type >= 0) | ||||||
|  |                 { | ||||||
|  |                     DFHack::MaterialInfo info; | ||||||
|  |                     if (!info.decode(improvement_thread->dye.mat_type, improvement_thread->dye.mat_index)) | ||||||
|  |                         continue; | ||||||
|  |                     ConvertDFColorDescriptor(info.material->powder_dye, NetItem->mutable_dye()); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case df::enums::improvement_type::CLOTH: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::SEWN_IMAGE: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::PAGES: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::ILLUSTRATION: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::INSTRUMENT_PIECE: | ||||||
|  |                 break; | ||||||
|  |             case df::enums::improvement_type::WRITING: | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     NetItem->set_volume(DfItem->getVolume()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::EmptyMessage *in, RemoteFortressReader::MaterialList *out) | ||||||
|  | { | ||||||
|  |     if (!Core::getInstance().isWorldLoaded()) { | ||||||
|  |         //out->set_available(false);
 | ||||||
|  |         return CR_OK; | ||||||
|  |     } | ||||||
|  |     FOR_ENUM_ITEMS(item_type, it) | ||||||
|  |     { | ||||||
|  |         MaterialDefinition *mat_def = out->add_material_list(); | ||||||
|  |         mat_def->mutable_mat_pair()->set_mat_type((int)it); | ||||||
|  |         mat_def->mutable_mat_pair()->set_mat_index(-1); | ||||||
|  |         mat_def->set_id(ENUM_KEY_STR(item_type, it)); | ||||||
|  |         switch (it) | ||||||
|  |         { | ||||||
|  |         case df::enums::item_type::GEM: | ||||||
|  |         case df::enums::item_type::SMALLGEM: | ||||||
|  |         { | ||||||
|  |             for (int i = 0; i < world->raws.language.shapes.size(); i++) | ||||||
|  |             { | ||||||
|  |                 auto shape = world->raws.language.shapes[i]; | ||||||
|  |                 if (shape->gems_use.whole == 0) | ||||||
|  |                     continue; | ||||||
|  |                 mat_def = out->add_material_list(); | ||||||
|  |                 mat_def->mutable_mat_pair()->set_mat_type((int)it); | ||||||
|  |                 mat_def->mutable_mat_pair()->set_mat_index(i); | ||||||
|  |                 mat_def->set_id(ENUM_KEY_STR(item_type, it) + "/" + shape->id); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case df::enums::item_type::BOX: | ||||||
|  |         { | ||||||
|  |             mat_def = out->add_material_list(); | ||||||
|  |             mat_def->mutable_mat_pair()->set_mat_type((int)it); | ||||||
|  |             mat_def->mutable_mat_pair()->set_mat_index(0); | ||||||
|  |             mat_def->set_id("BOX_CHEST"); | ||||||
|  |             mat_def = out->add_material_list(); | ||||||
|  |             mat_def->mutable_mat_pair()->set_mat_type((int)it); | ||||||
|  |             mat_def->mutable_mat_pair()->set_mat_index(1); | ||||||
|  |             mat_def->set_id("BOX_BAG"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         } | ||||||
|  |         int subtypes = Items::getSubtypeCount(it); | ||||||
|  |         if (subtypes >= 0) | ||||||
|  |         { | ||||||
|  |             for (int i = 0; i < subtypes; i++) | ||||||
|  |             { | ||||||
|  |                 mat_def = out->add_material_list(); | ||||||
|  |                 mat_def->mutable_mat_pair()->set_mat_type((int)it); | ||||||
|  |                 mat_def->mutable_mat_pair()->set_mat_index(i); | ||||||
|  |                 df::itemdef * item = Items::getSubtypeDef(it, i); | ||||||
|  |                 mat_def->set_id(ENUM_KEY_STR(item_type, it) + "/" + item->id); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return CR_OK; | ||||||
|  | } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | #ifndef ITEM_READER_H | ||||||
|  | #define ITEM_READER_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "RemoteClient.h" | ||||||
|  | #include "RemoteFortressReader.pb.h" | ||||||
|  | 
 | ||||||
|  | #include "DataDefs.h" | ||||||
|  | 
 | ||||||
|  | namespace df | ||||||
|  | { | ||||||
|  |     struct item; | ||||||
|  |     struct map_block; | ||||||
|  |     struct art_image; | ||||||
|  |     struct art_image_chunk; | ||||||
|  |     struct world; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace MapExtras | ||||||
|  | { | ||||||
|  |     class MapCache; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DFHack::command_result GetItemList(DFHack::color_ostream &stream, const DFHack::EmptyMessage *in, RemoteFortressReader::MaterialList *out); | ||||||
|  | void CopyItem(RemoteFortressReader::Item * NetItem, df::item * DfItem); | ||||||
|  | void ConvertDFColorDescriptor(int16_t index, RemoteFortressReader::ColorDefinition * out); | ||||||
|  | 
 | ||||||
|  | typedef df::art_image_chunk * (*GET_ART_IMAGE_CHUNK)(std::vector<df::art_image_chunk* > *, int); | ||||||
|  | 
 | ||||||
|  | void CopyImage(const df::art_image * image, RemoteFortressReader::ArtImage * netImage); | ||||||
|  | 
 | ||||||
|  | #endif // !ITEM_READER_H
 | ||||||
| @ -1,68 +0,0 @@ | |||||||
| using namespace DFHack; |  | ||||||
| using namespace df::enums; |  | ||||||
| 
 |  | ||||||
| using df::global::ui_sidebar_menus; |  | ||||||
| using df::global::ui_workshop_in_add; |  | ||||||
| 
 |  | ||||||
| static df::interface_key kitchen_bindings[] = { |  | ||||||
|     df::interface_key::HOTKEY_KITCHEN_COOK_2, |  | ||||||
|     df::interface_key::HOTKEY_KITCHEN_COOK_3, |  | ||||||
|     df::interface_key::HOTKEY_KITCHEN_COOK_4, |  | ||||||
|     // DF uses CUSTOM_R for this reaction in the raws, so this key is recognized
 |  | ||||||
|     // by this tweak but not displayed
 |  | ||||||
|     df::interface_key::HOTKEY_KITCHEN_RENDER_FAT |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct kitchen_keys_hook : df::viewscreen_dwarfmodest { |  | ||||||
|     typedef df::viewscreen_dwarfmodest interpose_base; |  | ||||||
| 
 |  | ||||||
|     void draw_binding (int row, df::interface_key key) |  | ||||||
|     { |  | ||||||
|         std::string label = Screen::getKeyDisplay(key); |  | ||||||
|         int x = Gui::getDwarfmodeViewDims().menu_x2 - 2 - label.size(); |  | ||||||
|         int y = row + 4; |  | ||||||
|         OutputString(COLOR_GREY, x, y, "("); |  | ||||||
|         OutputString(COLOR_LIGHTRED, x, y, label); |  | ||||||
|         OutputString(COLOR_GREY, x, y, ")"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool kitchen_in_add() |  | ||||||
|     { |  | ||||||
|         if (!*ui_workshop_in_add) |  | ||||||
|             return false; |  | ||||||
|         df::building_workshopst *ws = virtual_cast<df::building_workshopst>(world->selected_building); |  | ||||||
|         if (!ws) |  | ||||||
|             return false; |  | ||||||
|         if (ws->type != workshop_type::Kitchen) |  | ||||||
|             return false; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input)) |  | ||||||
|     { |  | ||||||
|         if (kitchen_in_add()) |  | ||||||
|         { |  | ||||||
|             for (int i = 0; i < 4; i++) |  | ||||||
|             { |  | ||||||
|                 if (input->count(kitchen_bindings[i])) |  | ||||||
|                 { |  | ||||||
|                     ui_sidebar_menus->workshop_job.cursor = i; |  | ||||||
|                     input->clear(); |  | ||||||
|                     input->insert(df::interface_key::SELECT); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         INTERPOSE_NEXT(feed)(input); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     DEFINE_VMETHOD_INTERPOSE(void, render, ()) |  | ||||||
|     { |  | ||||||
|         INTERPOSE_NEXT(render)(); |  | ||||||
|         if (kitchen_in_add()) |  | ||||||
|             for (int i = 0; i < 3; i++) |  | ||||||
|                 draw_binding(i, kitchen_bindings[i]); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| IMPLEMENT_VMETHOD_INTERPOSE(kitchen_keys_hook, feed); |  | ||||||
| IMPLEMENT_VMETHOD_INTERPOSE(kitchen_keys_hook, render); |  | ||||||
| @ -0,0 +1,137 @@ | |||||||
|  | #include "df/global_objects.h" | ||||||
|  | #include "modules/Gui.h" | ||||||
|  | #include "modules/Screen.h" | ||||||
|  | #include "df/enabler.h" | ||||||
|  | #include "df/viewscreen_dwarfmodest.h" | ||||||
|  | #include "df/viewscreen_titlest.h" | ||||||
|  | 
 | ||||||
|  | struct dwarfmode_pausing_fps_counter_hook : df::viewscreen_dwarfmodest { | ||||||
|  |     typedef df::viewscreen_dwarfmodest interpose_base; | ||||||
|  | 
 | ||||||
|  |     static const uint32_t history_length = 3; | ||||||
|  | 
 | ||||||
|  |     // whether init.txt have [FPS:YES]
 | ||||||
|  |     static bool init_have_fps_yes() | ||||||
|  |     { | ||||||
|  |         static bool first = true; | ||||||
|  |         static bool init_have_fps_yes; | ||||||
|  | 
 | ||||||
|  |         if (first && df::global::gps) | ||||||
|  |         { | ||||||
|  |             // if first time called, then display_frames is set iff init.txt have [FPS:YES]
 | ||||||
|  |             first = false; | ||||||
|  |             init_have_fps_yes = (df::global::gps->display_frames == 1); | ||||||
|  |         } | ||||||
|  |         return init_have_fps_yes; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     DEFINE_VMETHOD_INTERPOSE(void, render, ()) | ||||||
|  |     { | ||||||
|  |         INTERPOSE_NEXT(render)(); | ||||||
|  | 
 | ||||||
|  |         if (!df::global::pause_state || !df::global::enabler || !df::global::world | ||||||
|  |             || !df::global::gps || !df::global::pause_state) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         // if init.txt does not have [FPS:YES] then dont show this FPS counter
 | ||||||
|  |         if (!dwarfmode_pausing_fps_counter_hook::init_have_fps_yes()) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         static bool prev_paused = true; | ||||||
|  |         static uint32_t prev_clock = 0; | ||||||
|  |         static int32_t prev_frames = 0; | ||||||
|  |         static uint32_t elapsed_clock = 0; | ||||||
|  |         static uint32_t elapsed_frames = 0; | ||||||
|  |         static double history[history_length]; | ||||||
|  | 
 | ||||||
|  |         if (prev_clock == 0) | ||||||
|  |         { | ||||||
|  |             // init
 | ||||||
|  |             for (int i = 0; i < history_length; i++) | ||||||
|  |                 history[i] = 0.0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // disable default FPS counter because it is rendered on top of this FPS counter.
 | ||||||
|  |         if (df::global::gps->display_frames == 1) | ||||||
|  |             df::global::gps->display_frames = 0; | ||||||
|  | 
 | ||||||
|  |         if (*df::global::pause_state) | ||||||
|  |             prev_paused = true; | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             uint32_t clock = df::global::enabler->clock; | ||||||
|  |             int32_t frames = df::global::world->frame_counter; | ||||||
|  | 
 | ||||||
|  |             if (!prev_paused && prev_clock != 0 | ||||||
|  |                 && clock >= prev_clock && frames >= prev_frames) | ||||||
|  |             { | ||||||
|  |                 // if we were previously paused, then dont add clock/frames,
 | ||||||
|  |                 // but wait for the next time render is called.
 | ||||||
|  |                 elapsed_clock += clock - prev_clock; | ||||||
|  |                 elapsed_frames += frames - prev_frames; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             prev_paused = false; | ||||||
|  |             prev_clock = clock; | ||||||
|  |             prev_frames = frames; | ||||||
|  | 
 | ||||||
|  |             // add FPS to history every second or after at least one frame.
 | ||||||
|  |             if (elapsed_clock >= 1000 && elapsed_frames >= 1) | ||||||
|  |             { | ||||||
|  |                 double fps = elapsed_frames / (elapsed_clock / 1000.0); | ||||||
|  |                 for (int i = history_length - 1; i >= 1; i--) | ||||||
|  |                     history[i] = history[i - 1]; | ||||||
|  |                 history[0] = fps; | ||||||
|  | 
 | ||||||
|  |                 elapsed_clock = 0; | ||||||
|  |                 elapsed_frames = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // average fps over a few seconds to stabilize the counter.
 | ||||||
|  |         double fps_sum = 0.0; | ||||||
|  |         int fps_count = 0; | ||||||
|  |         for (int i = 0; i < history_length; i++) | ||||||
|  |         { | ||||||
|  |             if (history[i] > 0.0) | ||||||
|  |             { | ||||||
|  |                 fps_sum += history[i]; | ||||||
|  |                 fps_count++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         double fps = fps_count == 0 ? 1.0 : fps_sum / fps_count; | ||||||
|  |         double gfps = df::global::enabler->calculated_gfps; | ||||||
|  | 
 | ||||||
|  |         std::stringstream fps_counter; | ||||||
|  |         fps_counter << "FPS:" | ||||||
|  |                     << setw(4) << fixed << setprecision(fps >= 1.0 ? 0 : 2) << fps | ||||||
|  |                     << " (" << gfps << ")"; | ||||||
|  | 
 | ||||||
|  |         // show this FPS counter same as the default counter.
 | ||||||
|  |         int x = 10; | ||||||
|  |         int y = 0; | ||||||
|  |         OutputString(COLOR_WHITE, x, y, fps_counter.str(), | ||||||
|  |                      false, 0, COLOR_CYAN, false); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct title_pausing_fps_counter_hook : df::viewscreen_titlest { | ||||||
|  |     typedef df::viewscreen_titlest interpose_base; | ||||||
|  | 
 | ||||||
|  |     DEFINE_VMETHOD_INTERPOSE(void, render, ()) | ||||||
|  |     { | ||||||
|  |         INTERPOSE_NEXT(render)(); | ||||||
|  | 
 | ||||||
|  |         // if init.txt have FPS:YES then enable default FPS counter when exiting dwarf mode.
 | ||||||
|  |         // So it is enabled if starting adventure mode without exiting dwarf fortress.
 | ||||||
|  |         if (dwarfmode_pausing_fps_counter_hook::init_have_fps_yes() | ||||||
|  |             && df::global::gps && df::global::gps->display_frames == 0) | ||||||
|  |             df::global::gps->display_frames = 1; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_pausing_fps_counter_hook, render); | ||||||
|  | 
 | ||||||
|  | IMPLEMENT_VMETHOD_INTERPOSE(title_pausing_fps_counter_hook, render); | ||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 3baa24fec93461218b5b658de94884ebff0a0b23 | Subproject commit 8d079a59122d9ba72ce9c0f7687402a343d09bc7 | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | function set_test_stage(stage) | ||||||
|  |     local f = io.open('test_stage.txt', 'w') | ||||||
|  |     f:write(stage) | ||||||
|  |     f:close() | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | print('running tests') | ||||||
|  | 
 | ||||||
|  | set_test_stage('done') | ||||||
|  | dfhack.run_command('die') | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | :lua dfhack.internal.addScriptPath(os.getenv('TRAVIS_BUILD_DIR')) | ||||||
|  | test/main | ||||||
| @ -0,0 +1,40 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | 
 | ||||||
|  | tardest="df.tar.bz2" | ||||||
|  | 
 | ||||||
|  | which md5sum && alias md5=md5sum | ||||||
|  | selfmd5=$(openssl md5 < "$0") | ||||||
|  | echo $selfmd5 | ||||||
|  | 
 | ||||||
|  | cd "$(dirname "$0")" | ||||||
|  | echo "DF_VERSION: $DF_VERSION" | ||||||
|  | echo "DF_FOLDER: $DF_FOLDER" | ||||||
|  | mkdir -p "$DF_FOLDER" | ||||||
|  | cd "$DF_FOLDER" | ||||||
|  | 
 | ||||||
|  | if [ -f receipt ]; then | ||||||
|  |     if [ "$selfmd5" != "$(cat receipt)" ]; then | ||||||
|  |         echo "download-df.sh changed; removing DF" | ||||||
|  |     else | ||||||
|  |         echo "Already downloaded $DF_VERSION" | ||||||
|  |         exit 0 | ||||||
|  |     fi | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | rm -rif "$tardest" df_linux | ||||||
|  | 
 | ||||||
|  | minor=$(echo "$DF_VERSION" | cut -d. -f2) | ||||||
|  | patch=$(echo "$DF_VERSION" | cut -d. -f3) | ||||||
|  | url="http://www.bay12games.com/dwarves/df_${minor}_${patch}_linux.tar.bz2" | ||||||
|  | 
 | ||||||
|  | echo Downloading | ||||||
|  | wget "$url" -O "$tardest" | ||||||
|  | echo Extracting | ||||||
|  | tar xf "$tardest" --strip-components=1 | ||||||
|  | echo Changing settings | ||||||
|  | echo '' >> "$DF_FOLDER/data/init/init.txt" | ||||||
|  | echo '[PRINT_MODE:TEXT]' >> "$DF_FOLDER/data/init/init.txt" | ||||||
|  | echo '[SOUND:NO]' >> "$DF_FOLDER/data/init/init.txt" | ||||||
|  | echo Done | ||||||
|  | 
 | ||||||
|  | echo "$selfmd5" > receipt | ||||||
| @ -0,0 +1,4 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | cd "$(dirname "$0")" | ||||||
|  | cd .. | ||||||
|  | grep DF_VERSION CMakeLists.txt | perl -ne 'print "$&\n" if /[\d\.]+/' | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | import os, subprocess, sys | ||||||
|  | 
 | ||||||
|  | MAX_TRIES = 5 | ||||||
|  | 
 | ||||||
|  | dfhack = 'Dwarf Fortress.exe' if sys.platform == 'win32' else './dfhack' | ||||||
|  | test_stage = 'test_stage.txt' | ||||||
|  | 
 | ||||||
|  | def get_test_stage(): | ||||||
|  |     if os.path.isfile(test_stage): | ||||||
|  |         return open(test_stage).read().strip() | ||||||
|  |     return '0' | ||||||
|  | 
 | ||||||
|  | os.chdir(sys.argv[1]) | ||||||
|  | if os.path.exists(test_stage): | ||||||
|  |     os.remove(test_stage) | ||||||
|  | 
 | ||||||
|  | tries = 0 | ||||||
|  | while True: | ||||||
|  |     tries += 1 | ||||||
|  |     stage = get_test_stage() | ||||||
|  |     print('Run #%i: stage=%s' % (tries, get_test_stage())) | ||||||
|  |     if stage == 'done': | ||||||
|  |         print('Done!') | ||||||
|  |         os.remove(test_stage) | ||||||
|  |         sys.exit(0) | ||||||
|  |     if tries > MAX_TRIES: | ||||||
|  |         print('Too many tries - aborting') | ||||||
|  |         sys.exit(1) | ||||||
|  | 
 | ||||||
|  |     os.system(dfhack) | ||||||
		Loading…
	
		Reference in New Issue