From e48e2e65341ee9daf2e5daa3fb35c4733dda6dec Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 14 May 2016 19:41:51 +0300 Subject: [PATCH 01/44] A twbt utils plugin for misc stuff used in twbt by mifki. Currently only render map function. --- plugins/CMakeLists.txt | 1 + plugins/lua/twbt-utils.lua | 11 ++++ plugins/twbt-utils.cpp | 122 +++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 plugins/lua/twbt-utils.lua create mode 100644 plugins/twbt-utils.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b6e05b749..5d2d40813 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -149,6 +149,7 @@ if (BUILD_SUPPORTED) add_subdirectory(rendermax) DFHACK_PLUGIN(resume resume.cpp) DFHACK_PLUGIN(reveal reveal.cpp) + DFHACK_PLUGIN(twbt-utils twbt-utils.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(search search.cpp) DFHACK_PLUGIN(seedwatch seedwatch.cpp) DFHACK_PLUGIN(showmood showmood.cpp) diff --git a/plugins/lua/twbt-utils.lua b/plugins/lua/twbt-utils.lua new file mode 100644 index 000000000..56bdb0b19 --- /dev/null +++ b/plugins/lua/twbt-utils.lua @@ -0,0 +1,11 @@ +local _ENV = mkmodule('plugins.twbt-utils') + +--[[ + + Native functions: + + * render_map_rect(x,y,z,w,h) + +--]] + +return _ENV \ No newline at end of file diff --git a/plugins/twbt-utils.cpp b/plugins/twbt-utils.cpp new file mode 100644 index 000000000..7524ac29e --- /dev/null +++ b/plugins/twbt-utils.cpp @@ -0,0 +1,122 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "VersionInfo.h" +#include "VTableInterpose.h" +#include "LuaTools.h" + +#include "DataDefs.h" + +#include "df/viewscreen_dwarfmodest.h" +#include "df/init.h" +#include "df/renderer.h" +#include "df/graphic.h" + +using std::string; +using std::vector; +using namespace DFHack; +using namespace df::enums; + +DFHACK_PLUGIN("twbt-utils"); +REQUIRE_GLOBAL(window_x) +REQUIRE_GLOBAL(window_y) +REQUIRE_GLOBAL(window_z) +REQUIRE_GLOBAL_NO_USE(gps) + +#ifdef WIN32 + // On Windows there's no convert_magenta parameter. Arguments are pushed onto stack, + // except for tex_pos and filename, which go into ecx and edx. Simulating this with __fastcall. + typedef void(__fastcall *LOAD_MULTI_PDIM)(long *tex_pos, const string &filename, void *tex, long dimx, long dimy, long *disp_x, long *disp_y); + + // On Windows there's no parameter pointing to the map_renderer structure + typedef void(_stdcall *RENDER_MAP)(int); + typedef void(_stdcall *RENDER_UPDOWN)(); + + RENDER_MAP _render_map; + void render_map(){ _render_map(0); } +#else + typedef void(*LOAD_MULTI_PDIM)(void *tex, const string &filename, long *tex_pos, long dimx, long dimy, bool convert_magenta, long *disp_x, long *disp_y); + + typedef void(*RENDER_MAP)(void*, int); + typedef void(*RENDER_UPDOWN)(void*); + + RENDER_MAP _render_map; + void render_map(){ _render_map(); } +#endif +static int render_map_rect(lua_State* L) +{ + CoreSuspender suspender; + + int x = luaL_checkint(L, 1); + int y = luaL_checkint(L, 2); + int z = luaL_checkint(L, 3); + int w = luaL_checkint(L, 4); + int h = luaL_checkint(L, 5); + //backup state + uint8_t *s = df::global::gps->screen; + int32_t win_h = df::global::gps->dimy; + int32_t was_x = *window_x; + int32_t was_y = *window_y; + int32_t was_z = *window_z; + int32_t gx = df::global::init->display.grid_x; + int32_t gy = df::global::init->display.grid_y; + df::global::init->display.grid_x = w+1; + df::global::init->display.grid_y = h+1; + *window_x = x; + *window_y = y; + *window_z = z; + //force full redraw + df::global::gps->force_full_display_count = 1; + for (int ty = 0; ty < h; ty++) + for (int tx = 0; tx < w; tx++) + { + for (int i = 0; i < 4; i++) + { + int t = (tx + 1)*win_h + ty + 1; + s[t * 4 + i] = 0; + } + } + render_map(); + //restore state + *window_x = was_x; + *window_y = was_y; + *window_z = was_z; + df::global::init->display.grid_x = gx; + df::global::init->display.grid_y = gy; + + lua_createtable(L,w*h*4,0); + + int counter = 0; + for (int ty = 0; ty < h; ty++) + for (int tx = 0; tx < w; tx++) + { + for (int i = 0; i < 4;i++) + { + int t = (tx + 1)*win_h + ty + 1; + lua_pushnumber(L, s[t*4+i]); + lua_rawseti(L, -2, counter); + counter++; + } + } + return 1; +} + +DFHACK_PLUGIN_LUA_COMMANDS{ + DFHACK_LUA_COMMAND(render_map_rect), + DFHACK_LUA_END +}; + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +{ + auto addr =reinterpret_cast(Core::getInstance().vinfo->getAddress("twbt_render_map")); + if (addr == nullptr) + return CR_FAILURE; + _render_map = addr; + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) +{ + return CR_OK; +} From 6a65ad2fcbb6d4cb3a6d4cc6ddc53837c3079db2 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 21 Oct 2017 14:07:07 +0300 Subject: [PATCH 02/44] add more functions (not used yet) and fix linux --- plugins/twbt-utils.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/twbt-utils.cpp b/plugins/twbt-utils.cpp index 7524ac29e..4532f3320 100644 --- a/plugins/twbt-utils.cpp +++ b/plugins/twbt-utils.cpp @@ -12,6 +12,8 @@ #include "df/init.h" #include "df/renderer.h" #include "df/graphic.h" +#include "df/enabler.h" +#include "df/map_renderer.h" using std::string; using std::vector; @@ -23,6 +25,7 @@ REQUIRE_GLOBAL(window_x) REQUIRE_GLOBAL(window_y) REQUIRE_GLOBAL(window_z) REQUIRE_GLOBAL_NO_USE(gps) +REQUIRE_GLOBAL_NO_USE(enabler) #ifdef WIN32 // On Windows there's no convert_magenta parameter. Arguments are pushed onto stack, @@ -34,15 +37,31 @@ REQUIRE_GLOBAL_NO_USE(gps) typedef void(_stdcall *RENDER_UPDOWN)(); RENDER_MAP _render_map; + RENDER_UPDOWN _render_updown; + LOAD_MULTI_PDIM _load_multi_pdim; + void render_map(){ _render_map(0); } + void render_updown() { _render_updown(); } + void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x,long* disp_y) { + _load_multi_pdim(tex_pos, filename, &df::global::enabler->textures, dimx, dimy, disp_x, disp_y); + } #else +REQUIRE_GLOBAL_NO_USE(map_renderer) + typedef void(*LOAD_MULTI_PDIM)(void *tex, const string &filename, long *tex_pos, long dimx, long dimy, bool convert_magenta, long *disp_x, long *disp_y); typedef void(*RENDER_MAP)(void*, int); typedef void(*RENDER_UPDOWN)(void*); - RENDER_MAP _render_map; - void render_map(){ _render_map(); } + RENDER_MAP _render_map; + RENDER_UPDOWN _render_updown; + LOAD_MULTI_PDIM _load_multi_pdim; + + void render_map(){ _render_map(df::global::map_renderer,0); } + void render_updown() { _render_updown(df::global::map_renderer); } + void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x, long* disp_y) { + _load_multi_pdim(&df::global::enabler->textures, filename,tex_pos, &df::global::enabler->textures, dimx, dimy,true, disp_x, disp_y); + } #endif static int render_map_rect(lua_State* L) { From 04465f59f5bb7a3f60e3d643b41b6f43ccdf97ab Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 21 Oct 2017 14:27:08 +0300 Subject: [PATCH 03/44] Fix spaces and fix linux (again) --- plugins/twbt-utils.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/twbt-utils.cpp b/plugins/twbt-utils.cpp index 4532f3320..dd63418ca 100644 --- a/plugins/twbt-utils.cpp +++ b/plugins/twbt-utils.cpp @@ -37,14 +37,14 @@ REQUIRE_GLOBAL_NO_USE(enabler) typedef void(_stdcall *RENDER_UPDOWN)(); RENDER_MAP _render_map; - RENDER_UPDOWN _render_updown; - LOAD_MULTI_PDIM _load_multi_pdim; + RENDER_UPDOWN _render_updown; + LOAD_MULTI_PDIM _load_multi_pdim; void render_map(){ _render_map(0); } - void render_updown() { _render_updown(); } - void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x,long* disp_y) { - _load_multi_pdim(tex_pos, filename, &df::global::enabler->textures, dimx, dimy, disp_x, disp_y); - } + void render_updown() { _render_updown(); } + void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x,long* disp_y) { + _load_multi_pdim(tex_pos, filename, &df::global::enabler->textures, dimx, dimy, disp_x, disp_y); + } #else REQUIRE_GLOBAL_NO_USE(map_renderer) @@ -53,15 +53,15 @@ REQUIRE_GLOBAL_NO_USE(map_renderer) typedef void(*RENDER_MAP)(void*, int); typedef void(*RENDER_UPDOWN)(void*); - RENDER_MAP _render_map; - RENDER_UPDOWN _render_updown; - LOAD_MULTI_PDIM _load_multi_pdim; + RENDER_MAP _render_map; + RENDER_UPDOWN _render_updown; + LOAD_MULTI_PDIM _load_multi_pdim; void render_map(){ _render_map(df::global::map_renderer,0); } - void render_updown() { _render_updown(df::global::map_renderer); } - void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x, long* disp_y) { - _load_multi_pdim(&df::global::enabler->textures, filename,tex_pos, &df::global::enabler->textures, dimx, dimy,true, disp_x, disp_y); - } + void render_updown() { _render_updown(df::global::map_renderer); } + void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x, long* disp_y) { + _load_multi_pdim(&df::global::enabler->textures, filename,tex_pos, dimx, dimy,true, disp_x, disp_y); + } #endif static int render_map_rect(lua_State* L) { From d6df9cd257ddada6394cb1e08af7cbe0416adef0 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 31 Dec 2017 11:39:51 +0200 Subject: [PATCH 04/44] tidying up --- plugins/twbt-utils.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/plugins/twbt-utils.cpp b/plugins/twbt-utils.cpp index dd63418ca..4dbe3ca45 100644 --- a/plugins/twbt-utils.cpp +++ b/plugins/twbt-utils.cpp @@ -26,42 +26,23 @@ REQUIRE_GLOBAL(window_y) REQUIRE_GLOBAL(window_z) REQUIRE_GLOBAL_NO_USE(gps) REQUIRE_GLOBAL_NO_USE(enabler) +REQUIRE_GLOBAL_NO_USE(twbt_render_map) #ifdef WIN32 - // On Windows there's no convert_magenta parameter. Arguments are pushed onto stack, - // except for tex_pos and filename, which go into ecx and edx. Simulating this with __fastcall. - typedef void(__fastcall *LOAD_MULTI_PDIM)(long *tex_pos, const string &filename, void *tex, long dimx, long dimy, long *disp_x, long *disp_y); - // On Windows there's no parameter pointing to the map_renderer structure typedef void(_stdcall *RENDER_MAP)(int); - typedef void(_stdcall *RENDER_UPDOWN)(); RENDER_MAP _render_map; - RENDER_UPDOWN _render_updown; - LOAD_MULTI_PDIM _load_multi_pdim; void render_map(){ _render_map(0); } - void render_updown() { _render_updown(); } - void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x,long* disp_y) { - _load_multi_pdim(tex_pos, filename, &df::global::enabler->textures, dimx, dimy, disp_x, disp_y); - } #else REQUIRE_GLOBAL_NO_USE(map_renderer) - typedef void(*LOAD_MULTI_PDIM)(void *tex, const string &filename, long *tex_pos, long dimx, long dimy, bool convert_magenta, long *disp_x, long *disp_y); - typedef void(*RENDER_MAP)(void*, int); - typedef void(*RENDER_UPDOWN)(void*); RENDER_MAP _render_map; - RENDER_UPDOWN _render_updown; - LOAD_MULTI_PDIM _load_multi_pdim; void render_map(){ _render_map(df::global::map_renderer,0); } - void render_updown() { _render_updown(df::global::map_renderer); } - void load_tileset(const string &filename, long * tex_pos, long dimx, long dimy, long* disp_x, long* disp_y) { - _load_multi_pdim(&df::global::enabler->textures, filename,tex_pos, dimx, dimy,true, disp_x, disp_y); - } #endif static int render_map_rect(lua_State* L) { From 4e690df96a672b079d1d94d5899fc26d7402c3f1 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sun, 26 Aug 2018 18:26:09 -0500 Subject: [PATCH 05/44] Add Persistence module. Alter World to use Persistence instead of storing data in historical figures. Fake historical figures will be converted to the new format when a world is loaded. Added plugin_save and plugin_load functions to the plugin API. Made the weird int7/int28 code that was in the old persistence API slightly safer. --- library/CMakeLists.txt | 2 + library/Core.cpp | 31 +- library/PluginManager.cpp | 59 ++++ library/include/Core.h | 2 + library/include/PluginManager.h | 6 + library/include/modules/Persistence.h | 231 ++++++++++++++ library/include/modules/World.h | 78 +---- library/modules/Persistence.cpp | 420 ++++++++++++++++++++++++++ library/modules/World.cpp | 201 ++---------- plugins/skeleton/skeleton.cpp | 22 ++ 10 files changed, 793 insertions(+), 259 deletions(-) create mode 100644 library/include/modules/Persistence.h create mode 100644 library/modules/Persistence.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 33a430337..e952f7e85 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -126,6 +126,7 @@ include/modules/Maps.h include/modules/Materials.h include/modules/Notes.h include/modules/Once.h +include/modules/Persistence.h include/modules/Random.h include/modules/Renderer.h include/modules/Screen.h @@ -152,6 +153,7 @@ modules/Maps.cpp modules/Materials.cpp modules/Notes.cpp modules/Once.cpp +modules/Persistence.cpp modules/Random.cpp modules/Renderer.cpp modules/Screen.cpp diff --git a/library/Core.cpp b/library/Core.cpp index 56c400e87..e6056c62e 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -53,6 +53,7 @@ using namespace std; #include "modules/World.h" #include "modules/Graphic.h" #include "modules/Windows.h" +#include "modules/Persistence.h" #include "RemoteServer.h" #include "RemoteTools.h" #include "LuaTools.h" @@ -72,6 +73,7 @@ using namespace DFHack; #include "df/viewscreen_loadgamest.h" #include "df/viewscreen_new_regionst.h" #include "df/viewscreen_savegamest.h" +#include "df/viewscreen_optionst.h" #include #include @@ -2004,8 +2006,6 @@ void Core::doUpdate(color_ostream &out, bool first_update) last_world_data_ptr = new_wdata; last_local_map_ptr = new_mapdata; - World::ClearPersistentCache(); - // and if the world is going away, we report the map change first if(had_map) onStateChange(out, SC_MAP_UNLOADED); @@ -2022,7 +2022,6 @@ void Core::doUpdate(color_ostream &out, bool first_update) if (isMapLoaded() != had_map) { - World::ClearPersistentCache(); onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); } } @@ -2042,6 +2041,11 @@ void Core::doUpdate(color_ostream &out, bool first_update) // Execute per-frame handlers onUpdate(out); + if (df::global::ui->main.autosave_request || strict_virtual_cast(screen)) + { + doSave(out); + } + out << std::flush; } @@ -2285,6 +2289,27 @@ void Core::onStateChange(color_ostream &out, state_change_event event) Lua::Core::onStateChange(out, event); handleLoadAndUnloadScripts(out, event); + + if (event == SC_WORLD_UNLOADED) + { + Persistence::Internal::clear(); + } + if (event == SC_WORLD_LOADED) + { + doLoad(out); + } +} + +void Core::doSave(color_ostream &out) +{ + plug_mgr->doSave(out); + Persistence::Internal::save(); +} + +void Core::doLoad(color_ostream &out) +{ + Persistence::Internal::load(); + plug_mgr->doLoad(out); } int Core::Shutdown ( void ) diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index b17f8e214..6f608f91c 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -197,6 +197,9 @@ Plugin::Plugin(Core * core, const std::string & path, plugin_rpcconnect = 0; plugin_enable = 0; plugin_is_enabled = 0; + plugin_save = 0; + plugin_load = 0; + plugin_eval_ruby = 0; state = PS_UNLOADED; access = new RefLock(); } @@ -348,6 +351,8 @@ bool Plugin::load(color_ostream &con) plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); + plugin_save = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_save"); + plugin_load = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_load"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); plugin_lib = plug; @@ -359,6 +364,8 @@ bool Plugin::load(color_ostream &con) parent->registerCommands(this); if ((plugin_onupdate || plugin_enable) && !plugin_is_enabled) con.printerr("Plugin %s has no enabled var!\n", name.c_str()); + if (Core::getInstance().isWorldLoaded() && plugin_load && plugin_load(con) != CR_OK) + con.printerr("Plugin %s has failed to load saved data.\n", name.c_str()); fprintf(stderr, "loaded plugin %s; DFHack build %s\n", name.c_str(), plug_git_desc); fflush(stderr); return true; @@ -403,6 +410,8 @@ bool Plugin::unload(color_ostream &con) // enter suspend CoreSuspender suspend; access->lock(); + if (Core::getInstance().isWorldLoaded() && plugin_save && plugin_save(con) != CR_OK) + con.printerr("Plugin %s has failed to save data.\n", name.c_str()); // notify plugin about shutdown, if it has a shutdown function command_result cr = CR_OK; if(plugin_shutdown) @@ -410,6 +419,8 @@ bool Plugin::unload(color_ostream &con) // cleanup... plugin_is_enabled = 0; plugin_onupdate = 0; + plugin_save = 0; + plugin_load = 0; reset_lua(); parent->unregisterCommands(this); commands.clear(); @@ -570,6 +581,32 @@ command_result Plugin::on_state_change(color_ostream &out, state_change_event ev return cr; } +command_result Plugin::save_data(color_ostream &out) +{ + command_result cr = CR_NOT_IMPLEMENTED; + access->lock_add(); + if(state == PS_LOADED && plugin_save) + { + cr = plugin_save(out); + Lua::Core::Reset(out, "plugin_save"); + } + access->lock_sub(); + return cr; +} + +command_result Plugin::load_data(color_ostream &out) +{ + command_result cr = CR_NOT_IMPLEMENTED; + access->lock_add(); + if(state == PS_LOADED && plugin_load) + { + cr = plugin_load(out); + Lua::Core::Reset(out, "plugin_load"); + } + access->lock_sub(); + return cr; +} + RPCService *Plugin::rpc_connect(color_ostream &out) { RPCService *rv = NULL; @@ -1014,6 +1051,28 @@ void PluginManager::unregisterCommands( Plugin * p ) cmdlist_mutex->unlock(); } +void PluginManager::doSave(color_ostream &out) +{ + for (auto it = begin(); it != end(); ++it) + { + command_result cr = it->second->save_data(out); + + if (cr != CR_OK && cr != CR_NOT_IMPLEMENTED) + out.printerr("Plugin %s has failed to save data.\n", it->first.c_str()); + } +} + +void PluginManager::doLoad(color_ostream &out) +{ + for (auto it = begin(); it != end(); ++it) + { + command_result cr = it->second->load_data(out); + + if (cr != CR_OK && cr != CR_NOT_IMPLEMENTED) + out.printerr("Plugin %s has failed to load saved data.\n", it->first.c_str()); + } +} + Plugin *PluginManager::operator[] (std::string name) { MUTEX_GUARD(plugin_mutex); diff --git a/library/include/Core.h b/library/include/Core.h index 529633ff2..ab5406257 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -222,6 +222,8 @@ namespace DFHack void onUpdate(color_ostream &out); void onStateChange(color_ostream &out, state_change_event event); void handleLoadAndUnloadScripts(color_ostream &out, state_change_event event); + void doSave(color_ostream &out); + void doLoad(color_ostream &out); Core(Core const&); // Don't Implement void operator=(Core const&); // Don't implement diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index e473d7acb..8cb75cff1 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -148,6 +148,8 @@ namespace DFHack ~Plugin(); command_result on_update(color_ostream &out); command_result on_state_change(color_ostream &out, state_change_event event); + command_result save_data(color_ostream &out); + command_result load_data(color_ostream &out); void detach_connection(RPCService *svc); public: enum plugin_state @@ -238,6 +240,8 @@ namespace DFHack command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); command_result (*plugin_eval_ruby)(color_ostream &, const char*); + command_result (*plugin_save)(color_ostream &); + command_result (*plugin_load)(color_ostream &); }; class DFHACK_EXPORT PluginManager { @@ -251,6 +255,8 @@ namespace DFHack void OnStateChange(color_ostream &out, state_change_event event); void registerCommands( Plugin * p ); void unregisterCommands( Plugin * p ); + void doSave(color_ostream &out); + void doLoad(color_ostream &out); // PUBLIC METHODS public: // list names of all plugins present in hack/plugins diff --git a/library/include/modules/Persistence.h b/library/include/modules/Persistence.h new file mode 100644 index 000000000..3431bee55 --- /dev/null +++ b/library/include/modules/Persistence.h @@ -0,0 +1,231 @@ +/* +https://github.com/DFHack/dfhack +Copyright (c) 2009-2018 DFHack Team + +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. +*/ + +#pragma once +#ifndef CL_MOD_PERSISTENCE +#define CL_MOD_PERSISTENCE + +/** + * \defgroup grp_persistence Persistence: code related to saving and loading data + * @ingroup grp_modules + */ + +#include +#include +#include +#include + +#include "Export.h" +#include "Error.h" + +namespace DFHack +{ + class Core; + + namespace Persistence + { + struct LegacyData; + class Internal; + } + + class DFHACK_EXPORT PersistentDataItem { + size_t index; + std::shared_ptr data; + public: + static const int NumInts = 7; + + bool isValid() const; + size_t get_index() const + { + CHECK_INVALID_ARGUMENT(isValid()); + return index; + } + int entry_id() const { return isValid() ? int(index) + 100 : 0; } + + int raw_id() const { return isValid() ? -int(index) - 100 : 0; } + + const std::string &key() const; + + std::string &val(); + const std::string &val() const; + int &ival(int i); + int ival(int i) const; + + // Pack binary data into string field. + // Since DF serialization chokes on NUL bytes, + // use bit magic to ensure none of the bytes is 0. + // Choose the lowest bit for padding so that + // sign-extend can be used normally. + + size_t data_size() const + { + return val().size(); + } + + bool check_data(size_t off, size_t sz = 1) const + { + return (data_size() >= off + sz); + } + void ensure_data(size_t off, size_t sz = 0) + { + if (data_size() < off + sz) + { + val().resize(off + sz, '\x01'); + } + } + template + uint8_t (&pdata(size_t off))[N] + { + ensure_data(off, N); + typedef uint8_t array[N]; + return *(array *)(val().data() + off); + } + template + const uint8_t (&pdata(size_t off) const)[N] + { + CHECK_INVALID_ARGUMENT(check_data(off, N)); + typedef const uint8_t array[N]; + return *(array *)(val().data() + off); + } + template + const uint8_t (&cpdata(size_t off) const)[N] + { + return pdata(off); + } + + static const size_t int7_size = 1; + uint8_t get_uint7(size_t off) const + { + auto p = pdata(off); + return p[0] >> 1; + } + int8_t get_int7(size_t off) const + { + return int8_t(get_uint7(off)); + } + void set_uint7(size_t off, uint8_t val) + { + auto p = pdata(off); + p[0] = uint8_t((val << 1) | 1); + } + void set_int7(size_t off, int8_t val) + { + set_uint7(off, uint8_t(val)); + } + + static const size_t int28_size = 4; + uint32_t get_uint28(size_t off) const + { + auto p = pdata(off); + return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((p[3]&~1U)<<20); + } + int32_t get_int28(size_t off) const + { + return int32_t(get_uint28(off)); + } + void set_uint28(size_t off, uint32_t val) + { + auto p = pdata(off); + p[0] = uint8_t((val<<1) | 1); + p[1] = uint8_t((val>>6) | 1); + p[2] = uint8_t((val>>13) | 1); + p[3] = uint8_t((val>>20) | 1); + } + void set_int28(size_t off, int32_t val) + { + set_uint28(off, val); + } + + PersistentDataItem() : index(0), data(nullptr) {} + PersistentDataItem(size_t index, const std::shared_ptr &data) + : index(index), data(data) {} + }; + namespace Persistence + { + class Internal + { + static void clear(); + static void save(); + static void load(); + friend class ::DFHack::Core; + }; + + // Returns a new PersistentDataItem with the specified key. + // If there is no world loaded or the key is empty, returns an invalid item. + DFHACK_EXPORT PersistentDataItem addItem(const std::string &key); + // Returns an existing PersistentDataItem with the specified key. + // If "added" is not null and there is no such item, a new item is returned and + // the bool value is set to true. If "added" is not null and an item is found or + // no new item can be created, the bool value is set to false. + DFHACK_EXPORT PersistentDataItem getByKey(const std::string &key, bool *added = nullptr); + // Returns an existing PersistentDataItem with the specified index. + // If there is no world loaded or the index is empty, returns an invalid item. + DFHACK_EXPORT PersistentDataItem getByIndex(size_t index); + // If the item is invalid, returns false. Otherwise, deletes the item and returns + // true. All references to the item are invalid as soon as this function returns. + DFHACK_EXPORT bool deleteItem(const PersistentDataItem &item); + // Fills the vector with references to each persistent item. + DFHACK_EXPORT void getAll(std::vector &vec); + // Fills the vector with references to each persistent item with a key that is + // greater than or equal to "min" and less than "max". + DFHACK_EXPORT void getAllByKeyRange(std::vector &vec, const std::string &min, const std::string &max); + // Fills the vector with references to each persistent item with a key that is + // equal to the given key. + DFHACK_EXPORT void getAllByKey(std::vector &vec, const std::string &key); + +#if defined(__GNUC__) && __GNUC__ < 5 + // file stream move constructors are missing in libstdc++ before version 5. + template + class DFHACK_EXPORT gcc_4_fstream_shim : public T + { + const std::string file; + public: + explicit gcc_4_fstream_shim() : T(), file() {} + explicit gcc_4_fstream_shim(const std::string &file) : T(file), file() {} + gcc_4_fstream_shim(gcc_4_fstream_shim && s) : T(), file(s.file) + { + if (!file.empty()) + { + T::open(file.c_str()); + } + } + }; +#define FSTREAM(x) gcc_4_fstream_shim +#else +#define FSTREAM(x) x +#endif + + // Returns an input stream that data can be read from. If no world is currently loaded, + // or no data has been saved with the specified key, the stream is invalid and acts + // as if the file is empty. + DFHACK_EXPORT FSTREAM(std::ifstream) readSaveData(const std::string &key); + // Returns an output stream that data can be saved to. If no world is currently loaded, + // an invalid stream is returned, and writing to it has no effect. + DFHACK_EXPORT FSTREAM(std::ofstream) writeSaveData(const std::string &key); + +#undef FSTREAM + } +} + +#endif diff --git a/library/include/modules/World.h b/library/include/modules/World.h index 67c4082df..d5f21ec08 100644 --- a/library/include/modules/World.h +++ b/library/include/modules/World.h @@ -33,6 +33,7 @@ distribution. #include "Export.h" #include "Module.h" +#include "modules/Persistence.h" #include #include "DataDefs.h" @@ -60,81 +61,6 @@ namespace DFHack }; class DFContextShared; - class DFHACK_EXPORT PersistentDataItem { - int id; - std::string key_value; - - std::string *str_value; - int *int_values; - public: - static const int NumInts = 7; - - bool isValid() const { return id != 0; } - int entry_id() const { return -id; } - - int raw_id() const { return id; } - - const std::string &key() const { return key_value; } - - std::string &val() { return *str_value; } - const std::string &val() const { return *str_value; } - int &ival(int i) { return int_values[i]; } - int ival(int i) const { return int_values[i]; } - - // Pack binary data into string field. - // Since DF serialization chokes on NUL bytes, - // use bit magic to ensure none of the bytes is 0. - // Choose the lowest bit for padding so that - // sign-extend can be used normally. - - size_t data_size() const { return str_value->size(); } - - bool check_data(size_t off, size_t sz = 1) { - return (str_value->size() >= off+sz); - } - void ensure_data(size_t off, size_t sz = 0) { - if (str_value->size() < off+sz) str_value->resize(off+sz, '\x01'); - } - uint8_t *pdata(size_t off) { return (uint8_t*)&(*str_value)[off]; } - - static const size_t int7_size = 1; - uint8_t get_uint7(size_t off) { - uint8_t *p = pdata(off); - return p[0]>>1; - } - int8_t get_int7(size_t off) { - uint8_t *p = pdata(off); - return int8_t(p[0])>>1; - } - void set_uint7(size_t off, uint8_t val) { - uint8_t *p = pdata(off); - p[0] = uint8_t((val<<1) | 1); - } - void set_int7(size_t off, int8_t val) { set_uint7(off, val); } - - static const size_t int28_size = 4; - uint32_t get_uint28(size_t off) { - uint8_t *p = pdata(off); - return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((p[3]&~1U)<<20); - } - int32_t get_int28(size_t off) { - uint8_t *p = pdata(off); - return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((int8_t(p[3])&~1)<<20); - } - void set_uint28(size_t off, uint32_t val) { - uint8_t *p = pdata(off); - p[0] = uint8_t((val<<1) | 1); - p[1] = uint8_t((val>>6) | 1); - p[2] = uint8_t((val>>13) | 1); - p[3] = uint8_t((val>>20) | 1); - } - void set_int28(size_t off, int32_t val) { set_uint28(off, val); } - - PersistentDataItem() : id(0), str_value(0), int_values(0) {} - PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv) - : id(id), key_value(key), str_value(sv), int_values(iv) {} - }; - /** * The World module * \ingroup grp_modules @@ -179,8 +105,6 @@ namespace DFHack // Deletes the item; returns true if success. DFHACK_EXPORT bool DeletePersistentData(const PersistentDataItem &item); - DFHACK_EXPORT void ClearPersistentCache(); - DFHACK_EXPORT df::tile_bitmask *getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create = false); DFHACK_EXPORT bool deletePersistentTilemask(const PersistentDataItem &item, df::map_block *block); } diff --git a/library/modules/Persistence.cpp b/library/modules/Persistence.cpp new file mode 100644 index 000000000..fa866897f --- /dev/null +++ b/library/modules/Persistence.cpp @@ -0,0 +1,420 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) + +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. +*/ + +#include "Internal.h" +#include +#include +#include + +#include "Core.h" +#include "DataDefs.h" +#include "modules/Persistence.h" +#include "modules/World.h" + +#include "df/historical_figure.h" + +using namespace DFHack; + +static std::vector> legacy_data; +static std::multimap index_cache; + +struct Persistence::LegacyData +{ + const std::string key; + std::string str_value; + std::array int_values; + + explicit LegacyData(const std::string &key) : key(key) + { + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + int_values.at(i) = -1; + } + } + explicit LegacyData(Json::Value &json) : key(json["k"].asString()) + { + str_value = json["s"].asString(); + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + int_values.at(i) = json["i"][i].asInt(); + } + } + explicit LegacyData(const df::language_name &name) : key(name.first_name) + { + str_value = name.nickname; + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + int_values.at(i) = name.words[i]; + } + } + + Json::Value toJSON() + { + Json::Value json(Json::objectValue); + json["k"] = key; + json["s"] = str_value; + Json::Value ints(Json::arrayValue); + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + ints[i] = int_values.at(i); + } + json["i"] = std::move(ints); + + return std::move(json); + } +}; + +const std::string &PersistentDataItem::key() const +{ + CHECK_INVALID_ARGUMENT(isValid()); + return data->key; +} + +std::string &PersistentDataItem::val() +{ + CHECK_INVALID_ARGUMENT(isValid()); + return data->str_value; +} +const std::string &PersistentDataItem::val() const +{ + CHECK_INVALID_ARGUMENT(isValid()); + return data->str_value; +} +int &PersistentDataItem::ival(int i) +{ + CHECK_INVALID_ARGUMENT(isValid()); + CHECK_INVALID_ARGUMENT(i >= 0 && i < NumInts); + return data->int_values.at(i); +} +int PersistentDataItem::ival(int i) const +{ + CHECK_INVALID_ARGUMENT(isValid()); + CHECK_INVALID_ARGUMENT(i >= 0 && i < NumInts); + return data->int_values.at(i); +} + +bool PersistentDataItem::isValid() const +{ + if (data == nullptr) + return false; + + CoreSuspender suspend; + + if (legacy_data.size() <= index) + return false; + + return legacy_data.at(index) == data; +} + +void Persistence::Internal::clear() +{ + CoreSuspender suspend; + + legacy_data.clear(); + index_cache.clear(); +} + +void Persistence::Internal::save() +{ + CoreSuspender suspend; + + Json::Value json(Json::arrayValue); + for (size_t i = 0; i < legacy_data.size(); i++) + { + if (legacy_data.at(i) != nullptr) + { + while (json.size() < i) + { + json[json.size()] = Json::Value(); + } + + json[int(i)] = legacy_data.at(i)->toJSON(); + } + } + + auto file = writeSaveData("legacy-data"); + file << json; +} + +static void convertHFigs() +{ + auto &figs = df::historical_figure::get_vector(); + + auto src = figs.begin(); + while (src != figs.end() && (*src)->id > -100) + { + ++src; + } + + if (src == figs.end()) + { + return; + } + + auto dst = src; + while (src != figs.end()) + { + auto fig = *src; + if (fig->id > -100) + { + *dst = *src; + ++dst; + } + else + { + if (fig->name.has_name && !fig->name.first_name.empty()) + { + size_t index = size_t(-fig->id - 100); + if (legacy_data.size() <= index) + { + legacy_data.resize(index + 1); + } + legacy_data.at(index) = std::shared_ptr(new Persistence::LegacyData(fig->name)); + } + delete fig; + } + ++src; + } + + figs.erase(dst, figs.end()); +} + +void Persistence::Internal::load() +{ + CoreSuspender suspend; + + clear(); + + auto file = readSaveData("legacy-data"); + Json::Value json; + try + { + file >> json; + } + catch (std::exception &) + { + // empty file? + } + + if (json.isArray()) + { + legacy_data.resize(json.size()); + for (size_t i = 0; i < legacy_data.size(); i++) + { + if (json[int(i)].isObject()) + { + legacy_data.at(i) = std::shared_ptr(new LegacyData(json[int(i)])); + } + } + } + + convertHFigs(); + + for (size_t i = 0; i < legacy_data.size(); i++) + { + if (legacy_data.at(i) == nullptr) + { + continue; + } + + index_cache.insert(std::make_pair(legacy_data.at(i)->key, i)); + } +} + +PersistentDataItem Persistence::addItem(const std::string &key) +{ + if (key.empty() || !Core::getInstance().isWorldLoaded()) + return PersistentDataItem(); + + CoreSuspender suspend; + + size_t index = 0; + while (index < legacy_data.size() && legacy_data.at(index) != nullptr) + { + index++; + } + + auto ptr = std::shared_ptr(new LegacyData(key)); + + if (index == legacy_data.size()) + { + legacy_data.push_back(ptr); + } + else + { + legacy_data.at(index) = ptr; + } + + index_cache.insert(std::make_pair(key, index)); + + return PersistentDataItem(index, ptr); +} + +PersistentDataItem Persistence::getByKey(const std::string &key, bool *added) +{ + CoreSuspender suspend; + + auto it = index_cache.find(key); + + if (added) + { + *added = it == index_cache.end(); + } + + if (it != index_cache.end()) + { + return PersistentDataItem(it->second, legacy_data.at(it->second)); + } + + if (!added) + { + return PersistentDataItem(); + } + + return addItem(key); +} + +PersistentDataItem Persistence::getByIndex(size_t index) +{ + CoreSuspender suspend; + + if (index < legacy_data.size() && legacy_data.at(index) != nullptr) + { + return PersistentDataItem(index, legacy_data.at(index)); + } + + return PersistentDataItem(); +} + +bool Persistence::deleteItem(const PersistentDataItem &item) +{ + CoreSuspender suspend; + + if (!item.isValid()) + { + return false; + } + + size_t index = item.get_index(); + auto range = index_cache.equal_range(item.key()); + for (auto it = range.first; it != range.second; ++it) + { + if (it->second == index) + { + index_cache.erase(it); + break; + } + } + legacy_data.at(index) = nullptr; + + return true; +} + +void Persistence::getAll(std::vector &vec) +{ + vec.clear(); + + CoreSuspender suspend; + + for (size_t i = 0; i < legacy_data.size(); i++) + { + if (legacy_data.at(i) != nullptr) + { + vec.push_back(PersistentDataItem(i, legacy_data.at(i))); + } + } +} + +void Persistence::getAllByKeyRange(std::vector &vec, const std::string &min, const std::string &max) +{ + vec.clear(); + + CoreSuspender suspend; + + auto begin = index_cache.lower_bound(min); + auto end = index_cache.lower_bound(max); + for (auto it = begin; it != end; ++it) + { + vec.push_back(PersistentDataItem(it->second, legacy_data.at(it->second))); + } +} + +void Persistence::getAllByKey(std::vector &vec, const std::string &key) +{ + vec.clear(); + + CoreSuspender suspend; + + auto range = index_cache.equal_range(key); + for (auto it = range.first; it != range.second; ++it) + { + vec.push_back(PersistentDataItem(it->second, legacy_data.at(it->second))); + } +} + +static std::string filterSaveFileName(std::string s) +{ + for (auto &ch : s) + { + if (!isalnum(ch) && ch != '-' && ch != '_') + { + ch = '_'; + } + } + return s; +} + +static std::string getSaveFilePath(const std::string &world, const std::string &name) +{ + return "data/save/" + world + "/dfhack-" + filterSaveFileName(name) + ".dat"; +} + +#if defined(__GNUC__) && __GNUC__ < 5 +// file stream move constructors are missing in libstdc++ before version 5. +#define FSTREAM(x) Persistence::gcc_4_fstream_shim +#else +#define FSTREAM(x) x +#endif +FSTREAM(std::ifstream) Persistence::readSaveData(const std::string &name) +{ + if (!Core::getInstance().isWorldLoaded()) + { + // No world loaded - return unopened stream. + return FSTREAM(std::ifstream)(); + } + + return FSTREAM(std::ifstream)(getSaveFilePath(World::ReadWorldFolder(), name)); +} + +FSTREAM(std::ofstream) Persistence::writeSaveData(const std::string &name) +{ + if (!Core::getInstance().isWorldLoaded()) + { + // No world loaded - return unopened stream. + return FSTREAM(std::ofstream)(); + } + + return FSTREAM(std::ofstream)(getSaveFilePath("current", name)); +} +#undef FSTREAM diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 3be400515..12b339e95 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -24,6 +24,7 @@ distribution. #include "Internal.h" +#include #include #include #include @@ -56,10 +57,6 @@ using namespace df::enums; using df::global::world; -static int next_persistent_id = 0; -static std::multimap persistent_index; -typedef std::pair T_persistent_item; - bool World::ReadPauseState() { return DF_GLOBAL_VALUE(pause_state, false); @@ -183,117 +180,14 @@ bool World::isLegends(df::game_type t) return (t == game_type::VIEW_LEGENDS); } -static PersistentDataItem dataFromHFig(df::historical_figure *hfig) -{ - return PersistentDataItem(hfig->id, hfig->name.first_name, &hfig->name.nickname, hfig->name.words); -} - -// Hide fake histfigs from legends xml export -static bool in_export_xml = false; - -struct hide_fake_histfigs_hook : df::viewscreen_legendsst { - typedef df::viewscreen_legendsst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (input->count(interface_key::LEGENDS_EXPORT_XML)) - { - auto &figs = df::historical_figure::get_vector(); - - auto it = figs.begin(); - while (it != figs.end() && (*it)->id <= -100) - ++it; - - // Move our histfigs to a temporary vector - std::vector fakes(figs.begin(), it); - figs.erase(figs.begin(), it); - in_export_xml = true; - - INTERPOSE_NEXT(feed)(input); - - in_export_xml = false; - figs.insert(figs.begin(), fakes.begin(), fakes.end()); - } - else - INTERPOSE_NEXT(feed)(input); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(hide_fake_histfigs_hook, feed, -10000); - -void World::ClearPersistentCache() -{ - next_persistent_id = 0; - persistent_index.clear(); - - INTERPOSE_HOOK(hide_fake_histfigs_hook, feed).apply(Core::getInstance().isWorldLoaded()); -} - -static bool BuildPersistentCache() -{ - if (in_export_xml) - return false; - if (next_persistent_id) - return true; - if (!Core::getInstance().isWorldLoaded()) - return false; - - std::vector &hfvec = df::historical_figure::get_vector(); - - // Determine the next entry id as min(-100, lowest_id-1) - next_persistent_id = -100; - - if (hfvec.size() > 0 && hfvec[0]->id <= -100) - next_persistent_id = hfvec[0]->id-1; - - // Add the entries to the lookup table - persistent_index.clear(); - - for (size_t i = 0; i < hfvec.size() && hfvec[i]->id <= -100; i++) - { - if (!hfvec[i]->name.has_name || hfvec[i]->name.first_name.empty()) - continue; - - persistent_index.insert(T_persistent_item(hfvec[i]->name.first_name, -hfvec[i]->id)); - } - - return true; -} - PersistentDataItem World::AddPersistentData(const std::string &key) { - if (!BuildPersistentCache() || key.empty()) - return PersistentDataItem(); - - std::vector &hfvec = df::historical_figure::get_vector(); - - df::historical_figure *hfig = new df::historical_figure(); - hfig->id = next_persistent_id; - hfig->name.has_name = true; - hfig->name.first_name = key; - memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); - - if (!hfvec.empty()) - hfig->id = std::min(hfig->id, hfvec[0]->id-1); - next_persistent_id = hfig->id-1; - - hfvec.insert(hfvec.begin(), hfig); - - persistent_index.insert(T_persistent_item(key, -hfig->id)); - - return dataFromHFig(hfig); + return Persistence::addItem(key); } PersistentDataItem World::GetPersistentData(const std::string &key) { - if (!BuildPersistentCache()) - return PersistentDataItem(); - - auto it = persistent_index.find(key); - if (it != persistent_index.end()) - return GetPersistentData(it->second); - - return PersistentDataItem(); + return Persistence::getByKey(key); } PersistentDataItem World::GetPersistentData(int entry_id) @@ -301,96 +195,45 @@ PersistentDataItem World::GetPersistentData(int entry_id) if (entry_id < 100) return PersistentDataItem(); - auto hfig = df::historical_figure::find(-entry_id); - if (hfig && hfig->name.has_name) - return dataFromHFig(hfig); - - return PersistentDataItem(); + return Persistence::getByIndex(size_t(entry_id - 100)); } PersistentDataItem World::GetPersistentData(const std::string &key, bool *added) { - if (added) *added = false; - - PersistentDataItem rv = GetPersistentData(key); - - if (!rv.isValid()) - { - if (added) *added = true; - rv = AddPersistentData(key); - } + bool temp = false; + if (!added) + added = &temp; - return rv; + return Persistence::getByKey(key, added); } void World::GetPersistentData(std::vector *vec, const std::string &key, bool prefix) { - vec->clear(); - - if (!BuildPersistentCache()) - return; - - auto eqrange = persistent_index.equal_range(key); - - if (prefix) + if (prefix && key.empty()) { - if (key.empty()) + Persistence::getAll(*vec); + } + else if (prefix) + { + std::string min = key; + if (min.back() != '/') { - eqrange.first = persistent_index.begin(); - eqrange.second = persistent_index.end(); + min.push_back('/'); } - else - { - std::string bound = key; - if (bound[bound.size()-1] != '/') - bound += "/"; - eqrange.first = persistent_index.lower_bound(bound); + std::string max = min; + ++max.back(); - bound[bound.size()-1]++; - eqrange.second = persistent_index.lower_bound(bound); - } + Persistence::getAllByKeyRange(*vec, min, max); } - - for (auto it = eqrange.first; it != eqrange.second; ++it) + else { - auto hfig = df::historical_figure::find(-it->second); - if (hfig && hfig->name.has_name) - vec->push_back(dataFromHFig(hfig)); + Persistence::getAllByKey(*vec, key); } } bool World::DeletePersistentData(const PersistentDataItem &item) { - int id = item.raw_id(); - if (id > -100) - return false; - if (!BuildPersistentCache()) - return false; - - std::vector &hfvec = df::historical_figure::get_vector(); - - auto eqrange = persistent_index.equal_range(item.key()); - - for (auto it2 = eqrange.first; it2 != eqrange.second; ) - { - auto it = it2; ++it2; - - if (it->second != -id) - continue; - - persistent_index.erase(it); - - int idx = binsearch_index(hfvec, id); - - if (idx >= 0) { - delete hfvec[idx]; - hfvec.erase(hfvec.begin()+idx); - } - - return true; - } - - return false; + return Persistence::deleteItem(item); } df::tile_bitmask *World::getPersistentTilemask(const PersistentDataItem &item, df::map_block *block, bool create) diff --git a/plugins/skeleton/skeleton.cpp b/plugins/skeleton/skeleton.cpp index ea2f4fb4a..82d988a2e 100644 --- a/plugins/skeleton/skeleton.cpp +++ b/plugins/skeleton/skeleton.cpp @@ -6,6 +6,9 @@ #include #include +// If you need to save data per-world: +//#include "modules/Persistence.h" + // DF data structure definition headers #include "DataDefs.h" //#include "df/world.h" @@ -86,6 +89,25 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } */ +// If you need to save or load world-specific data, define these functions. +// plugin_save is called when the game might be about to save the world, +// and plugin_load is called whenever a new world is loaded. If the plugin +// is loaded or unloaded while a world is active, plugin_save or plugin_load +// will be called immediately. +/* +DFhackCExport command_result plugin_save( color_ostream &out ) +{ + // Call functions in the Persistence module here. + return CR_OK; +} + +DFhackCExport command_result plugin_load( color_ostream &out ) +{ + // Call functions in the Persistence module here. + return CR_OK; +} +*/ + // A command! It sits around and looks pretty. And it's nice and friendly. command_result skeleton (color_ostream &out, std::vector & parameters) { From a672ffcb95ed85187b4ed81e5feff503f52634d4 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sun, 26 Aug 2018 19:05:44 -0500 Subject: [PATCH 06/44] Use viewscreen_savegamest instead of viewscreen_optionst --- library/Core.cpp | 3 +-- library/include/modules/Persistence.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index e6056c62e..1a72e097d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -73,7 +73,6 @@ using namespace DFHack; #include "df/viewscreen_loadgamest.h" #include "df/viewscreen_new_regionst.h" #include "df/viewscreen_savegamest.h" -#include "df/viewscreen_optionst.h" #include #include @@ -2041,7 +2040,7 @@ void Core::doUpdate(color_ostream &out, bool first_update) // Execute per-frame handlers onUpdate(out); - if (df::global::ui->main.autosave_request || strict_virtual_cast(screen)) + if (df::global::ui->main.autosave_request || strict_virtual_cast(screen)) { doSave(out); } diff --git a/library/include/modules/Persistence.h b/library/include/modules/Persistence.h index 3431bee55..11f959698 100644 --- a/library/include/modules/Persistence.h +++ b/library/include/modules/Persistence.h @@ -52,6 +52,7 @@ namespace DFHack class DFHACK_EXPORT PersistentDataItem { size_t index; std::shared_ptr data; + public: static const int NumInts = 7; From 10267f3e70d4f608cece59835d72f2b301e4a789 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sun, 26 Aug 2018 19:11:41 -0500 Subject: [PATCH 07/44] Only save on the first frame of the save screen. --- library/Core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 1a72e097d..2cbba1cf1 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2040,7 +2040,7 @@ void Core::doUpdate(color_ostream &out, bool first_update) // Execute per-frame handlers onUpdate(out); - if (df::global::ui->main.autosave_request || strict_virtual_cast(screen)) + if (df::global::ui->main.autosave_request || (vs_changed && strict_virtual_cast(screen))) { doSave(out); } From cb9c964722ebe10ac313a61f853b0960b0094e15 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 10:40:20 +0300 Subject: [PATCH 08/44] Rename twbt-utils to map-render --- plugins/CMakeLists.txt | 2 +- plugins/{twbt-utils.cpp => map-render.cpp} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename plugins/{twbt-utils.cpp => map-render.cpp} (99%) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7a26188b5..f2e2ee030 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -135,6 +135,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) DFHACK_PLUGIN(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) DFHACK_PLUGIN(manipulator manipulator.cpp) + DFHACK_PLUGIN(map-render map-render.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(misery misery.cpp) DFHACK_PLUGIN(mode mode.cpp) DFHACK_PLUGIN(mousequery mousequery.cpp) @@ -151,7 +152,6 @@ if (BUILD_SUPPORTED) add_subdirectory(rendermax) DFHACK_PLUGIN(resume resume.cpp) DFHACK_PLUGIN(reveal reveal.cpp) - DFHACK_PLUGIN(twbt-utils twbt-utils.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(search search.cpp) DFHACK_PLUGIN(seedwatch seedwatch.cpp) DFHACK_PLUGIN(showmood showmood.cpp) diff --git a/plugins/twbt-utils.cpp b/plugins/map-render.cpp similarity index 99% rename from plugins/twbt-utils.cpp rename to plugins/map-render.cpp index 4dbe3ca45..c3506ff59 100644 --- a/plugins/twbt-utils.cpp +++ b/plugins/map-render.cpp @@ -20,7 +20,7 @@ using std::vector; using namespace DFHack; using namespace df::enums; -DFHACK_PLUGIN("twbt-utils"); +DFHACK_PLUGIN("map-render"); REQUIRE_GLOBAL(window_x) REQUIRE_GLOBAL(window_y) REQUIRE_GLOBAL(window_z) From f74ee143ddbc8b09169813430d963f583b71bc50 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 10:40:44 +0300 Subject: [PATCH 09/44] Might as well use REQUIRE_GLOBAL --- plugins/map-render.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/map-render.cpp b/plugins/map-render.cpp index c3506ff59..52a7f7699 100644 --- a/plugins/map-render.cpp +++ b/plugins/map-render.cpp @@ -36,13 +36,13 @@ REQUIRE_GLOBAL_NO_USE(twbt_render_map) void render_map(){ _render_map(0); } #else -REQUIRE_GLOBAL_NO_USE(map_renderer) +REQUIRE_GLOBAL(map_renderer) typedef void(*RENDER_MAP)(void*, int); RENDER_MAP _render_map; - void render_map(){ _render_map(df::global::map_renderer,0); } + void render_map(){ _render_map(map_renderer,0); } #endif static int render_map_rect(lua_State* L) { From d9d470e4b6d474f6d4b2347a5e1b21dc6369d237 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 10:41:43 +0300 Subject: [PATCH 10/44] Missing REQUIRE_GLOBAL(init) --- plugins/map-render.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/map-render.cpp b/plugins/map-render.cpp index 52a7f7699..1de871741 100644 --- a/plugins/map-render.cpp +++ b/plugins/map-render.cpp @@ -27,6 +27,7 @@ REQUIRE_GLOBAL(window_z) REQUIRE_GLOBAL_NO_USE(gps) REQUIRE_GLOBAL_NO_USE(enabler) REQUIRE_GLOBAL_NO_USE(twbt_render_map) +REQUIRE_GLOBAL(init) #ifdef WIN32 // On Windows there's no parameter pointing to the map_renderer structure @@ -59,10 +60,10 @@ static int render_map_rect(lua_State* L) int32_t was_x = *window_x; int32_t was_y = *window_y; int32_t was_z = *window_z; - int32_t gx = df::global::init->display.grid_x; - int32_t gy = df::global::init->display.grid_y; - df::global::init->display.grid_x = w+1; - df::global::init->display.grid_y = h+1; + int32_t gx = init->display.grid_x; + int32_t gy = init->display.grid_y; + init->display.grid_x = w+1; + init->display.grid_y = h+1; *window_x = x; *window_y = y; *window_z = z; @@ -82,8 +83,8 @@ static int render_map_rect(lua_State* L) *window_x = was_x; *window_y = was_y; *window_z = was_z; - df::global::init->display.grid_x = gx; - df::global::init->display.grid_y = gy; + init->display.grid_x = gx; + init->display.grid_y = gy; lua_createtable(L,w*h*4,0); From 5c1b7030e739ddf32683cd9d3e87086ab93bd7c5 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 10:44:56 +0300 Subject: [PATCH 11/44] Also rename the plugin lua file --- plugins/lua/{twbt-utils.lua => map-render.lua} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename plugins/lua/{twbt-utils.lua => map-render.lua} (63%) diff --git a/plugins/lua/twbt-utils.lua b/plugins/lua/map-render.lua similarity index 63% rename from plugins/lua/twbt-utils.lua rename to plugins/lua/map-render.lua index 56bdb0b19..9785c365e 100644 --- a/plugins/lua/twbt-utils.lua +++ b/plugins/lua/map-render.lua @@ -1,4 +1,4 @@ -local _ENV = mkmodule('plugins.twbt-utils') +local _ENV = mkmodule('plugins.map-render') --[[ From ff452f9181ed51dd3be1d05640cf78f0096330f1 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 13:37:49 +0300 Subject: [PATCH 12/44] Add more comments --- plugins/map-render.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/map-render.cpp b/plugins/map-render.cpp index 1de871741..d299c237c 100644 --- a/plugins/map-render.cpp +++ b/plugins/map-render.cpp @@ -54,8 +54,9 @@ static int render_map_rect(lua_State* L) int z = luaL_checkint(L, 3); int w = luaL_checkint(L, 4); int h = luaL_checkint(L, 5); + uint8_t *s = df::global::gps->screen; //backup state - uint8_t *s = df::global::gps->screen; + //TODO: figure out if we can replace screen with other pointer. That way it could be a bit more tidy int32_t win_h = df::global::gps->dimy; int32_t was_x = *window_x; int32_t was_y = *window_y; @@ -69,6 +70,7 @@ static int render_map_rect(lua_State* L) *window_z = z; //force full redraw df::global::gps->force_full_display_count = 1; + //this modifies screen so it REALLY wants to redraw stuff for (int ty = 0; ty < h; ty++) for (int tx = 0; tx < w; tx++) { From 96d11d1f543883bfb63147bfaefce9df16a812b2 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 12 Oct 2018 13:48:53 +0300 Subject: [PATCH 13/44] Add to docs --- docs/Lua API.rst | 16 ++++++++++++++++ docs/Plugins.rst | 1 + 2 files changed, 17 insertions(+) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 3eb053e73..a5894a3a5 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3920,6 +3920,22 @@ A class with all the tcp functionality. Tries connecting to that address and port. Returns ``client`` object. + +.. _map-render: + +map-render +========== + +A way to ask df to render a slice of map. This uses native df rendering function so it's highly dependant on +df settings (e.g. used tileset, colors, if using graphics or not and so on...) + +Functions +--------- + +- ``render_map_rect(x,y,z,w,h)`` + + returns a table with w*h*4 entries of rendered tiles. The format is same as ``df.global.gps.screen`` (tile,foreground,bright,background). + .. _cxxrandom: cxxrandom diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 62ddf29e6..458a43722 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -2745,4 +2745,5 @@ in the `lua-api` file under `lua-plugins`: * `eventful` * `building-hacks` * `luasocket` +* `map-render` * `cxxrandom` From b43ecf5fb1f9ea69088ac55ff95cba9b8667a930 Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 6 Dec 2018 15:20:33 +0200 Subject: [PATCH 14/44] Update Lua API.rst Remove trailing whitespace --- docs/Lua API.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index a5894a3a5..a4130096b 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3926,7 +3926,7 @@ A class with all the tcp functionality. map-render ========== -A way to ask df to render a slice of map. This uses native df rendering function so it's highly dependant on +A way to ask df to render a slice of map. This uses native df rendering function so it's highly dependant on df settings (e.g. used tileset, colors, if using graphics or not and so on...) Functions From b1f9486edde856f43542259c860c9861e57f33fe Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 6 Dec 2018 15:21:19 +0200 Subject: [PATCH 15/44] Update map-render.cpp Remove tabs --- plugins/map-render.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/map-render.cpp b/plugins/map-render.cpp index d299c237c..f89d8ab4e 100644 --- a/plugins/map-render.cpp +++ b/plugins/map-render.cpp @@ -54,9 +54,9 @@ static int render_map_rect(lua_State* L) int z = luaL_checkint(L, 3); int w = luaL_checkint(L, 4); int h = luaL_checkint(L, 5); - uint8_t *s = df::global::gps->screen; + uint8_t *s = df::global::gps->screen; //backup state - //TODO: figure out if we can replace screen with other pointer. That way it could be a bit more tidy + //TODO: figure out if we can replace screen with other pointer. That way it could be a bit more tidy int32_t win_h = df::global::gps->dimy; int32_t was_x = *window_x; int32_t was_y = *window_y; @@ -70,7 +70,7 @@ static int render_map_rect(lua_State* L) *window_z = z; //force full redraw df::global::gps->force_full_display_count = 1; - //this modifies screen so it REALLY wants to redraw stuff + //this modifies screen so it REALLY wants to redraw stuff for (int ty = 0; ty < h; ty++) for (int tx = 0; tx < w; tx++) { From 5b11d14c6c79020d6910d8a797aa42bd5663ba34 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 29 May 2019 19:52:03 -0400 Subject: [PATCH 16/44] Rename save/load to "save data"/"load data" --- library/Core.cpp | 12 ++++++------ library/PluginManager.cpp | 32 ++++++++++++++++---------------- library/include/Core.h | 4 ++-- library/include/PluginManager.h | 8 ++++---- plugins/skeleton/skeleton.cpp | 12 ++++++------ 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 0c0fc13c2..1f4c5859c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2027,7 +2027,7 @@ void Core::doUpdate(color_ostream &out, bool first_update) if (df::global::ui->main.autosave_request || (vs_changed && strict_virtual_cast(screen))) { - doSave(out); + doSaveData(out); } out << std::flush; @@ -2280,20 +2280,20 @@ void Core::onStateChange(color_ostream &out, state_change_event event) } if (event == SC_WORLD_LOADED) { - doLoad(out); + doLoadData(out); } } -void Core::doSave(color_ostream &out) +void Core::doSaveData(color_ostream &out) { - plug_mgr->doSave(out); + plug_mgr->doSaveData(out); Persistence::Internal::save(); } -void Core::doLoad(color_ostream &out) +void Core::doLoadData(color_ostream &out) { Persistence::Internal::load(); - plug_mgr->doLoad(out); + plug_mgr->doLoadData(out); } int Core::Shutdown ( void ) diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 6f608f91c..05db0521d 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -197,8 +197,8 @@ Plugin::Plugin(Core * core, const std::string & path, plugin_rpcconnect = 0; plugin_enable = 0; plugin_is_enabled = 0; - plugin_save = 0; - plugin_load = 0; + plugin_save_data = 0; + plugin_load_data = 0; plugin_eval_ruby = 0; state = PS_UNLOADED; access = new RefLock(); @@ -351,8 +351,8 @@ bool Plugin::load(color_ostream &con) plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); - plugin_save = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_save"); - plugin_load = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_load"); + plugin_save_data = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_save_data"); + plugin_load_data = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_load_data"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); plugin_lib = plug; @@ -364,7 +364,7 @@ bool Plugin::load(color_ostream &con) parent->registerCommands(this); if ((plugin_onupdate || plugin_enable) && !plugin_is_enabled) con.printerr("Plugin %s has no enabled var!\n", name.c_str()); - if (Core::getInstance().isWorldLoaded() && plugin_load && plugin_load(con) != CR_OK) + if (Core::getInstance().isWorldLoaded() && plugin_load_data && plugin_load_data(con) != CR_OK) con.printerr("Plugin %s has failed to load saved data.\n", name.c_str()); fprintf(stderr, "loaded plugin %s; DFHack build %s\n", name.c_str(), plug_git_desc); fflush(stderr); @@ -410,7 +410,7 @@ bool Plugin::unload(color_ostream &con) // enter suspend CoreSuspender suspend; access->lock(); - if (Core::getInstance().isWorldLoaded() && plugin_save && plugin_save(con) != CR_OK) + if (Core::getInstance().isWorldLoaded() && plugin_save_data && plugin_save_data(con) != CR_OK) con.printerr("Plugin %s has failed to save data.\n", name.c_str()); // notify plugin about shutdown, if it has a shutdown function command_result cr = CR_OK; @@ -419,8 +419,8 @@ bool Plugin::unload(color_ostream &con) // cleanup... plugin_is_enabled = 0; plugin_onupdate = 0; - plugin_save = 0; - plugin_load = 0; + plugin_save_data = 0; + plugin_load_data = 0; reset_lua(); parent->unregisterCommands(this); commands.clear(); @@ -585,10 +585,10 @@ command_result Plugin::save_data(color_ostream &out) { command_result cr = CR_NOT_IMPLEMENTED; access->lock_add(); - if(state == PS_LOADED && plugin_save) + if(state == PS_LOADED && plugin_save_data) { - cr = plugin_save(out); - Lua::Core::Reset(out, "plugin_save"); + cr = plugin_save_data(out); + Lua::Core::Reset(out, "plugin_save_data"); } access->lock_sub(); return cr; @@ -598,10 +598,10 @@ command_result Plugin::load_data(color_ostream &out) { command_result cr = CR_NOT_IMPLEMENTED; access->lock_add(); - if(state == PS_LOADED && plugin_load) + if(state == PS_LOADED && plugin_load_data) { - cr = plugin_load(out); - Lua::Core::Reset(out, "plugin_load"); + cr = plugin_load_data(out); + Lua::Core::Reset(out, "plugin_load_data"); } access->lock_sub(); return cr; @@ -1051,7 +1051,7 @@ void PluginManager::unregisterCommands( Plugin * p ) cmdlist_mutex->unlock(); } -void PluginManager::doSave(color_ostream &out) +void PluginManager::doSaveData(color_ostream &out) { for (auto it = begin(); it != end(); ++it) { @@ -1062,7 +1062,7 @@ void PluginManager::doSave(color_ostream &out) } } -void PluginManager::doLoad(color_ostream &out) +void PluginManager::doLoadData(color_ostream &out) { for (auto it = begin(); it != end(); ++it) { diff --git a/library/include/Core.h b/library/include/Core.h index a5fca570a..a0c791c2e 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -222,8 +222,8 @@ namespace DFHack void onUpdate(color_ostream &out); void onStateChange(color_ostream &out, state_change_event event); void handleLoadAndUnloadScripts(color_ostream &out, state_change_event event); - void doSave(color_ostream &out); - void doLoad(color_ostream &out); + void doSaveData(color_ostream &out); + void doLoadData(color_ostream &out); Core(Core const&); // Don't Implement void operator=(Core const&); // Don't implement diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 8cb75cff1..8f73c8ec2 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -240,8 +240,8 @@ namespace DFHack command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); command_result (*plugin_eval_ruby)(color_ostream &, const char*); - command_result (*plugin_save)(color_ostream &); - command_result (*plugin_load)(color_ostream &); + command_result (*plugin_save_data)(color_ostream &); + command_result (*plugin_load_data)(color_ostream &); }; class DFHACK_EXPORT PluginManager { @@ -255,8 +255,8 @@ namespace DFHack void OnStateChange(color_ostream &out, state_change_event event); void registerCommands( Plugin * p ); void unregisterCommands( Plugin * p ); - void doSave(color_ostream &out); - void doLoad(color_ostream &out); + void doSaveData(color_ostream &out); + void doLoadData(color_ostream &out); // PUBLIC METHODS public: // list names of all plugins present in hack/plugins diff --git a/plugins/skeleton/skeleton.cpp b/plugins/skeleton/skeleton.cpp index 82d988a2e..c06ca26cb 100644 --- a/plugins/skeleton/skeleton.cpp +++ b/plugins/skeleton/skeleton.cpp @@ -90,18 +90,18 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) */ // If you need to save or load world-specific data, define these functions. -// plugin_save is called when the game might be about to save the world, -// and plugin_load is called whenever a new world is loaded. If the plugin -// is loaded or unloaded while a world is active, plugin_save or plugin_load -// will be called immediately. +// plugin_save_data is called when the game might be about to save the world, +// and plugin_load_data is called whenever a new world is loaded. If the plugin +// is loaded or unloaded while a world is active, plugin_save_data or +// plugin_load_data will be called immediately. /* -DFhackCExport command_result plugin_save( color_ostream &out ) +DFhackCExport command_result plugin_save_data (color_ostream &out) { // Call functions in the Persistence module here. return CR_OK; } -DFhackCExport command_result plugin_load( color_ostream &out ) +DFhackCExport command_result plugin_load_data (color_ostream &out) { // Call functions in the Persistence module here. return CR_OK; From cde8ffd0fd0e6a1c0a7d09ed1a76389555870238 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 6 Jun 2019 20:56:47 +0200 Subject: [PATCH 17/44] Fixed materials vector length determination --- plugins/embark-assistant/embark-assistant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index cfd44a30b..13d202b7e 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -224,7 +224,7 @@ command_result embark_assistant(color_ostream &out, std::vector & // 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; + 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 From f09f3a2d253e36e93ef68262fb3e35fe352cd658 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 6 Jun 2019 20:57:21 +0200 Subject: [PATCH 18/44] Prettified by adding a blank --- plugins/embark-assistant/survey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index f19f3cd4a..00f60d51d 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -203,7 +203,7 @@ namespace embark_assist { } for (uint16_t m = 0; m < state->coals.size(); m++) { - if (vein== state->coals[m]) { + if (vein == state->coals[m]) { geo_summary->at(i).coal_absent = false; break; } From a72d25475b632ab39cb2acb2b1f4aa718067b104 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 6 Jun 2019 21:18:54 +0200 Subject: [PATCH 19/44] Added Enbark-Assistant entry --- docs/changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 17c766f75..61eed0e0e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ================================================================================ # Future +## Fixes +- `embark-assistant`: fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). + ## Internals - Fixed some OpenGL build issues with `stonesense` From b1a544b99c10cc953db33dc77048a5a033800775 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 20 Jun 2019 15:48:51 +0200 Subject: [PATCH 20/44] Added embark-assistant bug fix note --- docs/changelog.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 61eed0e0e..c003ef936 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,7 +38,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## Fixes -- `embark-assistant`: fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). +- `embark-assistant`: + - fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). + - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that + aren't (like DF does currently). ## Internals - Fixed some OpenGL build issues with `stonesense` From 488f1cd8a1f1babca570393858c16ac9ced01862 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 20 Jun 2019 15:50:11 +0200 Subject: [PATCH 21/44] Fixed bug causing minerals below magma sea being reported --- plugins/embark-assistant/survey.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index 00f60d51d..9df34bc86 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -667,7 +667,7 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data *geo_summary, embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::mid_level_tiles *mlt) { - // color_ostream_proxy out(Core::getInstance().getConsole()); +// color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); int16_t x = screen->location.region_pos.x; int16_t y = screen->location.region_pos.y; @@ -853,12 +853,12 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data if (layer->type == df::geo_layer_type::SOIL || layer->type == df::geo_layer_type::SOIL_SAND) { int16_t size = layer->top_height - layer->bottom_height - 1; - // Comment copied from prospector.cpp(like the logic)... + // Comment copied from prospector.cpp (like the logic)... // This is to replicate the behavior of a probable bug in the - // map generation code : if a layer is partially eroded, the - // removed levels are in fact transferred to the layer below, + // map generation code : if a layer is partially eroded, the + // removed levels are in fact transferred to the layer below, // because unlike the case of removing the whole layer, the code - // does not execute a loop to shift the lower part of the stack up. + // does not execute a loop to shift the lower part of the stack up. if (size > soil_erosion) { cur_shift = cur_shift - soil_erosion; } @@ -880,6 +880,8 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data } if (top_z >= bottom_z) { + last_bottom = bottom_z; + mlt->at(i).at(k).minerals[layer->mat_index] = true; end_check_m = static_cast(world->raws.inorganics[layer->mat_index]->metal_ore.mat_index.size()); From 9c3edac499701a2fc0f973f5275ad0ae8d9ab024 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Thu, 20 Jun 2019 15:59:08 +0200 Subject: [PATCH 22/44] Added embark-assistant bug fix note --- docs/changelog.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c003ef936..b29ee59f6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,8 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `embark-assistant`: - fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). - - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that - aren't (like DF does currently). + - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently). ## Internals - Fixed some OpenGL build issues with `stonesense` From aee7e2e4817ab4497e1896e3fcd9d6f7f93f1777 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:34:21 +0200 Subject: [PATCH 23/44] Mentioned Enbark-Assistant Flat detection modification --- docs/changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b29ee59f6..12faed5a2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently). +## Misc Improvements +- `embark-assistant`: Changed Flat detection to allow search for verified flat terrain by checking surrounding Mid Level Tiles or just mostly/probably flat terrain by checking embark tiles only + ## Internals - Fixed some OpenGL build issues with `stonesense` From b5e38451de86c6cacce8b2d4580fe24c282f7279 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:35:11 +0200 Subject: [PATCH 24/44] Modified Flat detection options --- plugins/embark-assistant/defs.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/embark-assistant/defs.h b/plugins/embark-assistant/defs.h index f7a9ed680..46ef75041 100644 --- a/plugins/embark-assistant/defs.h +++ b/plugins/embark-assistant/defs.h @@ -23,6 +23,12 @@ namespace embark_assist { Major }; + enum class flatnesses { + Flat_Verified, + Mostly_Flat, + Uneven + }; + struct mid_level_tile { bool aquifer = false; bool clay = false; @@ -111,7 +117,7 @@ namespace embark_assist { bool aquifer_full; uint8_t min_soil; uint8_t max_soil; - bool flat; + flatnesses flatness; bool waterfall; bool clay; bool sand; @@ -174,6 +180,13 @@ namespace embark_assist { Major }; + enum class flatness_ranges : int8_t { + NA = -1, + Flat_Verified, + Mostly_Flat, + Uneven + }; + // For possible future use. That's the level of data actually collected. // enum class adamantine_ranges : int8_t { // NA = -1, @@ -253,7 +266,7 @@ namespace embark_assist { river_ranges min_river; river_ranges max_river; yes_no_ranges waterfall; - yes_no_ranges flat; + flatness_ranges flatness; present_absent_ranges clay; present_absent_ranges sand; present_absent_ranges flux; From 3e97643b37821ca6bc9d218106c1ba0467cb8720 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:35:28 +0200 Subject: [PATCH 25/44] Modified Flat detection options --- plugins/embark-assistant/finder_ui.cpp | 47 +++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 5c7cc9046..59aa0783a 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -40,7 +40,7 @@ namespace embark_assist { min_river, max_river, waterfall, - flat, + flatness, clay, sand, flux, @@ -246,7 +246,7 @@ namespace embark_assist { fclose(infile); - // Checking done. No do the work. + // Checking done. Now do the work. infile = fopen(profile_file_name, "r"); i = first_fields; @@ -453,7 +453,6 @@ namespace embark_assist { break; case fields::waterfall: - case fields::flat: case fields::blood_rain: { embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA; @@ -482,6 +481,38 @@ namespace embark_assist { break; + case fields::flatness: + { + embark_assist::defs::flatness_ranges k = embark_assist::defs::flatness_ranges::NA; + while (true) { + switch (k) { + case embark_assist::defs::flatness_ranges::NA: + element->list.push_back({ "N/A", static_cast(k) }); + break; + + case embark_assist::defs::flatness_ranges::Flat_Verified: + element->list.push_back({ "Flat Verified", static_cast(k) }); + break; + + case embark_assist::defs::flatness_ranges::Mostly_Flat: + element->list.push_back({ "Mostly Flat", static_cast(k) }); + break; + + case embark_assist::defs::flatness_ranges::Uneven: + element->list.push_back({ "Uneven", static_cast(k) }); + break; + } + + if (k == embark_assist::defs::flatness_ranges::Uneven) { + break; + } + + k = static_cast (static_cast(k) + 1); + } + } + + break; + case fields::soil_min_everywhere: { embark_assist::defs::all_present_ranges k = embark_assist::defs::all_present_ranges::All; @@ -959,8 +990,8 @@ namespace embark_assist { state->finder_list.push_back({ "Waterfall", static_cast(i) }); break; - case fields::flat: - state->finder_list.push_back({ "Flat", static_cast(i) }); + case fields::flatness: + state->finder_list.push_back({ "Flatness", static_cast(i) }); break; case fields::soil_min_everywhere: @@ -1189,9 +1220,9 @@ namespace embark_assist { static_cast(state->ui[static_cast(i)]->current_value); break; - case fields::flat: - finder.flat = - static_cast(state->ui[static_cast(i)]->current_value); + case fields::flatness: + finder.flatness = + static_cast(state->ui[static_cast(i)]->current_value); break; case fields::soil_min_everywhere: From 3f7d11d1c754422381c199e1015d362d1f856575 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:35:48 +0200 Subject: [PATCH 26/44] Modified Flat detection options --- plugins/embark-assistant/help_ui.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index e92ae67c0..3d281b145 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -163,6 +163,9 @@ namespace embark_assist{ 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(" 'Mostly Flat' = Condition above met. 'Flat Verified' = All surrounding tiles"); + help_text.push_back(" also satisfy condition so no biome spill over may result in an"); + help_text.push_back(" elevation difference."); 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."); @@ -252,7 +255,7 @@ namespace embark_assist{ 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("- Thralling is determined by whether material interactions causes"); help_text.push_back(" blinking, which the author believes is one of 4 thralling changes."); help_text.push_back("- The geo information is gathered by code which is essentially a"); help_text.push_back(" copy of parts of prospector's code adapted for this plugin."); @@ -263,7 +266,13 @@ namespace embark_assist{ help_text.push_back(" reaching caverns that have been removed at world gen to fail to be"); help_text.push_back(" generated at all. It's likely this bug also affects magma pools."); help_text.push_back(" This plugin does not address this but scripts can correct it."); - help_text.push_back("Version 0.8 2018-12-04"); + help_text.push_back("- 'Flat Verified' vs 'Mostly Flat': There's no known way to detect"); + help_text.push_back(" if an adjacent Mid Level Tile's biome 'spills over' into a tile."); + help_text.push_back(" 'Flat Verified' means neighbors have the same elevation so spill overs"); + help_text.push_back(" don't matter. 'Mostly Flat' means spill overs may ruin a completely"); + help_text.push_back(" level embark, but might not. Can be used to 'rescue' a world where"); + help_text.push_back(" a 'Flat Verified' match failed."); + help_text.push_back("Version 0.9 2019-06-23"); break; } From 8259243d2aaa3b65add4b34c298cfdd5de58ee4e Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:35:59 +0200 Subject: [PATCH 27/44] Modified Flat detection options --- plugins/embark-assistant/matcher.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/plugins/embark-assistant/matcher.cpp b/plugins/embark-assistant/matcher.cpp index 7b3d385cb..26463f011 100644 --- a/plugins/embark-assistant/matcher.cpp +++ b/plugins/embark-assistant/matcher.cpp @@ -83,6 +83,12 @@ namespace embark_assist { for (uint8_t i = 0; i <= ENUM_LAST_ITEM(world_region_type); i++) region_types[i] = false; + if (finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified && + (start_x == 0 || + start_x + finder->x_dim == 16 || + start_y == 0 || + start_y + finder->y_dim == 16)) return false; + for (uint16_t i = start_x; i < start_x + finder->x_dim; i++) { for (uint16_t k = start_y; k < start_y + finder->y_dim; k++) { @@ -151,8 +157,9 @@ namespace embark_assist { river_elevation = mlt->at(i).at(k).river_elevation; } - // Flat - if (finder->flat == embark_assist::defs::yes_no_ranges::Yes && + // Flatness + if ((finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified || + finder->flatness == embark_assist::defs::flatness_ranges::Mostly_Flat) && elevation != mlt->at(i).at(k).elevation) return false; if (elevation != mlt->at(i).at(k).elevation) uneven = true; @@ -330,8 +337,20 @@ namespace embark_assist { if (!river_found && finder->min_river > embark_assist::defs::river_ranges::None) return false; if (finder->waterfall == embark_assist::defs::yes_no_ranges::Yes && !waterfall_found) return false; - // Flat - if (!uneven && finder->flat == embark_assist::defs::yes_no_ranges::No) return false; + // Flatness + if (!uneven && finder->flatness == embark_assist::defs::flatness_ranges::Uneven) return false; + + if (finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified) { + for (uint16_t i = start_x - 1; i < start_x + finder->x_dim + 1; i++) { + if (elevation != mlt->at(i).at(start_y - 1).elevation || + elevation != mlt->at(i).at(start_y + finder->y_dim).elevation) return false; + } + + for (uint16_t k = start_y; k < start_y + finder->y_dim; k++) { + if (elevation != mlt->at(start_x - 1).at(k).elevation || + elevation != mlt->at(start_x + finder->x_dim).at(k).elevation) return false; + } + } // Clay if (finder->clay == embark_assist::defs::present_absent_ranges::Present && !clay_found) return false; From 3139d0d3c73115eaec57d90d19b33af5792d3d9c Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:36:10 +0200 Subject: [PATCH 28/44] Modified Flat detection options --- plugins/embark-assistant/overlay.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/embark-assistant/overlay.cpp b/plugins/embark-assistant/overlay.cpp index c25859ccb..cf25eddf3 100644 --- a/plugins/embark-assistant/overlay.cpp +++ b/plugins/embark-assistant/overlay.cpp @@ -345,8 +345,12 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in 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->flatness == embark_assist::defs::flatnesses::Flat_Verified) { + state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Flat Verified" }); + } + else if (site_info->flatness == embark_assist::defs::flatnesses::Mostly_Flat) + { + state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Mostly Flat" }); } if (site_info->aquifer) { From 716fe903122ded39b3aa75997cacb917a6727f6a Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 23 Jun 2019 18:36:18 +0200 Subject: [PATCH 29/44] Modified Flat detection options --- plugins/embark-assistant/survey.cpp | 39 ++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index 9df34bc86..d79396118 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -1197,7 +1197,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * embark_assist::defs::site_infos *site_info, bool use_cache) { - // color_ostream_proxy out(Core::getInstance().getConsole()); + //color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); int16_t elevation = 0; uint16_t x = screen->location.region_pos.x; @@ -1207,6 +1207,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * std::vector metals(state->max_inorganic); std::vector economics(state->max_inorganic); std::vector minerals(state->max_inorganic); + bool flatness_verification_failed; if (!use_cache) { // For some reason DF scrambles these values on world tile movements (at least in Lua...). state->local_min_x = screen->location.embark_pos_min.x; @@ -1222,7 +1223,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * site_info->aquifer_full = true; site_info->min_soil = 10; site_info->max_soil = 0; - site_info->flat = true; + site_info->flatness = embark_assist::defs::flatnesses::Mostly_Flat; site_info->waterfall = false; site_info->clay = false; site_info->sand = false; @@ -1253,7 +1254,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * elevation = mlt->at(i).at(k).elevation; } else if (elevation != mlt->at(i).at(k).elevation) { - site_info->flat = false; + site_info->flatness = embark_assist::defs::flatnesses::Uneven; } if (mlt->at(i).at(k).river_present) { @@ -1291,6 +1292,38 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * } } } + + if (site_info->flatness == embark_assist::defs::flatnesses::Mostly_Flat && + state->local_min_x > 0 && + state->local_max_x < 15 && + state->local_min_y > 0 && + state->local_max_y < 15) + { + flatness_verification_failed = false; + + for (uint8_t i = state->local_min_x - 1; i <= state->local_max_x + 1; i++) { + if (mlt->at(i).at(state->local_min_y - 1).elevation != elevation || + mlt->at(i).at(state->local_max_y + 1).elevation != elevation) { + flatness_verification_failed = true; + break; + } + } + + if (!flatness_verification_failed) { + for (uint8_t k = state->local_min_y; k <= state->local_max_y; k++) { + if (mlt->at(state->local_min_x - 1).at(k).elevation != elevation || + mlt->at(state->local_max_x + 1).at(k).elevation != elevation) { + flatness_verification_failed = true; + break; + } + } + } + + if (!flatness_verification_failed) { + site_info->flatness = embark_assist::defs::flatnesses::Flat_Verified; + } + } + for (uint16_t l = 0; l < state->max_inorganic; l++) { if (metals[l]) { site_info->metals.push_back(l); From fd722332f83660d090be1756af526a2868c21b04 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:27:39 +0200 Subject: [PATCH 30/44] Embark-Assistant Waterfall detection change added --- docs/changelog.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 12faed5a2..88b0d59d3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,7 +43,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently). ## Misc Improvements -- `embark-assistant`: Changed Flat detection to allow search for verified flat terrain by checking surrounding Mid Level Tiles or just mostly/probably flat terrain by checking embark tiles only +- `embark-assistant`: + - Changed Flat detection to allow search for verified flat terrain by checking surrounding Mid Level Tiles or just mostly/probably flat terrain by checking embark tiles only. + - Changed Waterfall detection to look for level drop rather than just presence. ## Internals - Fixed some OpenGL build issues with `stonesense` From 092c9f965b3d52bef3fed1b795c61df4ed50e5e9 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:28:40 +0200 Subject: [PATCH 31/44] Waterfall detection changed --- plugins/embark-assistant/defs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/embark-assistant/defs.h b/plugins/embark-assistant/defs.h index 46ef75041..da03c1d7e 100644 --- a/plugins/embark-assistant/defs.h +++ b/plugins/embark-assistant/defs.h @@ -61,7 +61,7 @@ namespace embark_assist { uint16_t coal_count = 0; uint8_t min_region_soil = 10; uint8_t max_region_soil = 0; - bool waterfall = false; + uint8_t max_waterfall = 0; 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 @@ -118,7 +118,7 @@ namespace embark_assist { uint8_t min_soil; uint8_t max_soil; flatnesses flatness; - bool waterfall; + uint8_t max_waterfall; bool clay; bool sand; bool flux; @@ -265,7 +265,7 @@ namespace embark_assist { aquifer_ranges aquifer; river_ranges min_river; river_ranges max_river; - yes_no_ranges waterfall; + int8_t min_waterfall; // N/A(-1), Absent, 1-9 flatness_ranges flatness; present_absent_ranges clay; present_absent_ranges sand; From 242f6fbc383e005bde7bf82b65af9fcdd21090b3 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:28:54 +0200 Subject: [PATCH 32/44] Waterfall detection changed --- plugins/embark-assistant/embark-assistant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index 13d202b7e..972da12f9 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -253,7 +253,7 @@ command_result embark_assistant(color_ostream &out, std::vector & 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].max_waterfall = 0; embark_assist::main::state->survey_results[i][k].river_size = embark_assist::defs::river_sizes::None; for (uint8_t l = 1; l < 10; l++) { From fae5e894ae9a2fcdd749cf27dbe65cf504fde491 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:29:07 +0200 Subject: [PATCH 33/44] Waterfall detection changed --- plugins/embark-assistant/finder_ui.cpp | 27 +++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 59aa0783a..6e710f3bd 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -39,7 +39,7 @@ namespace embark_assist { aquifer, min_river, max_river, - waterfall, + min_waterfall, flatness, clay, sand, @@ -452,7 +452,21 @@ namespace embark_assist { break; - case fields::waterfall: + case fields::min_waterfall: + for (int16_t k = -1; k <= 9; k++) { + if (k == -1) { + element->list.push_back({ "N/A", k }); + } + else if (k == 0) { + element->list.push_back({ "Absent", k }); + } + else { + element->list.push_back({ std::to_string(k), k }); + } + } + + break; + case fields::blood_rain: { embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA; @@ -986,8 +1000,8 @@ namespace embark_assist { state->finder_list.push_back({ "Max River", static_cast(i) }); break; - case fields::waterfall: - state->finder_list.push_back({ "Waterfall", static_cast(i) }); + case fields::min_waterfall: + state->finder_list.push_back({ "Min Waterfall Drop", static_cast(i) }); break; case fields::flatness: @@ -1215,9 +1229,8 @@ namespace embark_assist { static_cast(state->ui[static_cast(i)]->current_value); break; - case fields::waterfall: - finder.waterfall = - static_cast(state->ui[static_cast(i)]->current_value); + case fields::min_waterfall: + finder.min_waterfall = state->ui[static_cast(i)]->current_value; break; case fields::flatness: From bcb62697c94ed9a47b6cfa98ca21aa9669fa0f79 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:29:19 +0200 Subject: [PATCH 34/44] Waterfall detection changed --- plugins/embark-assistant/matcher.cpp | 51 ++++++++++++++++------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/plugins/embark-assistant/matcher.cpp b/plugins/embark-assistant/matcher.cpp index 26463f011..3b483ef18 100644 --- a/plugins/embark-assistant/matcher.cpp +++ b/plugins/embark-assistant/matcher.cpp @@ -40,8 +40,7 @@ namespace embark_assist { bool evilness_found[3] = { false, false, false }; uint16_t aquifer_count = 0; bool river_found = false; - bool waterfall_found = false; - uint16_t river_elevation = 0xffff; + uint8_t max_waterfall = 0; uint16_t elevation = mlt->at(start_x).at(start_y).elevation; bool clay_found = false; bool sand_found = false; @@ -149,12 +148,23 @@ namespace embark_assist { if (finder->max_river != embark_assist::defs::river_ranges::NA && finder->max_river < static_cast(survey_results->at(x).at(y).river_size)) return false; - if (river_found && river_elevation != mlt->at(i).at(k).river_elevation) { - if (finder->waterfall == embark_assist::defs::yes_no_ranges::No) return false; - waterfall_found = true; + if (i < start_x + finder->x_dim - 2 && + mlt->at(i + 1).at(k).river_present && + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > max_waterfall) { + if (finder->min_waterfall == 0) return false; // 0 = Absent + max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation); } + + if (k < start_y + finder->y_dim - 2 && + mlt->at(i).at(k + 1).river_present && + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > max_waterfall) { + if (finder->min_waterfall == 0) return false; // 0 = Absent + max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation); + } + river_found = true; - river_elevation = mlt->at(i).at(k).river_elevation; } // Flatness @@ -335,7 +345,7 @@ namespace embark_assist { // River & Waterfall if (!river_found && finder->min_river > embark_assist::defs::river_ranges::None) return false; - if (finder->waterfall == embark_assist::defs::yes_no_ranges::Yes && !waterfall_found) return false; + if (max_waterfall < finder->min_waterfall) return false; // N/A = -1 is always smaller, so no additional check needed. // Flatness if (!uneven && finder->flatness == embark_assist::defs::flatness_ranges::Uneven) return false; @@ -576,19 +586,10 @@ namespace embark_assist { } // Waterfall - switch (finder->waterfall) { - case embark_assist::defs::yes_no_ranges::NA: - break; // No restriction - - case embark_assist::defs::yes_no_ranges::Yes: - if (!tile->waterfall) return false; - break; - - case embark_assist::defs::yes_no_ranges::No: - if (tile->waterfall && - embark_size == 256) return false; - break; - } + if (finder->min_waterfall > tile->max_waterfall) return false; // N/A = -1 is always smaller + if (finder->min_waterfall == 0 && // Absent + embark_size == 256 && + tile->max_waterfall > 0) return false; // Flat. No world tile checks. Need to look at the details @@ -1060,7 +1061,7 @@ namespace embark_assist { } // Waterfall - if (finder->waterfall == embark_assist::defs::yes_no_ranges::Yes && + if (finder->min_waterfall > 0 && tile->river_size == embark_assist::defs::river_sizes::None) return false; // Flat. No world tile checks. Need to look at the details @@ -1548,7 +1549,7 @@ uint16_t embark_assist::matcher::find(embark_assist::defs::match_iterators *iter return 0; } - if (iterator->finder.waterfall == embark_assist::defs::yes_no_ranges::Yes && + if (iterator->finder.min_waterfall > 0 && iterator->finder.max_river == embark_assist::defs::river_ranges::None) { out.printerr("matcher::find: Will never find any waterfalls with None as max river\n"); return 0; @@ -1560,6 +1561,12 @@ uint16_t embark_assist::matcher::find(embark_assist::defs::match_iterators *iter return 0; } + if (iterator->finder.flatness == embark_assist::defs::flatness_ranges::Flat_Verified && + (iterator->finder.x_dim > 14 || + iterator->finder.y_dim > 14)) { + out.printerr("matcher::find: Can never verify flatness without border around embark\n"); + } + if (iterator->finder.spire_count_max < iterator->finder.spire_count_min && iterator->finder.spire_count_max != -1) { out.printerr("matcher::find: Will never find any matches with max spires < min spires\n"); From 134bcf09a90fd02c26bfa23db9cb36051dae0ef5 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:29:53 +0200 Subject: [PATCH 35/44] Waterfall detection changed. Aquifer display changed --- plugins/embark-assistant/overlay.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/embark-assistant/overlay.cpp b/plugins/embark-assistant/overlay.cpp index cf25eddf3..494dbcd4a 100644 --- a/plugins/embark-assistant/overlay.cpp +++ b/plugins/embark-assistant/overlay.cpp @@ -355,16 +355,16 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in if (site_info->aquifer) { if (site_info->aquifer_full) { - state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Aquifer" }); + state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Full Aquifer" }); } else { - state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Aquifer" }); + state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Part. Aquifer" }); } } - if (site_info->waterfall) { - state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Waterfall" }); + if (site_info->max_waterfall > 0) { + state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Waterfall " + std::to_string(site_info->max_waterfall) }); } if (site_info->flux) { From 53cf1136fb59f7bbcb55f60c92d169e31853fced Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Mon, 1 Jul 2019 13:30:16 +0200 Subject: [PATCH 36/44] Waterfall detection changed --- plugins/embark-assistant/survey.cpp | 50 ++++++++++++++++------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index d79396118..42a766674 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -567,7 +567,7 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat results.coal_count = 0; results.min_region_soil = 10; results.max_region_soil = 0; - results.waterfall = false; + results.max_waterfall = 0; results.savagery_count[0] = 0; results.savagery_count[1] = 0; results.savagery_count[2] = 0; @@ -976,9 +976,6 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data survey_results->at(x).at(y).evilness_count[1] = 0; survey_results->at(x).at(y).evilness_count[2] = 0; - bool river_elevation_found = false; - int16_t river_elevation = 0; - for (uint8_t i = 0; i < 16; i++) { for (uint8_t k = 0; k < 16; k++) { if (mlt->at(i).at(k).aquifer) { survey_results->at(x).at(y).aquifer_count++; } @@ -996,15 +993,20 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data } if (mlt->at(i).at(k).river_present) { - if (river_elevation_found) { - if (mlt->at(i).at(k).river_elevation != river_elevation) - { - survey_results->at(x).at(y).waterfall = true; - } + if (i < 15 && + mlt->at(i + 1).at(k).river_present && + abs (mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > + survey_results->at(x).at(y).max_waterfall) { + survey_results->at(x).at(y).max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation); } - else { - river_elevation_found = true; - river_elevation = mlt->at(i).at(k).river_elevation; + + if (k < 15 && + mlt->at(i).at(k + 1).river_present && + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > + survey_results->at(x).at(y).max_waterfall) { + survey_results->at(x).at(y).max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation); } } @@ -1202,8 +1204,6 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * int16_t elevation = 0; uint16_t x = screen->location.region_pos.x; uint16_t y = screen->location.region_pos.y; - bool river_found = false; - int16_t river_elevation = 0; std::vector metals(state->max_inorganic); std::vector economics(state->max_inorganic); std::vector minerals(state->max_inorganic); @@ -1224,7 +1224,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * site_info->min_soil = 10; site_info->max_soil = 0; site_info->flatness = embark_assist::defs::flatnesses::Mostly_Flat; - site_info->waterfall = false; + site_info->max_waterfall = 0; site_info->clay = false; site_info->sand = false; site_info->flux = false; @@ -1258,14 +1258,20 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * } if (mlt->at(i).at(k).river_present) { - if (river_found) { - if (river_elevation != mlt->at(i).at(k).river_elevation) { - site_info->waterfall = true; - } + if (i < 15 && + mlt->at(i + 1).at(k).river_present && + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > + site_info->max_waterfall) { + site_info->max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation); } - else { - river_elevation = mlt->at(i).at(k).river_elevation; - river_found = true; + + if (k < 15 && + mlt->at(i).at(k + 1).river_present && + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > + site_info->max_waterfall) { + site_info->max_waterfall = + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation); } } From 8248832cbcb9e8993ce0e559b44fb47c9c43d43a Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Fri, 12 Jul 2019 22:09:03 +0200 Subject: [PATCH 37/44] Added detection and use of incursions into embark tiles --- build/win64/install-debug.bat | 4 +- docs/changelog.txt | 4 +- plugins/embark-assistant/defs.h | 34 +- plugins/embark-assistant/embark-assistant.cpp | 9 +- plugins/embark-assistant/finder_ui.cpp | 45 +- plugins/embark-assistant/help_ui.cpp | 136 +- plugins/embark-assistant/matcher.cpp | 993 ++++++++++++--- plugins/embark-assistant/overlay.cpp | 61 +- plugins/embark-assistant/survey.cpp | 1117 ++++++++++++++++- plugins/embark-assistant/survey.h | 43 + 10 files changed, 2141 insertions(+), 305 deletions(-) diff --git a/build/win64/install-debug.bat b/build/win64/install-debug.bat index 34668945c..64b5ae850 100644 --- a/build/win64/install-debug.bat +++ b/build/win64/install-debug.bat @@ -1,4 +1,4 @@ -call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +call "D:\Program (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 cd VC2015 msbuild /m /p:Platform=x64 /p:Configuration=RelWithDebInfo INSTALL.vcxproj -cd .. +cd .. \ No newline at end of file diff --git a/docs/changelog.txt b/docs/changelog.txt index 88b0d59d3..69436490c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,8 +44,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `embark-assistant`: - - Changed Flat detection to allow search for verified flat terrain by checking surrounding Mid Level Tiles or just mostly/probably flat terrain by checking embark tiles only. - Changed Waterfall detection to look for level drop rather than just presence. + - Changed matching to take incursions, i.e. parts of other biomes, into consideration when evaluating tiles. This allows for e.g. finding multiple biomes on single tile embarks. + - Changed overlay display to show when incursion surveying is incomplete. + - Changed overlay display to show Evil Weather. ## Internals - Fixed some OpenGL build issues with `stonesense` diff --git a/plugins/embark-assistant/defs.h b/plugins/embark-assistant/defs.h index da03c1d7e..9d1c8e888 100644 --- a/plugins/embark-assistant/defs.h +++ b/plugins/embark-assistant/defs.h @@ -3,6 +3,7 @@ #include #include #include +#include "df/world_region_type.h" using namespace std; using std::array; @@ -23,12 +24,6 @@ namespace embark_assist { Major }; - enum class flatnesses { - Flat_Verified, - Mostly_Flat, - Uneven - }; - struct mid_level_tile { bool aquifer = false; bool clay = false; @@ -88,6 +83,16 @@ namespace embark_assist { std::vector metals; std::vector economics; std::vector minerals; + mid_level_tile north_row[16]; + mid_level_tile south_row[16]; + mid_level_tile west_column[16]; + mid_level_tile east_column[16]; + uint8_t north_corner_selection[16]; // 0 - 3. For some reason DF stores everything needed for incursion + uint8_t west_corner_selection[16]; // detection in 17:th row/colum data in the region details except + // this info, so we have to go to neighboring world tiles to fetch it. + df::world_region_type region_type[16][16]; // Required for incursion override detection. We could store only the + // edges, but storing it for every tile allows for a unified fetching + // logic. }; struct geo_datum { @@ -113,16 +118,22 @@ namespace embark_assist { }; struct site_infos { + bool incursions_processed; bool aquifer; bool aquifer_full; uint8_t min_soil; uint8_t max_soil; - flatnesses flatness; + bool flat; uint8_t max_waterfall; bool clay; bool sand; bool flux; bool coal; + bool blood_rain; + bool permanent_syndrome_rain; + bool temporary_syndrome_rain; + bool reanimating; + bool thralling; std::vector metals; std::vector economics; std::vector minerals; @@ -180,13 +191,6 @@ namespace embark_assist { Major }; - enum class flatness_ranges : int8_t { - NA = -1, - Flat_Verified, - Mostly_Flat, - Uneven - }; - // For possible future use. That's the level of data actually collected. // enum class adamantine_ranges : int8_t { // NA = -1, @@ -266,7 +270,7 @@ namespace embark_assist { river_ranges min_river; river_ranges max_river; int8_t min_waterfall; // N/A(-1), Absent, 1-9 - flatness_ranges flatness; + yes_no_ranges flat; present_absent_ranges clay; present_absent_ranges sand; present_absent_ranges flux; diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index 972da12f9..22dd21298 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -62,7 +62,10 @@ namespace embark_assist { &state->survey_results, &mlt); - embark_assist::survey::survey_embark(&mlt, &state->site_info, false); + embark_assist::survey::survey_embark(&mlt, + &state->survey_results, + &state->site_info, + false); embark_assist::overlay::set_embark(&state->site_info); embark_assist::survey::survey_region_sites(&state->region_sites); @@ -272,7 +275,7 @@ command_result embark_assistant(color_ostream &out, std::vector & } 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::main::state->survey_results[i][k].minerals.resize(embark_assist::main::state->max_inorganic); } } @@ -291,7 +294,7 @@ command_result embark_assistant(color_ostream &out, std::vector & 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::survey::survey_embark(&mlt, &embark_assist::main::state->survey_results, &embark_assist::main::state->site_info, false); embark_assist::overlay::set_embark(&embark_assist::main::state->site_info); return CR_OK; diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 6e710f3bd..80c61dc6e 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -40,7 +40,7 @@ namespace embark_assist { min_river, max_river, min_waterfall, - flatness, + flat, clay, sand, flux, @@ -468,6 +468,7 @@ namespace embark_assist { break; case fields::blood_rain: + case fields::flat: { embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA; while (true) { @@ -495,38 +496,6 @@ namespace embark_assist { break; - case fields::flatness: - { - embark_assist::defs::flatness_ranges k = embark_assist::defs::flatness_ranges::NA; - while (true) { - switch (k) { - case embark_assist::defs::flatness_ranges::NA: - element->list.push_back({ "N/A", static_cast(k) }); - break; - - case embark_assist::defs::flatness_ranges::Flat_Verified: - element->list.push_back({ "Flat Verified", static_cast(k) }); - break; - - case embark_assist::defs::flatness_ranges::Mostly_Flat: - element->list.push_back({ "Mostly Flat", static_cast(k) }); - break; - - case embark_assist::defs::flatness_ranges::Uneven: - element->list.push_back({ "Uneven", static_cast(k) }); - break; - } - - if (k == embark_assist::defs::flatness_ranges::Uneven) { - break; - } - - k = static_cast (static_cast(k) + 1); - } - } - - break; - case fields::soil_min_everywhere: { embark_assist::defs::all_present_ranges k = embark_assist::defs::all_present_ranges::All; @@ -1004,8 +973,8 @@ namespace embark_assist { state->finder_list.push_back({ "Min Waterfall Drop", static_cast(i) }); break; - case fields::flatness: - state->finder_list.push_back({ "Flatness", static_cast(i) }); + case fields::flat: + state->finder_list.push_back({ "Flat", static_cast(i) }); break; case fields::soil_min_everywhere: @@ -1233,9 +1202,9 @@ namespace embark_assist { finder.min_waterfall = state->ui[static_cast(i)]->current_value; break; - case fields::flatness: - finder.flatness = - static_cast(state->ui[static_cast(i)]->current_value); + case fields::flat: + finder.flat = + static_cast(state->ui[static_cast(i)]->current_value); break; case fields::soil_min_everywhere: diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index 3d281b145..ef1bc2b39 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -18,7 +18,8 @@ namespace embark_assist{ Intro, General, Finder, - Caveats + Caveats_1, + Caveats_2 }; class ViewscreenHelpUi : public dfhack_viewscreen @@ -56,10 +57,14 @@ namespace embark_assist{ break; case pages::Finder: - current_page = pages::Caveats; + current_page = pages::Caveats_1; break; - case pages::Caveats: + case pages::Caveats_1: + current_page = pages::Caveats_2; + break; + + case pages::Caveats_2: current_page = pages::Intro; break; } @@ -67,7 +72,7 @@ namespace embark_assist{ else if (input->count(df::interface_key::SEC_CHANGETAB)) { switch (current_page) { case pages::Intro: - current_page = pages::Caveats; + current_page = pages::Caveats_2; break; case pages::General: @@ -78,8 +83,12 @@ namespace embark_assist{ current_page = pages::General; break; - case pages::Caveats: - current_page = pages::Intro; + case pages::Caveats_1: + current_page = pages::Finder; + break; + + case pages::Caveats_2: + current_page = pages::Caveats_1; break; } } @@ -135,6 +144,8 @@ namespace embark_assist{ 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."); + help_text.push_back("Note that World Tiles are the ones shown on the 'Region' map: the 'World'"); + help_text.push_back("typically merges several World Tiles into each of its tiles."); break; @@ -156,23 +167,25 @@ namespace embark_assist{ 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("Thirdly, it takes 'intrusions', i.e. small sections of neighboring tiles'"); + help_text.push_back("biomes into consideration for many fields."); + help_text.push_back("(It can be noted that the DFHack Sand indicator does take the first two"); + help_text.push_back("elements into 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(" 'Mostly Flat' = Condition above met. 'Flat Verified' = All surrounding tiles"); - help_text.push_back(" also satisfy condition so no biome spill over may result in an"); - help_text.push_back(" elevation difference."); - 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("Incompl. Survey if all intrusions couldn't be examined because that requires"); + help_text.push_back("info from neighboring world tiles that haven't been surveyed."); + help_text.push_back("Sand, if present, including through intrusions."); + help_text.push_back("Clay, if present, including thorugh intrusions."); + help_text.push_back("Min and Max soil depth in the embark rectangle, including intrusions."); + help_text.push_back("Flat indicator if all the tiles and intrusions have the same elevation."); + help_text.push_back("Aquifer indicator: Part(ial) or Full, when present, including intrusions."); + help_text.push_back("Waterfall and largest Z level drop if the river has elevation differences"); + help_text.push_back("Evil weather, when present: BR = Blood Rain, TS = Temporary Syndrome"); + help_text.push_back("PS = Permanent Syndrome, Re = Reanimating, and Th = Thralling. Intrusions."); + help_text.push_back("Flux, if present. NOT allowing for small intrusion bits."); + help_text.push_back("A list of all metals present in the embark. Not intrusions."); 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("stones are economic, so they show up here as well. Not intrusions."); help_text.push_back("In addition to the above, the Find functionality can also produce blinking"); help_text.push_back("overlays over the Local, Region, and World maps to indicate where"); help_text.push_back("matching embarks are found. The Local display marks the top left corner of"); @@ -208,15 +221,17 @@ namespace embark_assist{ 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("Clay, etc. means one has to be Present and Absent respectivey."); + help_text.push_back("Min Waterfall Drop finds embarks with drops of at least that number"); + help_text.push_back("of Z levels, but Absent = no waterfall at all."); 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("Present."); help_text.push_back("Freezing allows you to select embarks to select/avoid various freezing"); help_text.push_back("conditions. Note that the minimum temperature is held for only 10 ticks"); help_text.push_back("in many embarks."); help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,"); - help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as"); + help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, and"); help_text.push_back("Not Permanent matches everything except Permanent syndromes."); help_text.push_back("Reanimation packages thralling and reanimation into a single search"); help_text.push_back("criterion. Not Tralling means nothing and just reanimation is matched."); @@ -227,16 +242,28 @@ namespace embark_assist{ help_text.push_back("list. Note that Find is a fairly time consuming task (as it is in vanilla)."); break; - case pages::Caveats: - Screen::drawBorder(" Embark Assistant Help/Info Caveats Page "); + case pages::Caveats_1: + Screen::drawBorder(" Embark Assistant Help/Info Caveats 1 Page "); - help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough"); + help_text.push_back("The plugin surveys world tiles through two actions: using the 'f'ind"); + help_text.push_back("function and through manual movement of the embark rectangle between world"); + help_text.push_back("tiles. In both cases the whole world tile is surveyed, regardless of which"); + help_text.push_back("tiles the embark rectangle covers."); + 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("in overlays of inverted yellow X on top of the Region and World maps. 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("the same map (replacing or erasing the yellow ones). Local map overlay"); help_text.push_back("data is generated as well."); + help_text.push_back("Since 'intrusion' processing requires that the neighboring tiles that may"); + help_text.push_back("provide them are surveyed before the current tile and tiles have to be"); + help_text.push_back("surveyed in some order, the find function can not perform a complete"); + help_text.push_back("survey of prospective embarks that border world tiles yet to be surveyed"); + help_text.push_back("so the very first 'find' will fail to mark such embarks that actually do"); + help_text.push_back("match, while the second and following 'find' operations will locate them"); + help_text.push_back("because critical information from the first scan is kept for subsequent"); + help_text.push_back("ones."); 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"); @@ -245,8 +272,8 @@ namespace embark_assist{ 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(" Note that the first search can miss a fair number of matches for"); + help_text.push_back(" technical reasons discussed above and below."); 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"); @@ -258,7 +285,7 @@ namespace embark_assist{ help_text.push_back("- Thralling is determined by whether material interactions causes"); help_text.push_back(" blinking, which the author believes is one of 4 thralling changes."); help_text.push_back("- The geo information is gathered by code which is essentially a"); - help_text.push_back(" copy of parts of prospector's code adapted for this plugin."); + 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("- Coal is detected by finding COAL producing reactions on minerals."); @@ -266,13 +293,39 @@ namespace embark_assist{ help_text.push_back(" reaching caverns that have been removed at world gen to fail to be"); help_text.push_back(" generated at all. It's likely this bug also affects magma pools."); help_text.push_back(" This plugin does not address this but scripts can correct it."); - help_text.push_back("- 'Flat Verified' vs 'Mostly Flat': There's no known way to detect"); - help_text.push_back(" if an adjacent Mid Level Tile's biome 'spills over' into a tile."); - help_text.push_back(" 'Flat Verified' means neighbors have the same elevation so spill overs"); - help_text.push_back(" don't matter. 'Mostly Flat' means spill overs may ruin a completely"); - help_text.push_back(" level embark, but might not. Can be used to 'rescue' a world where"); - help_text.push_back(" a 'Flat Verified' match failed."); - help_text.push_back("Version 0.9 2019-06-23"); + + break; + + case pages::Caveats_2: + Screen::drawBorder(" Embark Assistant Help/Info Caveats 2 Page "); + + help_text.push_back("- The plugin detects 'incursions' of neighboring tiles into embarks, but"); + help_text.push_back(" this functionality is incomplete when the incursion comes from a"); + help_text.push_back(" neighboring tile that hasn't been surveyed yet. The embark info displays"); + help_text.push_back(" what it can, while indicating if it is incomplete, while the first 'f'ind"); + help_text.push_back(" will automatically fail to match any embarks that can not be analyzed"); + help_text.push_back(" fully. Such failures only appear on embarks that touch an edge of the"); + help_text.push_back(" world tile, and a second (and all subsequent) searches will be complete."); + help_text.push_back(" Since searches can take considerable time, it's left to the user to decide"); + help_text.push_back(" whether to make a second, completing, search."); + help_text.push_back("- Incursions are taken into consideration when looking for Aquifers,"); + help_text.push_back(" Clay, Sand, Min Soil when Everywhere, Biomes, Regions, Evil Weather,"); + help_text.push_back(" Savagery, Evilness, Freezing and Flatness, but ignored for metals/"); + help_text.push_back(" economics/minerals (including Flux and Coal) as any volumes are typically"); + help_text.push_back(" too small to be of interest. Rivers, Waterfalls, Spires, and Magma Pools"); + help_text.push_back(" are not incursion related features."); + help_text.push_back("- There are special rules for handing of intrusions from Lakes and Oceans,"); + help_text.push_back(" as well as Mountains into everything that isn't a Lake or Ocean, and the"); + help_text.push_back(" rules state that these intrusions should be reversed (i.e. 'normal' biomes"); + help_text.push_back(" should push into Lakes, Oceans, and Mountains, even when the indicators"); + help_text.push_back(" say otherwise). This rule is clear for edges, but not for corners, as it"); + help_text.push_back(" does not specify which of the potentially multiple 'superior' biomes"); + help_text.push_back(" should be used. The plugin uses the arbitrarily selected rule that the"); + help_text.push_back(" touching corner to the NW should be selected if eligible, then the one to"); + help_text.push_back(" the N, followed by the one to the W, and lastly the one acting as the"); + help_text.push_back(" reference. This means there's a risk embarks with such 'trouble' corners"); + help_text.push_back(" may get affected corner(s) evaluated incorrectly."); + help_text.push_back("Version 0.9 2019-07-12"); break; } @@ -320,7 +373,10 @@ namespace embark_assist{ embark_assist::screen::paintString(pen_lr, 3, 9, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str()); break; - case pages::Caveats: + case pages::Caveats_1: + break; + + case pages::Caveats_2: break; } dfhack_viewscreen::render(); diff --git a/plugins/embark-assistant/matcher.cpp b/plugins/embark-assistant/matcher.cpp index 3b483ef18..a3975a980 100644 --- a/plugins/embark-assistant/matcher.cpp +++ b/plugins/embark-assistant/matcher.cpp @@ -12,6 +12,7 @@ #include "df/world_data.h" #include "df/world_raws.h" #include "df/world_region.h" +#include "df/world_region_details.h" #include "df/world_region_type.h" #include "matcher.h" @@ -24,32 +25,22 @@ namespace embark_assist { //======================================================================================= - //======================================================================================= - - bool embark_match(embark_assist::defs::world_tile_data *survey_results, - embark_assist::defs::mid_level_tiles *mlt, - uint16_t x, - uint16_t y, - uint16_t start_x, - uint16_t start_y, - embark_assist::defs::finders *finder) { - -// color_ostream_proxy out(Core::getInstance().getConsole()); - df::world_data *world_data = world->world_data; + struct matcher_info { bool savagery_found[3] = { false, false, false }; bool evilness_found[3] = { false, false, false }; - uint16_t aquifer_count = 0; + bool aquifer_absence_found = false; + bool aquifer_presence_found = false; bool river_found = false; uint8_t max_waterfall = 0; - uint16_t elevation = mlt->at(start_x).at(start_y).elevation; + uint16_t elevation; bool clay_found = false; bool sand_found = false; bool flux_found = false; bool coal_found = false; uint8_t max_soil = 0; bool uneven = false; - int16_t min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(start_x).at(start_y).biome_offset]; - int16_t max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(start_x).at(start_y).biome_offset]; + int16_t min_temperature; + int16_t max_temperature; bool blood_rain_found = false; bool permanent_syndrome_rain_found = false; bool temporary_syndrome_rain_found = false; @@ -60,15 +51,449 @@ namespace embark_assist { bool biomes[ENUM_LAST_ITEM(biome_type) + 1]; bool region_types[ENUM_LAST_ITEM(world_region_type) + 1]; uint8_t biome_count; - bool metal_1 = finder->metal_1 == -1; - bool metal_2 = finder->metal_2 == -1; - bool metal_3 = finder->metal_3 == -1; - bool economic_1 = finder->economic_1 == -1; - bool economic_2 = finder->economic_2 == -1; - bool economic_3 = finder->economic_3 == -1; - bool mineral_1 = finder->mineral_1 == -1; - bool mineral_2 = finder->mineral_2 == -1; - bool mineral_3 = finder->mineral_3 == -1; + bool metal_1; + bool metal_2; + bool metal_3; + bool economic_1; + bool economic_2; + bool economic_3; + bool mineral_1; + bool mineral_2; + bool mineral_3; + }; + + //======================================================================================= + + void process_embark_incursion(matcher_info *result, + embark_assist::defs::world_tile_data *survey_results, + embark_assist::defs::mid_level_tile *mlt, // Note this is a single tile, as opposed to most usages of this variable name. + embark_assist::defs::finders *finder, + int16_t elevation, + uint16_t x, + uint16_t y, + bool *failed_match) { + + df::world_data *world_data = world->world_data; + + // Savagery & Evilness + { + result->savagery_found[mlt->savagery_level] = true; + result->evilness_found[mlt->evilness_level] = true; + + embark_assist::defs::evil_savagery_ranges l = embark_assist::defs::evil_savagery_ranges::Low; + while (true) { + if (mlt->savagery_level == static_cast(l)) { + if (finder->savagery[static_cast (l)] == + embark_assist::defs::evil_savagery_values::Absent) { + *failed_match = true; + return; + } + } + else { + if (finder->savagery[static_cast (l)] == + embark_assist::defs::evil_savagery_values::All) { + *failed_match = true; + return; + } + } + + if (mlt->evilness_level == static_cast(l)) { + if (finder->evilness[static_cast (l)] == + embark_assist::defs::evil_savagery_values::Absent) { + *failed_match = true; + return; + } + } + else { + if (finder->evilness[static_cast (l)] == + embark_assist::defs::evil_savagery_values::All) { + *failed_match = true; + return; + } + } + + if (l == embark_assist::defs::evil_savagery_ranges::High) break; + l = static_cast (static_cast(l) + 1); + } + } + + // Aquifer + switch (finder->aquifer) { + case embark_assist::defs::aquifer_ranges::NA: + break; + + case embark_assist::defs::aquifer_ranges::All: + if (!mlt->aquifer) { + *failed_match = true; + return; + } + break; + + case embark_assist::defs::aquifer_ranges::Present: + case embark_assist::defs::aquifer_ranges::Partial: + case embark_assist::defs::aquifer_ranges::Not_All: + if (mlt->aquifer) { + result->aquifer_presence_found = true; + } + else { + result->aquifer_absence_found = true; + } + + break; + + case embark_assist::defs::aquifer_ranges::Absent: + if (mlt->aquifer) { + *failed_match = true; + return; + } + break; + } + + // River & Waterfall. N/A for incursions. + + // Flat + if (finder->flat == embark_assist::defs::yes_no_ranges::Yes && + result->elevation != mlt->elevation) { + *failed_match = true; + return; + } + + // Clay + if (mlt->clay) { + if (finder->clay == embark_assist::defs::present_absent_ranges::Absent) { + *failed_match = true; + return; + } + result->clay_found = true; + } + + // Sand + if (mlt->sand) { + if (finder->sand == embark_assist::defs::present_absent_ranges::Absent) { + *failed_match = true; + return; + } + result->sand_found = true; + } + + // Flux. N/A for intrusions. + // Coal. N/A for intrusions + + // Min Soil + if (finder->soil_min != embark_assist::defs::soil_ranges::NA && + mlt->soil_depth < static_cast(finder->soil_min) && + finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::All) { + *failed_match = true; + return; + } + + if (result->max_soil < mlt->soil_depth) { + result->max_soil = mlt->soil_depth; + } + + // Max Soil + if (finder->soil_max != embark_assist::defs::soil_ranges::NA && + mlt->soil_depth > static_cast(finder->soil_max)) { + *failed_match = true; + return; + } + + // Freezing + if (result->min_temperature > survey_results->at(x).at(y).min_temperature[mlt->biome_offset]) { + result->min_temperature = survey_results->at(x).at(y).min_temperature[mlt->biome_offset]; + } + + if (result->max_temperature < survey_results->at(x).at(y).max_temperature[mlt->biome_offset]) { + result->max_temperature = survey_results->at(x).at(y).max_temperature[mlt->biome_offset]; + } + + if (result->min_temperature <= 0 && + finder->freezing == embark_assist::defs::freezing_ranges::Never) { + *failed_match = true; + return; + } + + if (result->max_temperature > 0 && + finder->freezing == embark_assist::defs::freezing_ranges::Permanent) { + *failed_match = true; + return; + } + + // Blood Rain + if (survey_results->at(x).at(y).blood_rain[mlt->biome_offset]) { + if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) { + *failed_match = true; + return; + } + result->blood_rain_found = true; + } + + // Syndrome Rain, Permanent + if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->biome_offset]) { + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Not_Permanent || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) { + *failed_match = true; + return; + } + result->permanent_syndrome_rain_found = true; + } + + // Syndrome Rain, Temporary + if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->biome_offset]) { + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent || + finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) { + *failed_match = true; + return; + } + result->temporary_syndrome_rain_found = true; + } + + // Reanmation + if (survey_results->at(x).at(y).reanimating[mlt->biome_offset]) { + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling || + finder->reanimation == embark_assist::defs::reanimation_ranges::None) { + *failed_match = true; + return; + } + result->reanimation_found = true; + } + + // Thralling + if (survey_results->at(x).at(y).thralling[mlt->biome_offset]) { + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation || + finder->reanimation == embark_assist::defs::reanimation_ranges::Not_Thralling || + finder->reanimation == embark_assist::defs::reanimation_ranges::None) { + *failed_match = true; + return; + } + result->thralling_found = true; + } + + // Spires. N/A for intrusions + // Magma. N/A for intrusions + // Biomes + + result->biomes[survey_results->at(x).at(y).biome[mlt->biome_offset]] = true; + + // Region Type + result->region_types[world_data->regions[survey_results->at(x).at(y).biome_index[mlt->biome_offset]]->type] = true; + + // Metals. N/A for intrusions + // Economics. N/A for intrusions + } + + //======================================================================================= + + + void process_embark_incursion_mid_level_tile(uint8_t from_direction, + matcher_info *result, + embark_assist::defs::world_tile_data *survey_results, + embark_assist::defs::mid_level_tiles *mlt, + embark_assist::defs::finders *finder, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k, + bool *failed_match) { + int8_t fetch_i = i; + int8_t fetch_k = k; + int16_t fetch_x = x; + int16_t fetch_y = y; + df::world_data *world_data = world->world_data; + + // Logic can be implemented with modulo and division, but that's harder to read. + switch (from_direction) { + case 0: + fetch_i = i - 1; + fetch_k = k - 1; + break; + + case 1: + fetch_k = k - 1; + break; + + case 2: + fetch_i = i + 1; + fetch_k = k - 1; + break; + + case 3: + fetch_i = i - 1; + break; + + case 4: + return; // Own tile provides the data, so there's no incursion. + break; + + case 5: + fetch_i = i + 1; + break; + + case 6: + fetch_i = i - 1; + fetch_k = k + 1; + break; + + case 7: + fetch_k = k + 1; + break; + + case 8: + fetch_i = i + 1; + fetch_k = k + 1; + } + + if (fetch_i < 0) { + fetch_x = x - 1; + } + else if (fetch_i > 15) { + fetch_x = x + 1; + } + + if (fetch_k < 0) { + fetch_y = y - 1; + } + else if (fetch_k > 15) { + fetch_y = y + 1; + } + + if (fetch_x < 0 || + fetch_x == world_data->world_width || + fetch_y < 0 || + fetch_y == world_data->world_height) { + return; // We're at the world edge, so no incursions from the outside. + } + + if (!&survey_results->at(fetch_x).at(fetch_y).surveyed) { + *failed_match = true; + return; + } + + if (fetch_k < 0) { + if (fetch_i < 0) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[15], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else if (fetch_i > 15) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[0], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[i], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + } + else if (fetch_k > 15) { + if (fetch_i < 0) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[15], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else if (fetch_i > 15) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[0], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[i], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + } + else { + if (fetch_i < 0) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).east_column[k], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else if (fetch_i > 15) { + process_embark_incursion(result, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).west_column[k], + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + else { + process_embark_incursion(result, + survey_results, + &mlt->at(fetch_i).at(fetch_k), + finder, + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y, + failed_match); + } + } + } + + //======================================================================================= + + bool embark_match(embark_assist::defs::world_tile_data *survey_results, + embark_assist::defs::mid_level_tiles *mlt, + uint16_t x, + uint16_t y, + uint16_t start_x, + uint16_t start_y, + embark_assist::defs::finders *finder) { + +// color_ostream_proxy out(Core::getInstance().getConsole()); + df::world_data *world_data = world->world_data; + matcher_info result; + result.elevation = mlt->at(start_x).at(start_y).elevation; + result.min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(start_x).at(start_y).biome_offset]; + result.max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(start_x).at(start_y).biome_offset]; + result.metal_1 = finder->metal_1 == -1; + result.metal_2 = finder->metal_2 == -1; + result.metal_3 = finder->metal_3 == -1; + result.economic_1 = finder->economic_1 == -1; + result.economic_2 = finder->economic_2 == -1; + result.economic_3 = finder->economic_3 == -1; + result.mineral_1 = finder->mineral_1 == -1; + result.mineral_2 = finder->mineral_2 == -1; + result.mineral_3 = finder->mineral_3 == -1; + bool failed_match = false; const uint16_t embark_size = finder->x_dim * finder->y_dim; @@ -77,24 +502,18 @@ namespace embark_assist { finder->biome_1 != -1 || finder->biome_2 != -1 || finder->biome_3 != -1) { - for (uint8_t i = 0; i <= ENUM_LAST_ITEM(biome_type); i++) biomes[i] = false; + for (uint8_t i = 0; i <= ENUM_LAST_ITEM(biome_type); i++) result.biomes[i] = false; } - for (uint8_t i = 0; i <= ENUM_LAST_ITEM(world_region_type); i++) region_types[i] = false; - - if (finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified && - (start_x == 0 || - start_x + finder->x_dim == 16 || - start_y == 0 || - start_y + finder->y_dim == 16)) return false; + for (uint8_t i = 0; i <= ENUM_LAST_ITEM(world_region_type); i++) result.region_types[i] = false; for (uint16_t i = start_x; i < start_x + finder->x_dim; i++) { for (uint16_t k = start_y; k < start_y + finder->y_dim; k++) { // Savagery & Evilness { - savagery_found[mlt->at(i).at(k).savagery_level] = true; - evilness_found[mlt->at(i).at(k).evilness_level] = true; + result.savagery_found[mlt->at(i).at(k).savagery_level] = true; + result.evilness_found[mlt->at(i).at(k).evilness_level] = true; embark_assist::defs::evil_savagery_ranges l = embark_assist::defs::evil_savagery_ranges::Low; while (true) { @@ -128,17 +547,23 @@ namespace embark_assist { case embark_assist::defs::aquifer_ranges::All: if (!mlt->at(i).at(k).aquifer) return false; - aquifer_count++; + result.aquifer_presence_found = true; break; case embark_assist::defs::aquifer_ranges::Present: case embark_assist::defs::aquifer_ranges::Partial: case embark_assist::defs::aquifer_ranges::Not_All: - if (mlt->at(i).at(k).aquifer) aquifer_count++; + if (mlt->at(i).at(k).aquifer) { + result.aquifer_presence_found = true; + } + else { + result.aquifer_absence_found = true; + } break; case embark_assist::defs::aquifer_ranges::Absent: if (mlt->at(i).at(k).aquifer) return false; + result.aquifer_presence_found = true; break; } @@ -150,52 +575,51 @@ namespace embark_assist { if (i < start_x + finder->x_dim - 2 && mlt->at(i + 1).at(k).river_present && - abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > max_waterfall) { + abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation) > result.max_waterfall) { if (finder->min_waterfall == 0) return false; // 0 = Absent - max_waterfall = + result.max_waterfall = abs(mlt->at(i).at(k).river_elevation - mlt->at(i + 1).at(k).river_elevation); } if (k < start_y + finder->y_dim - 2 && mlt->at(i).at(k + 1).river_present && - abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > max_waterfall) { + abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation) > result.max_waterfall) { if (finder->min_waterfall == 0) return false; // 0 = Absent - max_waterfall = + result.max_waterfall = abs(mlt->at(i).at(k).river_elevation - mlt->at(i).at(k + 1).river_elevation); } - river_found = true; + result.river_found = true; } - // Flatness - if ((finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified || - finder->flatness == embark_assist::defs::flatness_ranges::Mostly_Flat) && - elevation != mlt->at(i).at(k).elevation) return false; + // Flat + if (finder->flat == embark_assist::defs::yes_no_ranges::Yes && + result.elevation != mlt->at(i).at(k).elevation) return false; - if (elevation != mlt->at(i).at(k).elevation) uneven = true; + if (result.elevation != mlt->at(i).at(k).elevation) result.uneven = true; // Clay if (mlt->at(i).at(k).clay) { if (finder->clay == embark_assist::defs::present_absent_ranges::Absent) return false; - clay_found = true; + result.clay_found = true; } // Sand if (mlt->at(i).at(k).sand) { if (finder->sand == embark_assist::defs::present_absent_ranges::Absent) return false; - sand_found = true; + result.sand_found = true; } // Flux if (mlt->at(i).at(k).flux) { if (finder->flux == embark_assist::defs::present_absent_ranges::Absent) return false; - flux_found = true; + result.flux_found = true; } // Coal if (mlt->at(i).at(k).coal) { if (finder->coal == embark_assist::defs::present_absent_ranges::Absent) return false; - coal_found = true; + result.coal_found = true; } // Min Soil @@ -203,8 +627,8 @@ namespace embark_assist { mlt->at(i).at(k).soil_depth < static_cast(finder->soil_min) && finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::All) return false; - if (max_soil < mlt->at(i).at(k).soil_depth) { - max_soil = mlt->at(i).at(k).soil_depth; + if (result.max_soil < mlt->at(i).at(k).soil_depth) { + result.max_soil = mlt->at(i).at(k).soil_depth; } // Max Soil @@ -212,24 +636,24 @@ namespace embark_assist { mlt->at(i).at(k).soil_depth > static_cast(finder->soil_max)) return false; // Freezing - if (min_temperature > survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]) { - min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]; + if (result.min_temperature > survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]) { + result.min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]; } - if (max_temperature < survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]) { - max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]; + if (result.max_temperature < survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]) { + result.max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]; } - if (min_temperature <= 0 && + if (result.min_temperature <= 0 && finder->freezing == embark_assist::defs::freezing_ranges::Never) return false; - if (max_temperature > 0 && + if (result.max_temperature > 0 && finder->freezing == embark_assist::defs::freezing_ranges::Permanent) return false; // Blood Rain if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) { if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false; - blood_rain_found = true; + result.blood_rain_found = true; } // Syndrome Rain, Permanent @@ -237,21 +661,21 @@ namespace embark_assist { if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary || finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Not_Permanent || finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false; - permanent_syndrome_rain_found = true; + result.permanent_syndrome_rain_found = true; } // Syndrome Rain, Temporary if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->at(i).at(k).biome_offset]) { if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent || finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false; - temporary_syndrome_rain_found = true; + result.temporary_syndrome_rain_found = true; } // Reanmation if (survey_results->at(x).at(y).reanimating[mlt->at(i).at(k).biome_offset]) { if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling || finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false; - reanimation_found = true; + result.reanimation_found = true; } // Thralling @@ -259,50 +683,345 @@ namespace embark_assist { if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation || finder->reanimation == embark_assist::defs::reanimation_ranges::Not_Thralling || finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false; - thralling_found = true; + result.thralling_found = true; } // Spires if (mlt->at(i).at(k).adamantine_level != -1) { - spire_count++; + result.spire_count++; if (finder->spire_count_max != -1 && - finder->spire_count_max < spire_count) return false; + finder->spire_count_max < result.spire_count) return false; } // Magma if (mlt->at(i).at(k).magma_level != -1) { - if (mlt->at(i).at(k).magma_level > magma_level) + if (mlt->at(i).at(k).magma_level > result.magma_level) { - magma_level = mlt->at(i).at(k).magma_level; + result.magma_level = mlt->at(i).at(k).magma_level; if (finder->magma_max != embark_assist::defs::magma_ranges::NA && - static_cast(finder->magma_max) < magma_level) return false; + static_cast(finder->magma_max) < result.magma_level) return false; } } // Biomes - biomes[survey_results->at(x).at(y).biome[mlt->at(i).at(k).biome_offset]] = true; + result.biomes[survey_results->at(x).at(y).biome[mlt->at(i).at(k).biome_offset]] = true; // Region Type - region_types[world_data->regions[survey_results->at(x).at(y).biome_index[mlt->at(i).at(k).biome_offset]]->type] = true; + result.region_types[world_data->regions[survey_results->at(x).at(y).biome_index[mlt->at(i).at(k).biome_offset]]->type] = true; // Metals - metal_1 = metal_1 || mlt->at(i).at(k).metals[finder->metal_1]; - metal_2 = metal_2 || mlt->at(i).at(k).metals[finder->metal_2]; - metal_3 = metal_3 || mlt->at(i).at(k).metals[finder->metal_3]; + result.metal_1 = result.metal_1 || mlt->at(i).at(k).metals[finder->metal_1]; + result.metal_2 = result.metal_2 || mlt->at(i).at(k).metals[finder->metal_2]; + result.metal_3 = result.metal_3 || mlt->at(i).at(k).metals[finder->metal_3]; // Economics - economic_1 = economic_1 || mlt->at(i).at(k).economics[finder->economic_1]; - economic_2 = economic_2 || mlt->at(i).at(k).economics[finder->economic_2]; - economic_3 = economic_3 || mlt->at(i).at(k).economics[finder->economic_3]; + result.economic_1 = result.economic_1 || mlt->at(i).at(k).economics[finder->economic_1]; + result.economic_2 = result.economic_2 || mlt->at(i).at(k).economics[finder->economic_2]; + result.economic_3 = result.economic_3 || mlt->at(i).at(k).economics[finder->economic_3]; // Minerals - mineral_1 = mineral_1 || mlt->at(i).at(k).minerals[finder->mineral_1]; - mineral_2 = mineral_2 || mlt->at(i).at(k).minerals[finder->mineral_2]; - mineral_3 = mineral_3 || mlt->at(i).at(k).minerals[finder->mineral_3]; + result.mineral_1 = result.mineral_1 || mlt->at(i).at(k).minerals[finder->mineral_1]; + result.mineral_2 = result.mineral_2 || mlt->at(i).at(k).minerals[finder->mineral_2]; + result.mineral_3 = result.mineral_3 || mlt->at(i).at(k).minerals[finder->mineral_3]; } } + // Take incursions into account. + + for (int8_t i = start_x; i < start_x + finder->x_dim; i++) { + + // NW corner, north row + if ((i == 0 && start_y == 0 && x - 1 >= 0 && y - 1 >= 0 && !survey_results->at(x - 1).at(y - 1).surveyed) || + (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || + (start_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 4, + x, + y, + i, + start_y), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y, + &failed_match); + } + + // N edge, north row + if (start_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_ns_edge(survey_results, + true, + x, + y, + i, + start_y), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y, + &failed_match); + } + + // NE corner, north row + if ((i == 15 && start_y == 0 && x + 1 < world_data->world_width && y - 1 >= 0 && !survey_results->at(x + 1).at(y - 1).surveyed) || + (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || + (start_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 5, + x, + y, + i, + start_y), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y, + &failed_match); + } + + // SW corner, south row + if ((i == 0 && start_y + finder->y_dim == 16 && x - 1 >= 0 && y + 1 < world_data->world_height && !survey_results->at(x - 1).at(y + 1).surveyed) || + (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || + (start_y + finder->y_dim == 16 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 7, + x, + y, + i, + start_y + finder->y_dim - 1), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y + finder->y_dim - 1, + &failed_match); + } + + // S edge, south row + if (start_y + finder->y_dim == 16 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_ns_edge(survey_results, + false, + x, + y, + i, + start_y + finder->y_dim - 1), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y + finder->y_dim - 1, + &failed_match); + } + + // SE corner south row + if ((i == 15 && start_y + finder->y_dim == 16 && x + 1 < world_data->world_width && y + 1 < world_data->world_height && !survey_results->at(x + 1).at(y + 1).surveyed) || + (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || + (start_y + finder->y_dim == 16 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 8, + x, + y, + i, + start_y + finder->y_dim - 1), + &result, + survey_results, + mlt, + finder, + x, + y, + i, + start_y + finder->y_dim - 1, + &failed_match); + } + + if (failed_match) return false; + } + + for (int8_t k = start_y; k < start_y + finder->y_dim; k++) { + // NW corner, west side + if ((start_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed)) { + failed_match = true; + } + else if (k > start_y) { // We've already covered the NW corner of the NW, with its complications. + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 4, + x, + y, + start_x, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x, + k, + &failed_match); + } + + // W edge, west side + if (start_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_ew_edge(survey_results, + true, + x, + y, + start_x, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x, + k, + &failed_match); + } + + // SW corner, west side + if (start_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { + failed_match = true; + } + else if (k < start_y + finder->y_dim - 1) { // We've already covered the SW corner of the SW tile, with its complicatinons. + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 7, + x, + y, + start_x, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x, + k, + &failed_match); + } + + // NE corner, east side + if ((start_x + finder->x_dim == 16 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed)) { + failed_match = true; + } + else if (k > start_y) { // We've already covered the NE tile's NE corner, with its complications. + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 5, + x, + y, + start_x + finder->x_dim - 1, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x + finder->x_dim - 1, + k, + &failed_match); + } + + // E edge, east side + if (start_x + finder->y_dim == 16 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { + failed_match = true; + } + else { + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_ew_edge(survey_results, + false, + x, + y, + start_x + finder->x_dim - 1, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x + finder->x_dim - 1, + k, + &failed_match); + } + + // SE corner, east side + if (start_x + finder->x_dim == 16 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { + failed_match = true; + } + else if (k < start_y + finder->y_dim - 1) { // We've already covered the SE tile's SE corner, with its complications. + process_embark_incursion_mid_level_tile + (embark_assist::survey::translate_corner(survey_results, + 8, + x, + y, + start_x + finder->x_dim - 1, + k), + &result, + survey_results, + mlt, + finder, + x, + y, + start_x + finder->x_dim - 1, + k, + &failed_match); + } + + if (failed_match) return false; + } + // Summary section, for all the stuff that require the complete picture // // Savagery & Evilness @@ -312,11 +1031,11 @@ namespace embark_assist { while (true) { if (finder->savagery[static_cast (l)] == embark_assist::defs::evil_savagery_values::Present && - !savagery_found[static_cast(l)]) return false; + !result.savagery_found[static_cast(l)]) return false; if (finder->evilness[static_cast (l)] == embark_assist::defs::evil_savagery_values::Present && - !evilness_found[static_cast(l)]) return false; + !result.evilness_found[static_cast(l)]) return false; if (l == embark_assist::defs::evil_savagery_ranges::High) break; l = static_cast (static_cast(l) + 1); @@ -331,119 +1050,107 @@ namespace embark_assist { break; case embark_assist::defs::aquifer_ranges::Present: - if (aquifer_count == 0) return false; + if (!result.aquifer_presence_found) return false; break; case embark_assist::defs::aquifer_ranges::Partial: - if (aquifer_count == 0 || aquifer_count == embark_size) return false; + if (!result.aquifer_absence_found || !result.aquifer_presence_found) return false; break; case embark_assist::defs::aquifer_ranges::Not_All: - if (aquifer_count == embark_size) return false; + if (!result.aquifer_absence_found) return false; break; } // River & Waterfall - if (!river_found && finder->min_river > embark_assist::defs::river_ranges::None) return false; - if (max_waterfall < finder->min_waterfall) return false; // N/A = -1 is always smaller, so no additional check needed. - - // Flatness - if (!uneven && finder->flatness == embark_assist::defs::flatness_ranges::Uneven) return false; + if (!result.river_found && finder->min_river > embark_assist::defs::river_ranges::None) return false; + if (result.max_waterfall < finder->min_waterfall) return false; // N/A = -1 is always smaller, so no additional check needed. - if (finder->flatness == embark_assist::defs::flatness_ranges::Flat_Verified) { - for (uint16_t i = start_x - 1; i < start_x + finder->x_dim + 1; i++) { - if (elevation != mlt->at(i).at(start_y - 1).elevation || - elevation != mlt->at(i).at(start_y + finder->y_dim).elevation) return false; - } - - for (uint16_t k = start_y; k < start_y + finder->y_dim; k++) { - if (elevation != mlt->at(start_x - 1).at(k).elevation || - elevation != mlt->at(start_x + finder->x_dim).at(k).elevation) return false; - } - } + // Flat + if (!result.uneven && finder->flat == embark_assist::defs::yes_no_ranges::No) return false; // Clay - if (finder->clay == embark_assist::defs::present_absent_ranges::Present && !clay_found) return false; + if (finder->clay == embark_assist::defs::present_absent_ranges::Present && !result.clay_found) return false; // Sand - if (finder->sand == embark_assist::defs::present_absent_ranges::Present && !sand_found) return false; + if (finder->sand == embark_assist::defs::present_absent_ranges::Present && !result.sand_found) return false; // Flux - if (finder->flux == embark_assist::defs::present_absent_ranges::Present && !flux_found) return false; + if (finder->flux == embark_assist::defs::present_absent_ranges::Present && !result.flux_found) return false; // Coal - if (finder->coal == embark_assist::defs::present_absent_ranges::Present && !coal_found) return false; + if (finder->coal == embark_assist::defs::present_absent_ranges::Present && !result.coal_found) return false; // Min Soil if (finder->soil_min != embark_assist::defs::soil_ranges::NA && finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present && - max_soil < static_cast(finder->soil_min)) return false; + result.max_soil < static_cast(finder->soil_min)) return false; // Freezing if (finder->freezing == embark_assist::defs::freezing_ranges::At_Least_Partial && - min_temperature > 0) return false; + result.min_temperature > 0) return false; if (finder->freezing == embark_assist::defs::freezing_ranges::Partial && - (min_temperature > 0 || - max_temperature <= 0)) return false; + (result.min_temperature > 0 || + result.max_temperature <= 0)) return false; if (finder->freezing == embark_assist::defs::freezing_ranges::At_Most_Partial && - max_temperature <= 0) return false; + result.max_temperature <= 0) return false; // Blood Rain - if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false; + if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !result.blood_rain_found) return false; // Syndrome Rain - if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Any && !permanent_syndrome_rain_found && !temporary_syndrome_rain_found) return false; - if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent && !permanent_syndrome_rain_found) return false; - if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary && !temporary_syndrome_rain_found) return false; + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Any && !result.permanent_syndrome_rain_found && !result.temporary_syndrome_rain_found) return false; + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent && !result.permanent_syndrome_rain_found) return false; + if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary && !result.temporary_syndrome_rain_found) return false; // Reanimation - if (finder->reanimation == embark_assist::defs::reanimation_ranges::Both && !(reanimation_found && thralling_found)) return false; - if (finder->reanimation == embark_assist::defs::reanimation_ranges::Any && !reanimation_found && !thralling_found) return false; - if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling && !thralling_found) return false; - if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation && !reanimation_found) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Both && !(result.reanimation_found && result.thralling_found)) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Any && !result.reanimation_found && !result.thralling_found) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling && !result.thralling_found) return false; + if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation && !result.reanimation_found) return false; // Spires - if (finder->spire_count_min != -1 && finder->spire_count_min > spire_count) return false; - if (finder->spire_count_max != -1 && finder->spire_count_max < spire_count) return false; + if (finder->spire_count_min != -1 && finder->spire_count_min > result.spire_count) return false; + if (finder->spire_count_max != -1 && finder->spire_count_max < result.spire_count) return false; // Magma if (// finder->magma_min != embark_assist::defs::magma_ranges::NA && // This check is redundant. - finder->magma_min > static_cast(magma_level)) return false; + finder->magma_min > static_cast(result.magma_level)) return false; // Biomes if (finder->biome_count_min != -1 || finder->biome_count_max != -1) { - biome_count = 0; + result.biome_count = 0; for (uint8_t i = 0; i <= ENUM_LAST_ITEM(biome_type); i++) { - if (biomes[i]) biome_count++; + if (result.biomes[i]) result.biome_count++; } - if (biome_count < finder->biome_count_min || + if (result.biome_count < finder->biome_count_min || (finder->biome_count_max != -1 && - finder->biome_count_max < biome_count)) return false; + finder->biome_count_max < result.biome_count)) return false; } - if (finder->biome_1 != -1 && !biomes[finder->biome_1]) return false; - if (finder->biome_2 != -1 && !biomes[finder->biome_2]) return false; - if (finder->biome_3 != -1 && !biomes[finder->biome_3]) return false; + if (finder->biome_1 != -1 && !result.biomes[finder->biome_1]) return false; + if (finder->biome_2 != -1 && !result.biomes[finder->biome_2]) return false; + if (finder->biome_3 != -1 && !result.biomes[finder->biome_3]) return false; // Region Type - if (finder->region_type_1 != -1 && !region_types[finder->region_type_1]) return false; - if (finder->region_type_2 != -1 && !region_types[finder->region_type_2]) return false; - if (finder->region_type_3 != -1 && !region_types[finder->region_type_3]) return false; + if (finder->region_type_1 != -1 && !result.region_types[finder->region_type_1]) return false; + if (finder->region_type_2 != -1 && !result.region_types[finder->region_type_2]) return false; + if (finder->region_type_3 != -1 && !result.region_types[finder->region_type_3]) return false; // Metals, Economics, and Minerals - if (!metal_1 || - !metal_2 || - !metal_3 || - !economic_1 || - !economic_2 || - !economic_3 || - !mineral_1 || - !mineral_2 || - !mineral_3) return false; + if (!result.metal_1 || + !result.metal_2 || + !result.metal_3 || + !result.economic_1 || + !result.economic_2 || + !result.economic_3 || + !result.mineral_1 || + !result.mineral_2 || + !result.mineral_3) return false; return true; } @@ -1561,12 +2268,6 @@ uint16_t embark_assist::matcher::find(embark_assist::defs::match_iterators *iter return 0; } - if (iterator->finder.flatness == embark_assist::defs::flatness_ranges::Flat_Verified && - (iterator->finder.x_dim > 14 || - iterator->finder.y_dim > 14)) { - out.printerr("matcher::find: Can never verify flatness without border around embark\n"); - } - if (iterator->finder.spire_count_max < iterator->finder.spire_count_min && iterator->finder.spire_count_max != -1) { out.printerr("matcher::find: Will never find any matches with max spires < min spires\n"); diff --git a/plugins/embark-assistant/overlay.cpp b/plugins/embark-assistant/overlay.cpp index 494dbcd4a..a0defd0d5 100644 --- a/plugins/embark-assistant/overlay.cpp +++ b/plugins/embark-assistant/overlay.cpp @@ -331,6 +331,10 @@ void embark_assist::overlay::match_progress(uint16_t count, embark_assist::defs: void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_info) { state->embark_info.clear(); + if (!site_info->incursions_processed) { + state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTRED), "Incomp. Survey" }); + } + if (site_info->sand) { state->embark_info.push_back({ Screen::Pen(' ', COLOR_YELLOW), "Sand" }); } @@ -345,12 +349,8 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in 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->flatness == embark_assist::defs::flatnesses::Flat_Verified) { - state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Flat Verified" }); - } - else if (site_info->flatness == embark_assist::defs::flatnesses::Mostly_Flat) - { - state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Mostly Flat" }); + if (site_info->flat) { + state->embark_info.push_back({ Screen::Pen(' ', COLOR_BROWN), "Flat" }); } if (site_info->aquifer) { @@ -367,6 +367,55 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Waterfall " + std::to_string(site_info->max_waterfall) }); } + if (site_info->blood_rain || + site_info->permanent_syndrome_rain || + site_info->temporary_syndrome_rain || + site_info->reanimating || + site_info->thralling) { + std::string blood_rain; + std::string permanent_syndrome_rain; + std::string temporary_syndrome_rain; + std::string reanimating; + std::string thralling; + + if (site_info->blood_rain) { + blood_rain = "BR "; + } + else { + blood_rain = " "; + } + + if (site_info->permanent_syndrome_rain) { + permanent_syndrome_rain = "PS "; + } + else { + permanent_syndrome_rain = " "; + } + + if (site_info->temporary_syndrome_rain) { + temporary_syndrome_rain = "TS "; + } + else { + permanent_syndrome_rain = " "; + } + + if (site_info->reanimating) { + reanimating = "Re "; + } + else { + reanimating = " "; + } + + if (site_info->thralling) { + thralling = "Th"; + } + else { + thralling = " "; + } + + state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTRED), blood_rain + temporary_syndrome_rain + permanent_syndrome_rain + reanimating + thralling }); + } + if (site_info->flux) { state->embark_info.push_back({ Screen::Pen(' ', COLOR_WHITE), "Flux" }); } diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index 42a766674..a2942d415 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -50,6 +50,7 @@ #include "df/world_region.h" #include "df/world_region_details.h" #include "df/world_region_feature.h" +#include "df/world_region_type.h" #include "df/world_river.h" #include "df/world_site.h" #include "df/world_site_type.h" @@ -112,7 +113,7 @@ namespace embark_assist { for (uint16_t i = 0; i < world->raws.inorganics.size(); i++) { for (uint16_t k = 0; k < world->raws.inorganics[i]->economic_uses.size(); k++) { for (uint16_t l = 0; l < world->raws.reactions.reactions[world->raws.inorganics[i]->economic_uses[k]]->products.size(); l++) { - df::reaction_product_itemst *product = static_cast(world->raws.reactions.reactions[world->raws.inorganics[i]->economic_uses[k]]->products[l]); + df::reaction_product_itemst *product = static_cast(world->raws.reactions.reactions[world->raws.inorganics[i]->economic_uses[k]]->products[l]); if (product->mat_type == df::builtin_mats::COAL) { state->coals.push_back(i); @@ -172,7 +173,7 @@ namespace embark_assist { } } - for (uint16_t l = 0; l < state->coals.size(); l++) { + for (uint16_t l = 0; l < state->coals.size(); l++) { if (layer->mat_index == state->coals[l]) { geo_summary->at(i).coal_absent = false; break; @@ -229,7 +230,7 @@ namespace embark_assist { //================================================================================= void survey_rivers(embark_assist::defs::world_tile_data *survey_results) { -// color_ostream_proxy out(Core::getInstance().getConsole()); + // color_ostream_proxy out(Core::getInstance().getConsole()); df::world_data *world_data = world->world_data; int16_t x; int16_t y; @@ -327,23 +328,23 @@ namespace embark_assist { thralling = true; } else if (ce_type == df::creature_interaction_effect_type::PAIN || - ce_type == df::creature_interaction_effect_type::SWELLING || - ce_type == df::creature_interaction_effect_type::OOZING || - ce_type == df::creature_interaction_effect_type::BRUISING || - ce_type == df::creature_interaction_effect_type::BLISTERS || - ce_type == df::creature_interaction_effect_type::NUMBNESS || - ce_type == df::creature_interaction_effect_type::PARALYSIS || - ce_type == df::creature_interaction_effect_type::FEVER || - ce_type == df::creature_interaction_effect_type::BLEEDING || - ce_type == df::creature_interaction_effect_type::COUGH_BLOOD || - ce_type == df::creature_interaction_effect_type::VOMIT_BLOOD || - ce_type == df::creature_interaction_effect_type::NAUSEA || - ce_type == df::creature_interaction_effect_type::UNCONSCIOUSNESS || - ce_type == df::creature_interaction_effect_type::NECROSIS || - ce_type == df::creature_interaction_effect_type::IMPAIR_FUNCTION || - ce_type == df::creature_interaction_effect_type::DROWSINESS || - ce_type == df::creature_interaction_effect_type::DIZZINESS || - ce_type == df::creature_interaction_effect_type::ERRATIC_BEHAVIOR) { // Doubtful if possible for region. + ce_type == df::creature_interaction_effect_type::SWELLING || + ce_type == df::creature_interaction_effect_type::OOZING || + ce_type == df::creature_interaction_effect_type::BRUISING || + ce_type == df::creature_interaction_effect_type::BLISTERS || + ce_type == df::creature_interaction_effect_type::NUMBNESS || + ce_type == df::creature_interaction_effect_type::PARALYSIS || + ce_type == df::creature_interaction_effect_type::FEVER || + ce_type == df::creature_interaction_effect_type::BLEEDING || + ce_type == df::creature_interaction_effect_type::COUGH_BLOOD || + ce_type == df::creature_interaction_effect_type::VOMIT_BLOOD || + ce_type == df::creature_interaction_effect_type::NAUSEA || + ce_type == df::creature_interaction_effect_type::UNCONSCIOUSNESS || + ce_type == df::creature_interaction_effect_type::NECROSIS || + ce_type == df::creature_interaction_effect_type::IMPAIR_FUNCTION || + ce_type == df::creature_interaction_effect_type::DROWSINESS || + ce_type == df::creature_interaction_effect_type::DIZZINESS || + ce_type == df::creature_interaction_effect_type::ERRATIC_BEHAVIOR) { // Doubtful if possible for region. if (ce->end == -1) { permanent_syndrome_rain = true; } @@ -446,7 +447,7 @@ namespace embark_assist { } else if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::North || - world->world_data->flip_latitude == df::world_data::T_flip_latitude::South) { + world->world_data->flip_latitude == df::world_data::T_flip_latitude::South) { steps = world->world_data->world_height / 2; if (latitude > steps) { @@ -485,7 +486,7 @@ namespace embark_assist { divisor = (63 / steps * lat); } else if (world->world_data->world_height == 129 || - world->world_data->world_height == 257) { + world->world_data->world_height == 257) { divisor = (64 / steps * lat); } else { @@ -494,6 +495,222 @@ namespace embark_assist { return max_temperature - ceil(divisor * 3 / 4); } + + //================================================================================= + + void process_embark_incursion(embark_assist::defs::site_infos *site_info, + embark_assist::defs::world_tile_data *survey_results, + embark_assist::defs::mid_level_tile *mlt, // Note this is a single tile, as opposed to most usages of this variable name. + int16_t elevation, + uint16_t x, + uint16_t y) { + + if (mlt->aquifer) { + site_info->aquifer = true; + } + else { + site_info->aquifer_full = false; + } + + if (mlt->soil_depth < site_info->min_soil) { + site_info->min_soil = mlt->soil_depth; + } + + if (mlt->soil_depth > site_info->max_soil) { + site_info->max_soil = mlt->soil_depth; + } + + if (elevation != mlt->elevation) { + site_info->flat = false; + } + + if (mlt->clay) { + site_info->clay = true; + } + + if (mlt->sand) { + site_info->sand = true; + } + + if (survey_results->at(x).at(y).blood_rain [mlt->biome_offset]) { + site_info->blood_rain = true; + } + + if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->biome_offset]) { + site_info->permanent_syndrome_rain = true; + } + + if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->biome_offset]) { + site_info->temporary_syndrome_rain = true; + } + + if (survey_results->at(x).at(y).reanimating[mlt->biome_offset]) { + site_info->reanimating = true; + } + + if (survey_results->at(x).at(y).thralling[mlt->biome_offset]) { + site_info->thralling = true; + } + } + + //================================================================================= + + void process_embark_incursion_mid_level_tile(uint8_t from_direction, + embark_assist::defs::site_infos *site_info, + embark_assist::defs::world_tile_data *survey_results, + embark_assist::defs::mid_level_tiles *mlt, + uint8_t i, + uint8_t k) { + + int8_t fetch_i = i; + int8_t fetch_k = k; + int16_t fetch_x = state->x; + int16_t fetch_y = state->y; + df::world_data *world_data = world->world_data; + + // Logic can be implemented with modulo and division, but that's harder to read. + switch (from_direction) { + case 0: + fetch_i = i - 1; + fetch_k = k - 1; + break; + + case 1: + fetch_k = k - 1; + break; + + case 2: + fetch_i = i + 1; + fetch_k = k - 1; + break; + + case 3: + fetch_i = i - 1; + break; + + case 4: + return; // Own tile provides the data, so there's no incursion. + break; + + case 5: + fetch_i = i + 1; + break; + + case 6: + fetch_i = i - 1; + fetch_k = k + 1; + break; + + case 7: + fetch_k = k + 1; + break; + + case 8: + fetch_i = i + 1; + fetch_k = k + 1; + break; + } + + if (fetch_i < 0) { + fetch_x = state->x - 1; + } + else if (fetch_i > 15) { + fetch_x = state->x + 1; + } + + if (fetch_k < 0) { + fetch_y = state->y - 1; + } + else if (fetch_k > 15) { + fetch_y = state->y + 1; + } + + if (fetch_x < 0 || + fetch_x == world_data->world_width || + fetch_y < 0 || + fetch_y == world_data->world_height) { + return; // We're at the world edge, so no incursions from the outside. + } + + if (fetch_k < 0) { + if (fetch_i < 0) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[15], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else if (fetch_i > 15) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[0], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).south_row[i], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + } + else if (fetch_k > 15) { + if (fetch_i < 0) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[15], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else if (fetch_i > 15) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[0], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).north_row[i], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + } + else { + if (fetch_i < 0) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).east_column[k], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else if (fetch_i > 15) { + process_embark_incursion(site_info, + survey_results, + &survey_results->at(fetch_x).at(fetch_y).west_column[k], + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + else { + process_embark_incursion(site_info, + survey_results, + &mlt->at(fetch_i).at(fetch_k), + mlt->at(i).at(k).elevation, + fetch_x, + fetch_y); + } + } + } } } @@ -1051,8 +1268,111 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data } tile->biome_count = count; + + for (uint8_t i = 0; i < 16; i++) { + tile->north_row[i].aquifer = mlt->at(i).at(0).aquifer; + tile->south_row[i].aquifer = mlt->at(i).at(15).aquifer; + tile->west_column[i].aquifer = mlt->at(0).at(i).aquifer; + tile->east_column[i].aquifer = mlt->at(15).at(i).aquifer; + + tile->north_row[i].clay= mlt->at(i).at(0).clay; + tile->south_row[i].clay = mlt->at(i).at(15).clay; + tile->west_column[i].clay = mlt->at(0).at(i).clay; + tile->east_column[i].clay = mlt->at(15).at(i).clay; + + tile->north_row[i].sand = mlt->at(i).at(0).sand; + tile->south_row[i].sand = mlt->at(i).at(15).sand; + tile->west_column[i].sand = mlt->at(0).at(i).sand; + tile->east_column[i].sand = mlt->at(15).at(i).sand; + + tile->north_row[i].flux = mlt->at(i).at(0).flux; // Not used + tile->south_row[i].flux = mlt->at(i).at(15).flux; + tile->west_column[i].flux = mlt->at(0).at(i).flux; + tile->east_column[i].flux = mlt->at(15).at(i).flux; + + tile->north_row[i].coal = mlt->at(i).at(0).coal; // Not used + tile->south_row[i].coal = mlt->at(i).at(15).coal; + tile->west_column[i].coal = mlt->at(0).at(i).coal; + tile->east_column[i].coal = mlt->at(15).at(i).coal; + + tile->north_row[i].soil_depth = mlt->at(i).at(0).soil_depth; + tile->south_row[i].soil_depth = mlt->at(i).at(15).soil_depth; + tile->west_column[i].soil_depth = mlt->at(0).at(i).soil_depth; + tile->east_column[i].soil_depth = mlt->at(15).at(i).soil_depth; + + tile->north_row[i].offset = mlt->at(i).at(0).offset; // Not used + tile->south_row[i].offset = mlt->at(i).at(15).offset; + tile->west_column[i].offset = mlt->at(0).at(i).offset; + tile->east_column[i].offset = mlt->at(15).at(i).offset; + + tile->north_row[i].elevation = mlt->at(i).at(0).elevation; + tile->south_row[i].elevation = mlt->at(i).at(15).elevation; + tile->west_column[i].elevation = mlt->at(0).at(i).elevation; + tile->east_column[i].elevation = mlt->at(15).at(i).elevation; + + tile->north_row[i].river_present = mlt->at(i).at(0).river_present; // Not used + tile->south_row[i].river_present = mlt->at(i).at(15).river_present; + tile->west_column[i].river_present = mlt->at(0).at(i).river_present; + tile->east_column[i].river_present = mlt->at(15).at(i).river_present; + + tile->north_row[i].river_elevation = mlt->at(i).at(0).river_elevation; // Not used + tile->south_row[i].river_elevation = mlt->at(i).at(15).river_elevation; + tile->west_column[i].river_elevation = mlt->at(0).at(i).river_elevation; + tile->east_column[i].river_elevation = mlt->at(15).at(i).river_elevation; + + tile->north_row[i].adamantine_level = mlt->at(i).at(0).adamantine_level; // Not used + tile->south_row[i].adamantine_level = mlt->at(i).at(15).adamantine_level; + tile->west_column[i].adamantine_level = mlt->at(0).at(i).adamantine_level; + tile->east_column[i].adamantine_level = mlt->at(15).at(i).adamantine_level; + + tile->north_row[i].magma_level = mlt->at(i).at(0).magma_level; // Not used + tile->south_row[i].magma_level = mlt->at(i).at(15).magma_level; + tile->west_column[i].magma_level = mlt->at(0).at(i).magma_level; + tile->east_column[i].magma_level = mlt->at(15).at(i).magma_level; + + tile->north_row[i].biome_offset = mlt->at(i).at(0).biome_offset; + tile->south_row[i].biome_offset = mlt->at(i).at(15).biome_offset; + tile->west_column[i].biome_offset = mlt->at(0).at(i).biome_offset; + tile->east_column[i].biome_offset = mlt->at(15).at(i).biome_offset; + + tile->north_row[i].savagery_level = mlt->at(i).at(0).savagery_level; + tile->south_row[i].savagery_level = mlt->at(i).at(15).savagery_level; + tile->west_column[i].savagery_level = mlt->at(0).at(i).savagery_level; + tile->east_column[i].savagery_level = mlt->at(15).at(i).savagery_level; + + tile->north_row[i].evilness_level = mlt->at(i).at(0).evilness_level; + tile->south_row[i].evilness_level = mlt->at(i).at(15).evilness_level; + tile->west_column[i].evilness_level = mlt->at(0).at(i).evilness_level; + tile->east_column[i].evilness_level = mlt->at(15).at(i).evilness_level; + + tile->north_row[i].metals.resize(0); // Not used + tile->south_row[i].metals.resize(0); + tile->west_column[i].metals.resize(0); + tile->east_column[i].metals.resize(0); + + tile->north_row[i].economics.resize(0); // Not used + tile->south_row[i].economics.resize(0); + tile->west_column[i].economics.resize(0); + tile->east_column[i].economics.resize(0); + + tile->north_row[i].minerals.resize(0); // Not used + tile->south_row[i].minerals.resize(0); + tile->west_column[i].minerals.resize(0); + tile->east_column[i].minerals.resize(0); + + tile->north_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[i][0]; + tile->west_corner_selection[i] = world_data->region_details[0]->edges.biome_corner[0][i]; + } + + for (uint8_t i = 0; i < 16; i++) { + for (uint8_t k = 0; k < 16; k++) { + tile->region_type[i][k] = world_data->regions[tile->biome[mlt->at(i).at(k).biome_offset]]->type; + } + } + tile->surveyed = true; } + //================================================================================= df::coord2d embark_assist::survey::apply_offset(uint16_t x, uint16_t y, int8_t offset) { @@ -1124,6 +1444,454 @@ df::coord2d embark_assist::survey::apply_offset(uint16_t x, uint16_t y, int8_t o //================================================================================= +void adjust_coordinates(int16_t *x, int16_t *y, int8_t *i, int8_t *k) { + if (*i < 0) { + *x = *x - 1; + *i = *i + 16; + } + else if (*i > 15) { + *x = *x + 1; + *i = *i - 16; + } + + if (*k < 0) { + *y = *y - 1; + *k = *k + 16; + } + else if (*k > 15) { + *y = *y + 1; + *k = *k - 16; + } +} + +//================================================================================= + +df::world_region_type embark_assist::survey::region_type_of(embark_assist::defs::world_tile_data *survey_results, + int16_t x, + int16_t y, + int8_t i, + int8_t k) { + + df::world_data *world_data = world->world_data; + int16_t effective_x = x; + int16_t effective_y = y; + int8_t effective_i = i; + int8_t effective_k = k; + adjust_coordinates(&effective_x, &effective_y, &effective_i, &effective_i); + + if (effective_x < 0 || + effective_x >= world_data->world_width || + effective_y < 0 || + effective_y >= world_data->world_height) { + return df::world_region_type::Lake; // Essentially dummy value, yielding to everything. It will + // be handled properly later anyway. + } + + return survey_results->at(effective_x).at(effective_y).region_type[effective_i][effective_k]; +} + +//================================================================================= + +uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile_data *survey_results, + uint8_t corner_location, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k) { + + df::world_data *world_data = world->world_data; + df::world_region_type nw_region_type; + df::world_region_type n_region_type; + df::world_region_type w_region_type; + df::world_region_type home_region_type; + + int16_t effective_x = x; + int16_t effective_y = y; + int8_t effective_i = i; + int8_t effective_k = k; + + uint8_t effective_corner; + bool nw_region_type_active; + bool n_region_type_active; + bool w_region_type_active; + bool home_region_type_active; + uint8_t nw_region_type_level; + uint8_t n_region_type_level; + uint8_t w_region_type_level; + uint8_t home_region_type_level; + + if (corner_location == 4) { // We're the reference. No change. + } + else if (corner_location == 5) { // Tile to the east is the reference + effective_i = i + 1; + } + else if (corner_location == 7) { // Tile to the south is the reference + effective_k = k + 1; + } + else { // 8, tile to the southeast is the reference. + effective_i = i + 1; + effective_k = k + 1; + } + + adjust_coordinates(&effective_x, &effective_y, &effective_i, &effective_i); + + if (effective_x < 0 || effective_x == world_data->world_width || + effective_y < 0 || effective_y == world_data->world_height) { + effective_x = x; + effective_y = y; + effective_i = i; + effective_k = k; + } + + if (effective_x == x && effective_y == y) { + effective_corner = world_data->region_details[0]->edges.biome_corner[effective_i][effective_k]; + } + else if (effective_y != y) { + effective_corner = survey_results->at(effective_x).at(effective_y).north_corner_selection[effective_i]; + } + else { + effective_corner = survey_results->at(effective_x).at(effective_y).west_corner_selection[effective_k]; + } + + nw_region_type = embark_assist::survey::region_type_of(survey_results, x, y, effective_i - 1, effective_k - 1); + n_region_type = embark_assist::survey::region_type_of(survey_results, x, y, effective_i, effective_k - 1); + w_region_type = embark_assist::survey::region_type_of(survey_results, x, y, effective_i - 1, effective_k); + home_region_type = embark_assist::survey::region_type_of(survey_results, x, y, effective_i, effective_k); + + if (nw_region_type == df::world_region_type::Lake || + nw_region_type == df::world_region_type::Ocean) { + nw_region_type_level = 0; + } + else if (nw_region_type == df::world_region_type::Mountains) { + nw_region_type_level = 1; + } + else { + nw_region_type_level = 2; + } + + if (n_region_type == df::world_region_type::Lake || + n_region_type == df::world_region_type::Ocean) { + n_region_type_level = 0; + } + else if (n_region_type == df::world_region_type::Mountains) { + n_region_type_level = 1; + } + else { + n_region_type_level = 2; + } + + if (w_region_type == df::world_region_type::Lake || + w_region_type == df::world_region_type::Ocean) { + w_region_type_level = 0; + } + else if (w_region_type == df::world_region_type::Mountains) { + w_region_type_level = 1; + } + else { + w_region_type_level = 2; + } + + if (home_region_type == df::world_region_type::Lake || + home_region_type == df::world_region_type::Ocean) { + home_region_type_level = 0; + } + else if (n_region_type == df::world_region_type::Mountains) { + home_region_type_level = 1; + } + else { + home_region_type_level = 2; + } + + nw_region_type_active = nw_region_type_level >= n_region_type_level && + nw_region_type_level >= w_region_type_level && + nw_region_type_level >= home_region_type_level; + + n_region_type_active = n_region_type_level >= nw_region_type_level && + n_region_type_level >= w_region_type_level && + n_region_type_level >= home_region_type_level; + + w_region_type_active = w_region_type_level >= nw_region_type_level && + w_region_type_level >= n_region_type_level && + w_region_type_level >= home_region_type_level; + + home_region_type_active = home_region_type_level >= nw_region_type_level && + home_region_type_level >= n_region_type_level && + home_region_type_level >= w_region_type_level; + + if ((effective_corner == 0 && !nw_region_type_active) || + (effective_corner == 1 && !n_region_type_active) || + (effective_corner == 2 && !w_region_type_active) || + (effective_corner == 3 && !home_region_type_active)) { + //### The designated corner is suppressed, but we do not have any + // knowledge of how to select a replacement. Thus, the presedence + // list used is fairly arbitrary: Use the lowest number available. + if (nw_region_type_active) { + effective_corner = 0; + } + else if (n_region_type_active) { + effective_corner = 1; + } + else if (w_region_type_active) { + effective_corner = 2; + } + else { + effective_corner = 3; + } + } + + switch (effective_corner) { + case 0: + return corner_location - 4; + break; + + case 1: + return corner_location - 3; + break; + + case 2: + return corner_location - 1; + break; + + case 3: + return corner_location; + break; + } + + return -1; // Should never happen + + /* The logic that's reduced to the code above. + switch (corner_location) { + case 0: // N/A Not to the north or west + case 1: // N/A + case 2: // N/A + case 3: // N/A + case 6: // N/A + return -1; // Should never happen + break; + + case 4: // Self + switch (corner) { + case 0: + return 0; // Northwest + break; + + case 1: + return 1; // North + break; + + case 2: + return 3; // West + break; + + case 3: + return 4; // Self + } + + case 5: // East + switch (corner) { + case 0: + return 1; // North + break; + + case 1: + return 2; // Northeast + break; + + case 2: + return 4; // Self + break; + + case 3: + return 5; // East + } + case 7: // South + switch (corner) { + case 0: + return 3; // West + break; + + case 1: + return 4; // Self + break; + + case 2: + return 6; // Southwest + break; + + case 3: + return 7; // South + } + + case 8: // Southeast + switch (corner) { + case 0: + return 4; // Self + break; + + case 1: + return 5; // East + break; + + case 2: + return 7; // South + break; + + case 3: + return 8; // Southeast + } + } + */ +} + +//================================================================================= + +uint8_t embark_assist::survey::translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, + bool own_edge, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k) { + + df::world_data *world_data = world->world_data; + uint8_t effective_edge; + df::world_region_type north_region_type; + df::world_region_type south_region_type; + + if (own_edge) { + effective_edge = world_data->region_details[0]->edges.biome_x[i][k]; + south_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); + north_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k - 1); + } + else { + effective_edge = world_data->region_details[0]->edges.biome_x[i][k + 1]; + north_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); + south_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k + 1); + } + + // Apply rules for Ocean && Lake to yield to everything else, + // and Mountain to everything but those. + // + if ((north_region_type == df::world_region_type::Lake || + north_region_type == df::world_region_type::Ocean) && + south_region_type != df::world_region_type::Lake && + south_region_type != df::world_region_type::Ocean) { + effective_edge = 1; + } + + if ((south_region_type == df::world_region_type::Lake || + south_region_type == df::world_region_type::Ocean) && + north_region_type != df::world_region_type::Lake && + north_region_type != df::world_region_type::Ocean) { + effective_edge = 0; + } + + if (north_region_type == df::world_region_type::Mountains && + south_region_type != df::world_region_type::Lake && + south_region_type != df::world_region_type::Ocean && + south_region_type != df::world_region_type::Mountains) { + effective_edge = 1; + } + + if (south_region_type == df::world_region_type::Mountains && + north_region_type != df::world_region_type::Lake && + north_region_type != df::world_region_type::Ocean && + north_region_type != df::world_region_type::Mountains) { + effective_edge = 0; + } + + if (effective_edge == 0) { + if (own_edge) { + return 1; + } + else { + return 4; + } + } + else { + if (own_edge) { + return 4; + } + else { + return 7; + } + } +} + +//================================================================================= + +uint8_t embark_assist::survey::translate_ew_edge(embark_assist::defs::world_tile_data *survey_results, + bool own_edge, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k) { + + df::world_data *world_data = world->world_data; + uint8_t effective_edge; + df::world_region_type west_region_type; + df::world_region_type east_region_type; + + if (own_edge) { + effective_edge = world_data->region_details[0]->edges.biome_x[i][k]; + east_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); + west_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i - 1, k); + } + else { + effective_edge = world_data->region_details[0]->edges.biome_x[i + 1][k]; + west_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i, k); + east_region_type = embark_assist::survey::region_type_of(survey_results, x, y, i + 1, k); + } + + // Apply rules for Ocean && Lake to yield to everything else, + // and Mountain to everything but those. + // + if ((west_region_type == df::world_region_type::Lake || + west_region_type == df::world_region_type::Ocean) && + east_region_type != df::world_region_type::Lake && + east_region_type != df::world_region_type::Ocean) { + effective_edge = 1; + } + + if ((east_region_type == df::world_region_type::Lake || + east_region_type == df::world_region_type::Ocean) && + west_region_type != df::world_region_type::Lake && + west_region_type != df::world_region_type::Ocean) { + effective_edge = 0; + } + + if (west_region_type == df::world_region_type::Mountains && + west_region_type != df::world_region_type::Lake && + east_region_type != df::world_region_type::Ocean && + east_region_type != df::world_region_type::Mountains) { + effective_edge = 1; + } + + if (east_region_type == df::world_region_type::Mountains && + east_region_type != df::world_region_type::Lake && + west_region_type != df::world_region_type::Ocean && + west_region_type != df::world_region_type::Mountains) { + effective_edge = 0; + } + if (effective_edge == 0) { + if (own_edge) { + return 3; + } + else { + return 4; + } + } + else { + if (own_edge) { + return 4; + } + else { + return 5; + } + } +} + +//================================================================================= + void embark_assist::survey::survey_region_sites(embark_assist::defs::site_lists *site_list) { // color_ostream_proxy out(Core::getInstance().getConsole()); auto screen = Gui::getViewscreenByType(0); @@ -1196,6 +1964,7 @@ void embark_assist::survey::survey_region_sites(embark_assist::defs::site_lists //================================================================================= void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *mlt, + embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::site_infos *site_info, bool use_cache) { @@ -1207,7 +1976,8 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * std::vector metals(state->max_inorganic); std::vector economics(state->max_inorganic); std::vector minerals(state->max_inorganic); - bool flatness_verification_failed; + bool incursion_processing_failed = false; + df::world_data *world_data = world->world_data; if (!use_cache) { // For some reason DF scrambles these values on world tile movements (at least in Lua...). state->local_min_x = screen->location.embark_pos_min.x; @@ -1219,16 +1989,22 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * state->x = x; state->y = y; + site_info->incursions_processed = true; site_info->aquifer = false; site_info->aquifer_full = true; site_info->min_soil = 10; site_info->max_soil = 0; - site_info->flatness = embark_assist::defs::flatnesses::Mostly_Flat; + site_info->flat = true; site_info->max_waterfall = 0; site_info->clay = false; site_info->sand = false; site_info->flux = false; site_info->coal = false; + site_info->blood_rain = false; + site_info->permanent_syndrome_rain = false; + site_info->temporary_syndrome_rain = false; + site_info->reanimating = false; + site_info->thralling = false; site_info->metals.clear(); site_info->economics.clear(); site_info->metals.clear(); @@ -1254,7 +2030,7 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * elevation = mlt->at(i).at(k).elevation; } else if (elevation != mlt->at(i).at(k).elevation) { - site_info->flatness = embark_assist::defs::flatnesses::Uneven; + site_info->flat = false; } if (mlt->at(i).at(k).river_present) { @@ -1291,42 +2067,31 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * site_info->coal = true; } - for (uint16_t l = 0; l < state->max_inorganic; l++) { - metals[l] = metals[l] || mlt->at(i).at(k).metals[l]; - economics[l] = economics[l] || mlt->at(i).at(k).economics[l]; - minerals[l] = minerals[l] || mlt->at(i).at(k).minerals[l]; + if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) { + site_info->blood_rain = true; } - } - } - if (site_info->flatness == embark_assist::defs::flatnesses::Mostly_Flat && - state->local_min_x > 0 && - state->local_max_x < 15 && - state->local_min_y > 0 && - state->local_max_y < 15) - { - flatness_verification_failed = false; + if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->at(i).at(k).biome_offset]) { + site_info->permanent_syndrome_rain = true; + } - for (uint8_t i = state->local_min_x - 1; i <= state->local_max_x + 1; i++) { - if (mlt->at(i).at(state->local_min_y - 1).elevation != elevation || - mlt->at(i).at(state->local_max_y + 1).elevation != elevation) { - flatness_verification_failed = true; - break; + if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->at(i).at(k).biome_offset]) { + site_info->temporary_syndrome_rain = true; } - } - if (!flatness_verification_failed) { - for (uint8_t k = state->local_min_y; k <= state->local_max_y; k++) { - if (mlt->at(state->local_min_x - 1).at(k).elevation != elevation || - mlt->at(state->local_max_x + 1).at(k).elevation != elevation) { - flatness_verification_failed = true; - break; - } + if (survey_results->at(x).at(y).reanimating[mlt->at(i).at(k).biome_offset]) { + site_info->reanimating = true; + } + + if (survey_results->at(x).at(y).thralling[mlt->at(i).at(k).biome_offset]) { + site_info->thralling = true; } - } - if (!flatness_verification_failed) { - site_info->flatness = embark_assist::defs::flatnesses::Flat_Verified; + for (uint16_t l = 0; l < state->max_inorganic; l++) { + metals[l] = metals[l] || mlt->at(i).at(k).metals[l]; + economics[l] = economics[l] || mlt->at(i).at(k).economics[l]; + minerals[l] = minerals[l] || mlt->at(i).at(k).minerals[l]; + } } } @@ -1343,6 +2108,250 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles * site_info->minerals.push_back(l); } } + + // Take incursions into account. + + for (int8_t i = state->local_min_x; i <= state->local_max_x; i++) { + // NW corner, north row + if ((i == 0 && state->local_min_y == 0 && x - 1 >= 0 && y - 1 >= 0 && !survey_results->at(x - 1).at (y - 1).surveyed) || + (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || + (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 4, + x, + y, + i, + state->local_min_y), + site_info, + survey_results, + mlt, + i, + state->local_min_y); + } + + // N edge, north row + if (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_ns_edge(survey_results, + true, + x, + y, + i, + state->local_min_y), + site_info, + survey_results, + mlt, + i, + state->local_min_y); + } + + // NE corner, north row + if ((i == 15 && state->local_min_y == 0 && x + 1 < world_data->world_width && y - 1 >= 0 && !survey_results->at(x + 1).at(y - 1).surveyed) || + (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || + (state->local_min_y == 0 && y - 1 >= 0 && !survey_results->at(x).at(y - 1).surveyed)) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 5, + x, + y, + i, + state->local_min_y), + site_info, + survey_results, + mlt, + i, + state->local_min_y); + } + + // SW corner, south row + if ((i == 0 && state->local_max_y == 15 && x - 1 >= 0 && y + 1 < world_data->world_height && !survey_results->at(x - 1).at(y + 1).surveyed) || + (i == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) || + (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 7, + x, + y, + i, + state->local_max_y), + site_info, + survey_results, + mlt, + i, + state->local_max_y); + } + + // S edge, south row + if (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_ns_edge(survey_results, + false, + x, + y, + i, + state->local_max_y), + site_info, + survey_results, + mlt, + i, + state->local_max_y); + } + + // SE corner south row + if ((i == 15 && state->local_max_y == 15 && x + 1 < world_data->world_width && y + 1 < world_data->world_height && !survey_results->at(x + 1).at(y + 1).surveyed) || + (i == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) || + (state->local_max_y == 15 && y + 1 < world_data->world_height && !survey_results->at(x).at(y + 1).surveyed)) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 8, + x, + y, + i, + state->local_max_y), + site_info, + survey_results, + mlt, + i, + state->local_max_y); + } + } + + for (int8_t k = state->local_min_y; k <= state->local_max_y; k++) { + // NW corner, west side + if ((state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed)) { + incursion_processing_failed = true; + } + else if (k > state->local_min_y) { // We've already covered the NW corner of the NW, with its complications. + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 4, + x, + y, + state->local_min_x, + k), + site_info, + survey_results, + mlt, + state->local_min_x, + k); + } + + // W edge, west side + if (state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_ns_edge(survey_results, + true, + x, + y, + state->local_min_x, + k), + site_info, + survey_results, + mlt, + state->local_min_x, + k); + } + + // SW corner, west side + if (state->local_min_x == 0 && x - 1 >= 0 && !survey_results->at(x - 1).at(y).surveyed) { + incursion_processing_failed = true; + } + else if (k < state->local_max_y) { // We've already covered the SW corner of the SW tile, with its complicatinons. + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 7, + x, + y, + state->local_min_x, + k), + site_info, + survey_results, + mlt, + state->local_min_x, + k); + } + + // NE corner, east side + if ((state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed)) { + incursion_processing_failed = true; + } + else if (k > state->local_min_y) { // We've already covered the NE tile's NE corner, with its complications. + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 5, + x, + y, + state->local_max_x, + k), + site_info, + survey_results, + mlt, + state->local_max_x, + k); + } + + // E edge, east side + if (state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { + incursion_processing_failed = true; + } + else { + process_embark_incursion_mid_level_tile + (translate_ns_edge(survey_results, + false, + x, + y, + state->local_max_x, + k), + site_info, + survey_results, + mlt, + state->local_max_x, + k); + } + + // SE corner, east side + if (state->local_max_x == 15 && x + 1 < world_data->world_width && !survey_results->at(x + 1).at(y).surveyed) { + incursion_processing_failed = true; + } + else if (k < state->local_max_y) { // We've already covered the SE tile's SE corner, with its complications. + process_embark_incursion_mid_level_tile + (translate_corner(survey_results, + 8, + x, + y, + state->local_max_x, + k), + site_info, + survey_results, + mlt, + state->local_max_x, + k); + } + } + + if (incursion_processing_failed) site_info->incursions_processed = false; } //================================================================================= diff --git a/plugins/embark-assistant/survey.h b/plugins/embark-assistant/survey.h index 03d2d9a03..5f201b04f 100644 --- a/plugins/embark-assistant/survey.h +++ b/plugins/embark-assistant/survey.h @@ -27,9 +27,52 @@ namespace embark_assist { df::coord2d apply_offset(uint16_t x, uint16_t y, int8_t offset); + df::world_region_type region_type_of(embark_assist::defs::world_tile_data *survey_results, + int16_t x, + int16_t y, + int8_t i, + int8_t k); + + // Returns the direction from which data should be read using DF's + // 0 - 8 range direction indication. + // "corner_location" uses the 0 - 8 notation to indicate the reader's + // relation to the data read. Only some values are legal, as the set of + // relations is limited by how the corners are defined. + // x, y, i, k are the world tile/mid level tile coordinates of the + // tile the results should be applied to. + // Deals with references outside of the world map by returning "yield" + // results, but requires all world tiles affected by the corner to have + // been surveyed. + // + uint8_t translate_corner(embark_assist::defs::world_tile_data *survey_results, + uint8_t corner_location, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k); + + // Same logic and restrictions as for translate_corner. + // + uint8_t translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, + bool own_edge, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k); + + // Same logic and restrictions as for translate_corner. + // + uint8_t translate_ew_edge(embark_assist::defs::world_tile_data *survey_results, + bool own_edge, + uint16_t x, + uint16_t y, + uint8_t i, + uint8_t k); + void survey_region_sites(embark_assist::defs::site_lists *site_list); void survey_embark(embark_assist::defs::mid_level_tiles *mlt, + embark_assist::defs::world_tile_data *survey_results, embark_assist::defs::site_infos *site_info, bool use_cache); From 6f5abc79e96e9dbe42dd1091c40223fdee1c472d Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Fri, 12 Jul 2019 22:38:12 +0200 Subject: [PATCH 38/44] Restored install-debug contents and removed offensive white space --- build/win64/install-debug.bat | 2 +- plugins/embark-assistant/embark-assistant.cpp | 2 +- plugins/embark-assistant/survey.cpp | 2 +- plugins/embark-assistant/survey.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/win64/install-debug.bat b/build/win64/install-debug.bat index 64b5ae850..d149a5d86 100644 --- a/build/win64/install-debug.bat +++ b/build/win64/install-debug.bat @@ -1,4 +1,4 @@ -call "D:\Program (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 cd VC2015 msbuild /m /p:Platform=x64 /p:Configuration=RelWithDebInfo INSTALL.vcxproj cd .. \ No newline at end of file diff --git a/plugins/embark-assistant/embark-assistant.cpp b/plugins/embark-assistant/embark-assistant.cpp index 22dd21298..1eed98593 100644 --- a/plugins/embark-assistant/embark-assistant.cpp +++ b/plugins/embark-assistant/embark-assistant.cpp @@ -275,7 +275,7 @@ command_result embark_assistant(color_ostream &out, std::vector & } 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::main::state->survey_results[i][k].minerals.resize(embark_assist::main::state->max_inorganic); } } diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index a2942d415..30ba9d441 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -1745,7 +1745,7 @@ uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile //================================================================================= -uint8_t embark_assist::survey::translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, +uint8_t embark_assist::survey::translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, bool own_edge, uint16_t x, uint16_t y, diff --git a/plugins/embark-assistant/survey.h b/plugins/embark-assistant/survey.h index 5f201b04f..14cc8a468 100644 --- a/plugins/embark-assistant/survey.h +++ b/plugins/embark-assistant/survey.h @@ -53,7 +53,7 @@ namespace embark_assist { // Same logic and restrictions as for translate_corner. // - uint8_t translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, + uint8_t translate_ns_edge(embark_assist::defs::world_tile_data *survey_results, bool own_edge, uint16_t x, uint16_t y, From af26f7dc6e30fde1cd87be10cc026f6d2f6d10c8 Mon Sep 17 00:00:00 2001 From: PatrikLundell Date: Sun, 14 Jul 2019 16:11:51 +0200 Subject: [PATCH 39/44] Added intrusion calculations for world edges --- plugins/embark-assistant/survey.cpp | 50 +++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index 30ba9d441..5bed173fa 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -1535,12 +1535,26 @@ uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile adjust_coordinates(&effective_x, &effective_y, &effective_i, &effective_i); - if (effective_x < 0 || effective_x == world_data->world_width || - effective_y < 0 || effective_y == world_data->world_height) { - effective_x = x; - effective_y = y; - effective_i = i; - effective_k = k; + if (effective_x == world_data->world_width) { + if (effective_y == world_data->world_height) { // Only the SE corner of the SE most tile of the world can reference this. + return 4; + } + else { // East side corners of the east edge of the world + if (corner_location == 5) { + return 1; + } + else { // Can only be corner_location == 8 + return 4; + } + } + } + else if (effective_y == world_data->world_height) { + if (corner_location == 7) { + return 4; + } + else { // Can only be corner_location == 8 + return 3; + } } if (effective_x == x && effective_y == y) { @@ -1602,6 +1616,25 @@ uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile home_region_type_level = 2; } + if (effective_x == 0 && effective_i == 0) { // West edge of the world + if (effective_y == 0 && effective_k == 0) { + return 4; // Only a single reference to this info, the own tile. + } + else { + nw_region_type_level = -1; // Knock out the unreachable corners + w_region_type_level = -1; + } + } + + if (effective_y == 0 && effective_k == 0) { // North edge of the world + nw_region_type_level = -1; // Knock out the unreachable corners + n_region_type_level = -1; + + if (corner_location == 4 && effective_corner == 1) { // The logic below would select the wrong alternative. + effective_corner = 3; + } + } + nw_region_type_active = nw_region_type_level >= n_region_type_level && nw_region_type_level >= w_region_type_level && nw_region_type_level >= home_region_type_level; @@ -1622,9 +1655,8 @@ uint8_t embark_assist::survey::translate_corner(embark_assist::defs::world_tile (effective_corner == 1 && !n_region_type_active) || (effective_corner == 2 && !w_region_type_active) || (effective_corner == 3 && !home_region_type_active)) { - //### The designated corner is suppressed, but we do not have any - // knowledge of how to select a replacement. Thus, the presedence - // list used is fairly arbitrary: Use the lowest number available. + // The designated corner is suppressed. The precedence list below seems + // to match what DF produces except in the case adjusted above. if (nw_region_type_active) { effective_corner = 0; } From b19a04fa88cc03aee6be97b96b19b4c35e90455d Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 22 Aug 2019 21:24:48 -0400 Subject: [PATCH 40/44] Revert newline change --- build/win64/install-debug.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/win64/install-debug.bat b/build/win64/install-debug.bat index d149a5d86..34668945c 100644 --- a/build/win64/install-debug.bat +++ b/build/win64/install-debug.bat @@ -1,4 +1,4 @@ call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 cd VC2015 msbuild /m /p:Platform=x64 /p:Configuration=RelWithDebInfo INSTALL.vcxproj -cd .. \ No newline at end of file +cd .. From 100e584c3435edca39216550c05c5997a0ef401f Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 23 Aug 2019 20:30:06 -0400 Subject: [PATCH 41/44] Fix saving persistent data on regular save --- library/Core.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index da0de12de..0dd02f12d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -136,6 +136,8 @@ struct Core::Private { std::thread iothread; std::thread hotkeythread; + + bool last_autosave_request{false}; }; struct CommandDepthCounter @@ -1965,6 +1967,14 @@ void Core::doUpdate(color_ostream &out, bool first_update) vs_changed = true; } + // save data (do this before updating last_world_data_ptr and triggering unload events) + if ((df::global::ui->main.autosave_request && !d->last_autosave_request) || + (vs_changed && strict_virtual_cast(screen))) + { + doSaveData(out); + } + d->last_autosave_request = df::global::ui->main.autosave_request; + bool is_load_save = strict_virtual_cast(screen) || strict_virtual_cast(screen) || @@ -2026,11 +2036,6 @@ void Core::doUpdate(color_ostream &out, bool first_update) // Execute per-frame handlers onUpdate(out); - if (df::global::ui->main.autosave_request || (vs_changed && strict_virtual_cast(screen))) - { - doSaveData(out); - } - out << std::flush; } From 2ef46562c811bb220d2073477a4c77127cb70a9b Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 23 Aug 2019 20:45:35 -0400 Subject: [PATCH 42/44] Avoid calling doSaveData twice when unloading a world This was happening because of a switch to viewscreen_game_cleanerst and back to viewscreen_savegamest when saving --- library/Core.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 0dd02f12d..380f37965 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -138,6 +138,7 @@ struct Core::Private std::thread hotkeythread; bool last_autosave_request{false}; + bool was_load_save{false}; }; struct CommandDepthCounter @@ -1967,18 +1968,17 @@ void Core::doUpdate(color_ostream &out, bool first_update) vs_changed = true; } + bool is_load_save = + strict_virtual_cast(screen) || + strict_virtual_cast(screen) || + strict_virtual_cast(screen); + // save data (do this before updating last_world_data_ptr and triggering unload events) if ((df::global::ui->main.autosave_request && !d->last_autosave_request) || - (vs_changed && strict_virtual_cast(screen))) + (is_load_save && !d->was_load_save && strict_virtual_cast(screen))) { doSaveData(out); } - d->last_autosave_request = df::global::ui->main.autosave_request; - - bool is_load_save = - strict_virtual_cast(screen) || - strict_virtual_cast(screen) || - strict_virtual_cast(screen); // detect if the game was loaded or unloaded in the meantime void *new_wdata = NULL; @@ -2036,6 +2036,9 @@ void Core::doUpdate(color_ostream &out, bool first_update) // Execute per-frame handlers onUpdate(out); + d->last_autosave_request = df::global::ui->main.autosave_request; + d->was_load_save = is_load_save; + out << std::flush; } From 222b88063c4eb1729ca83bb2a0def38989e39003 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 23 Aug 2019 21:37:50 -0400 Subject: [PATCH 43/44] Update submodules --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index c192f9798..4388fbfb8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c192f9798b5d134e777b1f37c8ebc0df4bd53bda +Subproject commit 4388fbfb8f51be41777406c6e7c518f738c195c7 diff --git a/scripts b/scripts index 189d0d8f6..8ef283377 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 189d0d8f63b5d34ac3f779254b6ab5ff4763459a +Subproject commit 8ef283377c9830fb932ea888d89b551873af36cf From 01ce954c294a7dc7532b1998f04b08199980214c Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 23 Aug 2019 22:17:09 -0400 Subject: [PATCH 44/44] changelog: update with scripts changes since 0.44.12-r2 and clean up --- docs/changelog.txt | 69 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 68e38b9dd..47dcaa58b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,27 +37,74 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ================================================================================ # Future +## New Scripts +- `dwarf-op`: optimizes dwarves for fort-mode work; makes managing labors easier +- `forget-dead-body`: removes emotions associated with seeing a dead body +- `gui/create-tree`: creates a tree at the selected tile +- `linger`: takes over your killer in adventure mode +- `modtools/create-tree`: creates a tree +- `modtools/spawn-liquid`: spawns water or lava at the specified coordinates +- `unretire-anyone`: turns any historical figure into a playable adventurer + ## Fixes +- `bodyswap`: fixed companion list not being updated often enough +- `digfort`: now accounts for z-level changes when calculating maximum y dimension - `embark-assistant`: - fixed bug causing crash on worlds without generated metals (as well as pruning vectors as originally intended). - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently). +- `gui/autogems`: fixed error when no world is loaded +- `gui/companion-order`: + - fixed error when resetting group leaders + - ``leave`` now properly removes companion links +- `gui/create-item`: fixed module support - can now be used from other scripts +- `gui/stamper`: + - stopped "invert" from resetting the designation type + - switched to using DF's designation keybindings instead of custom bindings + - fixed some typos and text overlapping +- `modtools/create-unit`: + - fixed an error associating historical entities with units + - stopped recalculating health to avoid newly-created citizens triggering a "recover wounded" job + - fixed units created in arena mode having blank names + - fixed units created in arena mode having the wrong race and/or interaction effects applied after creating units manually in-game +- `modtools/reaction-product-trigger`: + - fixed an error dealing with reactions in adventure mode + - blocked ``\\BUILDING_ID`` for adventure mode reactions + - fixed ``-clear`` to work without passing other unneeded arguments +- `modtools/reaction-trigger`: + - fixed a bug when determining whether a command was run + - fixed handling of ``-resetPolicy`` +- `stonesense`: + - fixed crash due to wagons and other soul-less creatures +- `tame`: now sets the civ ID of tamed animals (fixes compatibility with `autobutcher`) ## Misc Improvements +- `bodyswap`: added arena mode support +- `combine-drinks`: added more default output, similar to `combine-plants` - `embark-assistant`: - - Changed Waterfall detection to look for level drop rather than just presence. - - Changed matching to take incursions, i.e. parts of other biomes, into consideration when evaluating tiles. This allows for e.g. finding multiple biomes on single tile embarks. - - Changed overlay display to show when incursion surveying is incomplete. - - Changed overlay display to show Evil Weather. + - changed waterfall detection to look for level drop rather than just presence + - changed matching to take incursions, i.e. parts of other biomes, into consideration when evaluating tiles. This allows for e.g. finding multiple biomes on single tile embarks. + - changed overlay display to show when incursion surveying is incomplete + - changed overlay display to show evil weather +- `exportlegends`: added rivers to custom XML export +- `exterminate`: added support for a special ``enemy`` caste +- `modtools/create-unit`: + - added the ability to specify ``\\LOCAL`` for the fort group entity +- `modtools/reaction-trigger`: + - added ``-ignoreWorker``: ignores the worker when selecting the targets + - changed the default behavior to skip inactive/dead units; added ``-dontSkipInactive`` to include creatures that are inactive + - added ``-range``: controls how far elligible targets can be from the workshop + - syndromes now are applied before commands are run, not after + - if both a command and a syndrome are given, the command only runs if the syndrome could be applied - `RemoteFortressReader`: - - added basic framework for controlling and reading the menus in DF, currently only supports the building menu. - - added support for reading item raws. - - added a check for wheather or not the game is currently saving or loading, for utilities to check if it's safe to read from DF. - - guestimate unit facing direction, and position within tiles. - - get unit age. - - get unit wounds. + - added a basic framework for controlling and reading the menus in DF (currently only supports the building menu) + - added support for reading item raws + - added a check for whether or not the game is currently saving or loading, for utilities to check if it's safe to read from DF + - added unit facing direction estimate and position within tiles + - added unit age + - added unit wounds ## Internals -- Fixed some OpenGL build issues with `stonesense` +- `stonesense`: fixed some OpenGL build issues on Linux # 0.44.12-r2