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 | ||||
| #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