From ca9a0fb7d1f4713cfa3ab6e552ddcbc84439a5fa Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 17:29:39 -0700 Subject: [PATCH 01/65] progress towards cpp20 --- CMakeLists.txt | 13 ++++++------- depends/CMakeLists.txt | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed92b1eca..d031c8784 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,9 +35,10 @@ option(REMOVE_SYMBOLS_FROM_DF_STUBS "Remove debug symbols from DF stubs. (Reduce macro(CHECK_GCC compiler_path) execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT) string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT) - if(${GCC_VERSION_OUT} VERSION_LESS "4.8") - message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.8 or later") - elseif(${GCC_VERSION_OUT} VERSION_GREATER "4.9.9") + if(${GCC_VERSION_OUT} VERSION_LESS "10") + message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 10 or later") + # TODO: this may need to be removed when DF linux actually comes out + # TODO: and we can test # GCC 5 changes ABI name mangling to enable C++11 changes. # This must be disabled to enable linking against DF. # http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/ @@ -66,8 +67,8 @@ if(WIN32) endif() endif() -# Ask for C++11 standard from compilers -set(CMAKE_CXX_STANDARD 11) +# Ask for C++-20 standard from compilers +set(CMAKE_CXX_STANDARD 20) # Require the standard support from compilers. set(CMAKE_CXX_STANDARD_REQUIRED ON) # Use only standard c++ to keep code portable @@ -226,9 +227,7 @@ if(UNIX) ## flags for GCC # default to hidden symbols # ensure compatibility with older CPUs - # enable C++11 features add_definitions(-DLINUX_BUILD) - add_definitions(-D_GLIBCXX_USE_C99) set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}") diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index 15ff52488..566965f8a 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -1,14 +1,26 @@ # list depends here. add_subdirectory(lodepng) -add_subdirectory(lua) add_subdirectory(md5) + +add_subdirectory(lua) add_subdirectory(protobuf) +if(UNIX) + set_target_properties(lua PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-deprecated-enum-enum-conversion") + target_compile_options(protoc + PUBLIC -Wno-deprecated-declarations -Wno-restrict) + target_compile_options(protoc-bin + PUBLIC -Wno-deprecated-declarations -Wno-restrict) + target_compile_options(protobuf-lite + PUBLIC -Wno-deprecated-declarations -Wno-restrict) + target_compile_options(protobuf + PUBLIC -Wno-deprecated-declarations -Wno-restrict) +endif() if(UNIX AND NOT APPLE) # remove this once our MSVC build env has been updated option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF) add_subdirectory(googletest) if(UNIX) - set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized -Wno-sign-compare") + set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized -Wno-sign-compare -Wno-restrict") endif() endif() From 9dba18124eb1dd6f25b592fec46e85a8595f6896 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 17 Jun 2023 10:00:54 -0700 Subject: [PATCH 02/65] attach compile options to dfhack that allow protobuf headers to be included --- library/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 8681c9c90..1db78c24e 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -377,6 +377,9 @@ if(WIN32) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"" ) else() set_target_properties(dfhack PROPERTIES COMPILE_FLAGS "-include Export.h" ) + # required because of protobuf headers + target_compile_options(dfhack + PUBLIC -Wno-deprecated-declarations -Wno-restrict) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "-include Export.h" ) add_library(dfhooks SHARED Hooks.cpp) target_link_libraries(dfhooks dfhack) From 2efef75b4e4ca368f866bd104f123aaf79c8e568 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 17 Jun 2023 10:46:56 -0700 Subject: [PATCH 03/65] re-constify --- library/modules/DFSteam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 1fd064d56..103e2dfee 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -108,7 +108,7 @@ static bool is_running_on_wine() { return !!pwine_get_version; } -static DWORD findProcess(LPWSTR name) { +static DWORD findProcess(const LPWSTR name) { PROCESSENTRY32W entry; entry.dwSize = sizeof(PROCESSENTRY32W); From 8a079e1ae755b46822a0e88df09ce9c354f6557d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 17 Jun 2023 22:14:11 -0700 Subject: [PATCH 04/65] get compiling on windows --- library/CMakeLists.txt | 4 ++-- library/modules/DFSteam.cpp | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 1db78c24e..48ebdb986 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -377,9 +377,9 @@ if(WIN32) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"" ) else() set_target_properties(dfhack PROPERTIES COMPILE_FLAGS "-include Export.h" ) - # required because of protobuf headers + # required because of transitively-included protobuf headers target_compile_options(dfhack - PUBLIC -Wno-deprecated-declarations -Wno-restrict) + INTERFACE -Wno-deprecated-declarations -Wno-restrict) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "-include Export.h" ) add_library(dfhooks SHARED Hooks.cpp) target_link_libraries(dfhooks dfhack) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 103e2dfee..9aab53466 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -108,7 +108,7 @@ static bool is_running_on_wine() { return !!pwine_get_version; } -static DWORD findProcess(const LPWSTR name) { +static DWORD findProcess(LPCWSTR name) { PROCESSENTRY32W entry; entry.dwSize = sizeof(PROCESSENTRY32W); @@ -150,11 +150,14 @@ static bool launchDFHack(color_ostream& out) { si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - // note that the enviornment must be explicitly zeroed out and not NULL, + static LPCWSTR procname = L"hack/launchdf.exe"; + static const char * env = "\0"; + + // note that the environment must be explicitly zeroed out and not NULL, // otherwise the launched process will inherit this process's environment, // and the Steam API in the launchdf process will think it is in DF's context. - BOOL res = CreateProcessW(L"hack/launchdf.exe", - NULL, NULL, NULL, FALSE, 0, "\0", NULL, &si, &pi); + BOOL res = CreateProcessW(procname, + NULL, NULL, NULL, FALSE, 0, (LPVOID)env, NULL, &si, &pi); return !!res; } From 8f413628c2342c3639d6c44959bd663bcabfb4a0 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 19 Jun 2023 17:40:40 -0700 Subject: [PATCH 05/65] reinstate alphabetical ordering --- depends/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index 566965f8a..9ec71646a 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -1,9 +1,9 @@ # list depends here. add_subdirectory(lodepng) -add_subdirectory(md5) - add_subdirectory(lua) +add_subdirectory(md5) add_subdirectory(protobuf) + if(UNIX) set_target_properties(lua PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-deprecated-enum-enum-conversion") target_compile_options(protoc From 8f1efcd8a38fe1f484d416f1eaf18182307c006e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 25 Jun 2023 17:44:06 -0700 Subject: [PATCH 06/65] remove need to ignore warnings for dfhack-dependent targets --- library/CMakeLists.txt | 3 --- library/Debug.cpp | 1 + library/include/Core.h | 2 -- library/include/Export.h | 14 ++++++++++++++ library/include/PluginManager.h | 2 -- library/include/RemoteClient.h | 11 ----------- library/include/modules/Buildings.h | 8 +++++++- plugins/channel-safely/channel-manager.cpp | 4 ++-- plugins/channel-safely/include/inlines.h | 2 +- plugins/confirm.cpp | 2 +- plugins/hotkeys.cpp | 2 +- 11 files changed, 27 insertions(+), 24 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 48ebdb986..8681c9c90 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -377,9 +377,6 @@ if(WIN32) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"" ) else() set_target_properties(dfhack PROPERTIES COMPILE_FLAGS "-include Export.h" ) - # required because of transitively-included protobuf headers - target_compile_options(dfhack - INTERFACE -Wno-deprecated-declarations -Wno-restrict) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "-include Export.h" ) add_library(dfhooks SHARED Hooks.cpp) target_link_libraries(dfhooks dfhack) diff --git a/library/Debug.cpp b/library/Debug.cpp index 9b13af168..dafbeb5ce 100644 --- a/library/Debug.cpp +++ b/library/Debug.cpp @@ -26,6 +26,7 @@ redistribute it freely, subject to the following restrictions: #include "Debug.h" #include "DebugManager.h" +#include #include #include #include diff --git a/library/include/Core.h b/library/include/Core.h index 696be4ead..c9f49cbb3 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -40,8 +40,6 @@ distribution. #include #include -#include "RemoteClient.h" - #define DFH_MOD_SHIFT 1 #define DFH_MOD_CTRL 2 #define DFH_MOD_ALT 4 diff --git a/library/include/Export.h b/library/include/Export.h index 9e2a78d4b..ad2b4ceec 100644 --- a/library/include/Export.h +++ b/library/include/Export.h @@ -69,3 +69,17 @@ distribution. #else #define Wformat(type, fmtstr, vararg) #endif + +namespace DFHack +{ + enum command_result + { + CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error + CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console + CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded + CR_OK = 0, // Success + CR_FAILURE = 1, // Failure + CR_WRONG_USAGE = 2, // Wrong arguments or ui state + CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) + }; +} diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 78c5e5dc8..f67023f93 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -35,8 +35,6 @@ distribution. #include "Core.h" #include "DataFuncs.h" -#include "RemoteClient.h" - typedef struct lua_State lua_State; namespace tthread diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index e71b985cd..17e296a2e 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -39,17 +39,6 @@ namespace DFHack using dfproto::IntMessage; using dfproto::StringMessage; - enum command_result - { - CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error - CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console - CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded - CR_OK = 0, // Success - CR_FAILURE = 1, // Failure - CR_WRONG_USAGE = 2, // Wrong arguments or ui state - CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) - }; - enum DFHackReplyCode : int16_t { RPC_REPLY_RESULT = -1, RPC_REPLY_FAIL = -2, diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 78163108e..22dbb0370 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -237,7 +237,7 @@ DFHACK_EXPORT std::string getRoomDescription(df::building *building, df::unit *u * starting at the top left and moving right, row by row, * the block's items are checked for anything on the ground within that stockpile. */ -class DFHACK_EXPORT StockpileIterator : public std::iterator +class DFHACK_EXPORT StockpileIterator { df::building_stockpilest* stockpile; df::map_block* block; @@ -245,6 +245,12 @@ class DFHACK_EXPORT StockpileIterator : public std::iteratorblock_events) { if (auto evT = virtual_cast(event)) { // we want to let the user keep some designations free of being managed - auto b = max(0, cavein_candidates[pos] - least_access); + auto b = std::max(0, cavein_candidates[pos] - least_access); auto v = 1000 + (b * 1700); DEBUG(manager).print("(" COORD ") 1000+1000(%d) -> %d {least-access: %d}\n",COORDARGS(pos), b, v, least_access); evT->priority[Coord(local)] = v; diff --git a/plugins/channel-safely/include/inlines.h b/plugins/channel-safely/include/inlines.h index a29f5a04d..362fd927a 100644 --- a/plugins/channel-safely/include/inlines.h +++ b/plugins/channel-safely/include/inlines.h @@ -23,7 +23,7 @@ namespace CSP { inline uint32_t calc_distance(df::coord p1, df::coord p2) { // calculate chebyshev (chessboard) distance uint32_t distance = abs(p2.z - p1.z); - distance += max(abs(p2.x - p1.x), abs(p2.y - p1.y)); + distance += std::max(abs(p2.x - p1.x), abs(p2.y - p1.y)); return distance; } diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 1dfb6809d..bd6e41b64 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -411,7 +411,7 @@ public: Screen::paintTile(corner_ur, x2, y1); Screen::paintTile(corner_dl, x1, y2); Screen::paintTile(corner_dr, x2, y2); - string title = " " + get_title() + " "; + string title = ' ' + get_title() + ' '; Screen::paintString(Screen::Pen(' ', COLOR_DARKGREY, COLOR_BLACK), x2 - 6, y1, "DFHack"); Screen::paintString(Screen::Pen(' ', COLOR_BLACK, COLOR_GREY), diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 136ad7a9d..ef2ca9422 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -103,7 +103,7 @@ static void find_active_keybindings(color_ostream &out, df::viewscreen *screen, } for (int i = 1; i <= 12; i++) { - valid_keys.push_back("F" + int_to_string(i)); + valid_keys.push_back('F' + int_to_string(i)); } valid_keys.push_back("`"); From 78448f438d5386b773afe28e859468dff87a57a6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 25 Jun 2023 17:53:16 -0700 Subject: [PATCH 07/65] don't leak warning suppression out of protobuf --- depends/CMakeLists.txt | 12 ++++------- .../protobuf/google/protobuf/repeated_field.h | 4 ++++ library/DataDefs.cpp | 4 ++-- library/modules/Gui.cpp | 20 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index 9ec71646a..0756519ef 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -6,14 +6,10 @@ add_subdirectory(protobuf) if(UNIX) set_target_properties(lua PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-deprecated-enum-enum-conversion") - target_compile_options(protoc - PUBLIC -Wno-deprecated-declarations -Wno-restrict) - target_compile_options(protoc-bin - PUBLIC -Wno-deprecated-declarations -Wno-restrict) - target_compile_options(protobuf-lite - PUBLIC -Wno-deprecated-declarations -Wno-restrict) - target_compile_options(protobuf - PUBLIC -Wno-deprecated-declarations -Wno-restrict) + set_target_properties(protoc PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict") + set_target_properties(protoc-bin PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict") + set_target_properties(protobuf-lite PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict") + set_target_properties(protobuf PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-restrict") endif() if(UNIX AND NOT APPLE) # remove this once our MSVC build env has been updated diff --git a/depends/protobuf/google/protobuf/repeated_field.h b/depends/protobuf/google/protobuf/repeated_field.h index aed4ce9f2..637708254 100644 --- a/depends/protobuf/google/protobuf/repeated_field.h +++ b/depends/protobuf/google/protobuf/repeated_field.h @@ -46,6 +46,10 @@ #ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include #include #include diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index f376edc6a..cd261d50f 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -213,12 +213,12 @@ std::string pointer_identity::getFullName() std::string container_identity::getFullName(type_identity *item) { - return "<" + (item ? item->getFullName() : std::string("void")) + ">"; + return '<' + (item ? item->getFullName() : std::string("void")) + '>'; } std::string ptr_container_identity::getFullName(type_identity *item) { - return "<" + (item ? item->getFullName() : std::string("void")) + "*>"; + return '<' + (item ? item->getFullName() : std::string("void")) + std::string("*>"); } std::string bit_container_identity::getFullName(type_identity *) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index d03bcce09..01fbdddf3 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -182,23 +182,23 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) if (game->main_interface.info.open) { newFocusString = baseFocus; newFocusString += "/Info"; - newFocusString += "/" + enum_item_key(game->main_interface.info.current_mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.current_mode); switch(game->main_interface.info.current_mode) { case df::enums::info_interface_mode_type::CREATURES: - newFocusString += "/" + enum_item_key(game->main_interface.info.creatures.current_mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.creatures.current_mode); break; case df::enums::info_interface_mode_type::BUILDINGS: - newFocusString += "/" + enum_item_key(game->main_interface.info.buildings.mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.buildings.mode); break; case df::enums::info_interface_mode_type::LABOR: - newFocusString += "/" + enum_item_key(game->main_interface.info.labor.mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.labor.mode); break; case df::enums::info_interface_mode_type::ARTIFACTS: - newFocusString += "/" + enum_item_key(game->main_interface.info.artifacts.mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.artifacts.mode); break; case df::enums::info_interface_mode_type::JUSTICE: - newFocusString += "/" + enum_item_key(game->main_interface.info.justice.current_mode); + newFocusString += '/' + enum_item_key(game->main_interface.info.justice.current_mode); break; default: break; @@ -209,7 +209,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) if (game->main_interface.view_sheets.open) { newFocusString = baseFocus; newFocusString += "/ViewSheets"; - newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet); + newFocusString += '/' + enum_item_key(game->main_interface.view_sheets.active_sheet); focusStrings.push_back(newFocusString); } @@ -233,7 +233,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) newFocusString += "/Zone"; if (game->main_interface.civzone.cur_bld) { newFocusString += "/Some"; - newFocusString += "/" + enum_item_key(game->main_interface.civzone.cur_bld->type); + newFocusString += '/' + enum_item_key(game->main_interface.civzone.cur_bld->type); } break; case df::enums::main_bottom_mode_type::ZONE_PAINT: @@ -499,7 +499,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) if (!adventure) return; - focus += "/" + enum_item_key(adventure->menu); + focus += '/' + enum_item_key(adventure->menu); } */ @@ -1427,7 +1427,7 @@ DFHACK_EXPORT int Gui::makeAnnouncement(df::announcement_type type, df::announce if (flags.bits.D_DISPLAY) { world->status.display_timer = ANNOUNCE_DISPLAY_TIME; - Gui::writeToGamelog("x" + to_string(repeat_count + 1)); + Gui::writeToGamelog('x' + to_string(repeat_count + 1)); } return -1; } From 175c249d29cc62675a0b5da85e14da295c77cea7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 2 Jul 2023 18:04:06 -0700 Subject: [PATCH 08/65] support copy/paste from system clipboard --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 14 +++++++++++ library/LuaApi.cpp | 6 +++++ library/include/modules/DFSDL.h | 8 +++++++ library/lua/gui/widgets.lua | 10 ++++++++ library/modules/DFSDL.cpp | 41 +++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index d10407621..c30d313d5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index d07cb045e..ffd43aea1 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2815,6 +2815,14 @@ and are only documented here for completeness: Returns 0 if the address is not found. Requires a heap snapshot. +* ``dfhack.internal.getClipboardText()`` + + Gets the system clipboard text (converted to CP437 encoding). + +* ``dfhack.internal.setClipboardText(text)`` + + Converts the given text from CP437 to UTF-8 and sets the system clipboard + text. .. _lua-core-context: @@ -4688,6 +4696,12 @@ following keyboard hotkeys: - Ctrl-B/Ctrl-F: move the cursor one word back or forward. - Ctrl-A/Ctrl-E: move the cursor to the beginning/end of the text. +The widget also supports integration with the system clipboard: + +- Ctrl-C: copy current text to the system clipboard +- Ctrl-X: copy current text to the system clipboard and clear text in widget +- Ctrl-V: paste text from the system clipboard (text is converted to cp437) + The ``EditField`` class also provides the following functions: * ``editfield:setCursor([cursor_pos])`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d2c32cf63..13eb01c65 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -47,6 +47,7 @@ distribution. #include "modules/Burrows.h" #include "modules/Constructions.h" #include "modules/Designations.h" +#include "modules/DFSDL.h" #include "modules/Filesystem.h" #include "modules/Gui.h" #include "modules/Items.h" @@ -2999,6 +3000,9 @@ static int msize_address(uintptr_t ptr) return -1; } +static std::string getClipboardText() { return DFSDL::DFSDL_GetClipboardTextCp437(); } +static void setClipboardText(std::string s) { DFSDL::DFSDL_SetClipboardTextCp437(s); } + static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAP(getImageBase), WRAP(getRebaseDelta), @@ -3013,6 +3017,8 @@ static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAPN(getAddressSizeInHeap, get_address_size_in_heap), WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object), WRAPN(msizeAddress, msize_address), + WRAP(getClipboardText), + WRAP(setClipboardText), { NULL, NULL } }; diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index 626224d60..88dd08f05 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -48,6 +48,14 @@ DFHACK_EXPORT void DFSDL_FreeSurface(SDL_Surface *surface); // DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem); DFHACK_EXPORT int DFSDL_PushEvent(SDL_Event *event); +// System clipboard +DFHACK_EXPORT std::string DFSDL_GetClipboardTextUtf8(); +DFHACK_EXPORT std::string DFSDL_GetClipboardTextCp437(); +DFHACK_EXPORT bool DFSDL_SetClipboardTextUtf8(const char *text); +DFHACK_EXPORT bool DFSDL_SetClipboardTextUtf8(const std::string &text); +DFHACK_EXPORT bool DFSDL_SetClipboardTextCp437(const char *text); +DFHACK_EXPORT bool DFSDL_SetClipboardTextCp437(const std::string &text); + } } diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index a58ac228c..8759f97bf 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -772,6 +772,16 @@ function EditField:onInput(keys) elseif keys.CUSTOM_CTRL_E then -- end self:setCursor() return true + elseif keys.CUSTOM_CTRL_C then + dfhack.internal.setClipboardText(self.text) + return true + elseif keys.CUSTOM_CTRL_X then + dfhack.internal.setClipboardText(self.text) + self:setText('') + return true + elseif keys.CUSTOM_CTRL_V then + self:insert(dfhack.internal.getClipboardText()) + return true end -- if we're modal, then unconditionally eat all the input diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 7aa7f36d5..4ed4bc5e8 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -3,8 +3,11 @@ #include "modules/DFSDL.h" #include "Debug.h" +#include "MiscUtils.h" #include "PluginManager.h" +#include + namespace DFHack { DBG_DECLARE(core, dfsdl, DebugCategory::LINFO); } @@ -35,6 +38,10 @@ void (*g_SDL_FreeSurface)(SDL_Surface *) = nullptr; // int (*g_SDL_SemWait)(DFSDL_sem *) = nullptr; // int (*g_SDL_SemPost)(DFSDL_sem *) = nullptr; int (*g_SDL_PushEvent)(SDL_Event *) = nullptr; +SDL_bool (*g_SDL_HasClipboardText)(); +int (*g_SDL_SetClipboardText)(const char *text); +char * (*g_SDL_GetClipboardText)(); +void (*g_SDL_free)(void *); bool DFSDL::init(color_ostream &out) { for (auto &lib_str : SDL_LIBS) { @@ -71,6 +78,10 @@ bool DFSDL::init(color_ostream &out) { // bind(g_sdl_handle, SDL_SemWait); // bind(g_sdl_handle, SDL_SemPost); bind(g_sdl_handle, SDL_PushEvent); + bind(g_sdl_handle, SDL_HasClipboardText); + bind(g_sdl_handle, SDL_SetClipboardText); + bind(g_sdl_handle, SDL_GetClipboardText); + bind(g_sdl_handle, SDL_free); #undef bind DEBUG(dfsdl,out).print("sdl successfully loaded\n"); @@ -124,3 +135,33 @@ void DFSDL::DFSDL_FreeSurface(SDL_Surface *surface) { int DFSDL::DFSDL_PushEvent(SDL_Event *event) { return g_SDL_PushEvent(event); } + +std::string DFSDL::DFSDL_GetClipboardTextUtf8() { + if (g_SDL_HasClipboardText() != SDL_TRUE) + return ""; + char *text = g_SDL_GetClipboardText(); + std::string ret = text; + g_SDL_free(text); + return ret; +} + +std::string DFSDL::DFSDL_GetClipboardTextCp437() { + std::string utf8text = DFSDL_GetClipboardTextUtf8(); + return UTF2DF(utf8text); +} + +bool DFSDL::DFSDL_SetClipboardTextUtf8(const char *text) { + return g_SDL_SetClipboardText(text) == 0; +} + +bool DFSDL::DFSDL_SetClipboardTextUtf8(const std::string &text) { + return DFSDL_SetClipboardTextUtf8(text.c_str()); +} + +bool DFSDL::DFSDL_SetClipboardTextCp437(const char *text) { + return DFSDL_SetClipboardTextUtf8(DF2UTF(text)); +} + +bool DFSDL::DFSDL_SetClipboardTextCp437(const std::string &text) { + return DFSDL_SetClipboardTextUtf8(DF2UTF(text)); +} From 07e8edcdcaf4c58ac13cb359d433536a217b5232 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 2 Jul 2023 19:27:07 -0700 Subject: [PATCH 09/65] ensure changing text fires the on_change event --- library/lua/gui/widgets.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 8759f97bf..76b5c3b68 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -640,8 +640,12 @@ function EditField:setCursor(cursor) end function EditField:setText(text, cursor) + local old = self.text self.text = text self:setCursor(cursor) + if self.on_change and text ~= old then + self.on_change(self.text, old) + end end function EditField:postUpdateLayout() @@ -699,11 +703,7 @@ function EditField:onInput(keys) end if self.key and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then - local old = self.text self:setText(self.saved_text) - if self.on_change and old ~= self.saved_text then - self.on_change(self.text, old) - end self:setFocus(false) return true end @@ -747,9 +747,6 @@ function EditField:onInput(keys) return self.modal end end - if self.on_change and self.text ~= old then - self.on_change(self.text, old) - end return true elseif keys.KEYBOARD_CURSOR_LEFT then self:setCursor(self.cursor - 1) From fdf2430fc45e8a2b620fa55e23afb454886aea63 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 09:09:52 -0700 Subject: [PATCH 10/65] filter out spurious STRING keybindings that don't match actual SDL string input --- docs/changelog.txt | 1 + library/include/modules/Screen.h | 4 ++-- library/modules/Screen.cpp | 8 +++++--- plugins/overlay.cpp | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d10407621..f30db1b41 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing ## Misc Improvements diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index daa08ca59..b918ab6c2 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -229,8 +229,8 @@ namespace DFHack DFHACK_EXPORT bool hasActiveScreens(Plugin *p); DFHACK_EXPORT void raise(df::viewscreen *screen); - // returns a new set with text interface keys from the text buffer added in (if any) - DFHACK_EXPORT std::set add_text_keys(const std::set& keys); + // returns a new set of interface keys that ensures that string input matches the DF text buffer + DFHACK_EXPORT std::set normalize_text_keys(const std::set& keys); /// Retrieve the string representation of the bound key. DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 76f782edb..c7ef88c60 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -585,8 +585,10 @@ void Hide::merge() { } } } -std::set Screen::add_text_keys(const std::set& keys) { - std::set combined_keys(keys); +std::set Screen::normalize_text_keys(const std::set& keys) { + std::set combined_keys; + std::copy_if(keys.begin(), keys.end(), std::inserter(combined_keys, combined_keys.begin()), + [](df::interface_key k){ return k <= df::interface_key::STRING_A000 || k > df::interface_key::STRING_A255; } ); if (df::global::enabler->last_text_input[0]) { char c = df::global::enabler->last_text_input[0]; df::interface_key key = charToKey(c); @@ -952,7 +954,7 @@ int dfhack_lua_viewscreen::do_input(lua_State *L) } lua_pushvalue(L, -2); - Lua::PushInterfaceKeys(L, Screen::add_text_keys(*keys)); + Lua::PushInterfaceKeys(L, Screen::normalize_text_keys(*keys)); lua_call(L, 2, 0); self->update_focus(L, -1); diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index 7185f73a1..5dc9ed327 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -76,7 +76,7 @@ struct viewscreen_overlay : T { [&](lua_State *L) { Lua::Push(L, T::_identity.getName()); Lua::Push(L, this); - Lua::PushInterfaceKeys(L, Screen::add_text_keys(*input)); + Lua::PushInterfaceKeys(L, Screen::normalize_text_keys(*input)); }, [&](lua_State *L) { input_is_handled = lua_toboolean(L, -1); }); From 9ca96567a5b292914023ec9bc6d429076869eed0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 11:05:58 -0700 Subject: [PATCH 11/65] move conversion logic to MiscUtils but keep minimal wrappers in SDL module so we don't leak memory --- docs/dev/Lua API.rst | 9 ++++----- library/LuaApi.cpp | 8 ++------ library/MiscUtils.cpp | 10 ++++++++++ library/include/MiscUtils.h | 4 ++++ library/include/modules/DFSDL.h | 11 ++++------- library/lua/gui/widgets.lua | 6 +++--- library/modules/DFSDL.cpp | 22 ++-------------------- 7 files changed, 29 insertions(+), 41 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ffd43aea1..830ba6b09 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2815,14 +2815,13 @@ and are only documented here for completeness: Returns 0 if the address is not found. Requires a heap snapshot. -* ``dfhack.internal.getClipboardText()`` +* ``dfhack.internal.getClipboardTextCp437()`` - Gets the system clipboard text (converted to CP437 encoding). + Gets the system clipboard text (and converts text to CP437 encoding). -* ``dfhack.internal.setClipboardText(text)`` +* ``dfhack.internal.setClipboardTextCp437(text)`` - Converts the given text from CP437 to UTF-8 and sets the system clipboard - text. + Sets the system clipboard text from a CP437 string. .. _lua-core-context: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 13eb01c65..c04a8ef0f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -47,7 +47,6 @@ distribution. #include "modules/Burrows.h" #include "modules/Constructions.h" #include "modules/Designations.h" -#include "modules/DFSDL.h" #include "modules/Filesystem.h" #include "modules/Gui.h" #include "modules/Items.h" @@ -3000,9 +2999,6 @@ static int msize_address(uintptr_t ptr) return -1; } -static std::string getClipboardText() { return DFSDL::DFSDL_GetClipboardTextCp437(); } -static void setClipboardText(std::string s) { DFSDL::DFSDL_SetClipboardTextCp437(s); } - static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAP(getImageBase), WRAP(getRebaseDelta), @@ -3017,8 +3013,8 @@ static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAPN(getAddressSizeInHeap, get_address_size_in_heap), WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object), WRAPN(msizeAddress, msize_address), - WRAP(getClipboardText), - WRAP(setClipboardText), + WRAP(getClipboardTextCp437), + WRAP(setClipboardTextCp437), { NULL, NULL } }; diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index b959e756e..6b50da347 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -27,6 +27,8 @@ distribution. #include "MiscUtils.h" #include "ColorText.h" +#include "modules/DFSDL.h" + #ifndef LINUX_BUILD // We don't want min and max macros #define NOMINMAX @@ -470,3 +472,11 @@ DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::stri { return out.is_console() ? DF2CONSOLE(in) : in; } + +DFHACK_EXPORT std::string getClipboardTextCp437() { + return UTF2DF(DFHack::DFSDL::DFSDL_GetClipboardText()); +} + +DFHACK_EXPORT bool setClipboardTextCp437(std::string text) { + return DFHack::DFSDL::DFSDL_SetClipboardText(DF2UTF(text).c_str()); +} diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index d14bdb6e9..099e86581 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -496,3 +496,7 @@ DFHACK_EXPORT std::string UTF2DF(const std::string &in); DFHACK_EXPORT std::string DF2UTF(const std::string &in); DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in); DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in); + +// System clipboard -- submitted and returned text must be in CP437 +DFHACK_EXPORT std::string getClipboardTextCp437(); +DFHACK_EXPORT bool setClipboardTextCp437(std::string text); diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index 88dd08f05..6cee58342 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -48,13 +48,10 @@ DFHACK_EXPORT void DFSDL_FreeSurface(SDL_Surface *surface); // DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem); DFHACK_EXPORT int DFSDL_PushEvent(SDL_Event *event); -// System clipboard -DFHACK_EXPORT std::string DFSDL_GetClipboardTextUtf8(); -DFHACK_EXPORT std::string DFSDL_GetClipboardTextCp437(); -DFHACK_EXPORT bool DFSDL_SetClipboardTextUtf8(const char *text); -DFHACK_EXPORT bool DFSDL_SetClipboardTextUtf8(const std::string &text); -DFHACK_EXPORT bool DFSDL_SetClipboardTextCp437(const char *text); -DFHACK_EXPORT bool DFSDL_SetClipboardTextCp437(const std::string &text); +// submitted and returned text is UTF-8 +// see wrapper functions in MiscUtils.h for cp-437 variants +DFHACK_EXPORT std::string DFSDL_GetClipboardText(); +DFHACK_EXPORT bool DFSDL_SetClipboardText(const char *text); } diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 76b5c3b68..980d0104f 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -770,14 +770,14 @@ function EditField:onInput(keys) self:setCursor() return true elseif keys.CUSTOM_CTRL_C then - dfhack.internal.setClipboardText(self.text) + dfhack.internal.setClipboardTextCp437(self.text) return true elseif keys.CUSTOM_CTRL_X then - dfhack.internal.setClipboardText(self.text) + dfhack.internal.setClipboardTextCp437(self.text) self:setText('') return true elseif keys.CUSTOM_CTRL_V then - self:insert(dfhack.internal.getClipboardText()) + self:insert(dfhack.internal.getClipboardTextCp437()) return true end diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 4ed4bc5e8..280fb04d6 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -3,7 +3,6 @@ #include "modules/DFSDL.h" #include "Debug.h" -#include "MiscUtils.h" #include "PluginManager.h" #include @@ -136,7 +135,7 @@ int DFSDL::DFSDL_PushEvent(SDL_Event *event) { return g_SDL_PushEvent(event); } -std::string DFSDL::DFSDL_GetClipboardTextUtf8() { +std::string DFSDL::DFSDL_GetClipboardText() { if (g_SDL_HasClipboardText() != SDL_TRUE) return ""; char *text = g_SDL_GetClipboardText(); @@ -145,23 +144,6 @@ std::string DFSDL::DFSDL_GetClipboardTextUtf8() { return ret; } -std::string DFSDL::DFSDL_GetClipboardTextCp437() { - std::string utf8text = DFSDL_GetClipboardTextUtf8(); - return UTF2DF(utf8text); -} - -bool DFSDL::DFSDL_SetClipboardTextUtf8(const char *text) { +bool DFSDL::DFSDL_SetClipboardText(const char *text) { return g_SDL_SetClipboardText(text) == 0; } - -bool DFSDL::DFSDL_SetClipboardTextUtf8(const std::string &text) { - return DFSDL_SetClipboardTextUtf8(text.c_str()); -} - -bool DFSDL::DFSDL_SetClipboardTextCp437(const char *text) { - return DFSDL_SetClipboardTextUtf8(DF2UTF(text)); -} - -bool DFSDL::DFSDL_SetClipboardTextCp437(const std::string &text) { - return DFSDL_SetClipboardTextUtf8(DF2UTF(text)); -} From ae545aa1d6a3577f6b4a2fbf485335544f4b23f4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 11:18:14 -0700 Subject: [PATCH 12/65] add new linkage dependency on dfhack --- library/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 5d2698bb0..40006a432 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -408,8 +408,8 @@ endif() target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_static dfhack-version ${PROJECT_LIBS}) set_target_properties(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") -target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static) -target_link_libraries(dfhack-run dfhack-client) +target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static dfhack) +target_link_libraries(dfhack-run dfhack-client dfhack) if(APPLE) add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...") From 5a36a0fcbdaa3cddd0b59408a1d72f6770ab8305 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 11:44:33 -0700 Subject: [PATCH 13/65] ensure we can still inject strings with simulateInput --- library/LuaApi.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d2c32cf63..8823eb270 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2603,14 +2603,29 @@ static int screen_doSimulateInput(lua_State *L) int sz = lua_rawlen(L, 2); std::set keys; + char str = '\0'; for (int j = 1; j <= sz; j++) { lua_rawgeti(L, 2, j); - keys.insert((df::interface_key)lua_tointeger(L, -1)); + df::interface_key k = (df::interface_key)lua_tointeger(L, -1); + if (!str && k > df::interface_key::STRING_A000 && k <= df::interface_key::STRING_A255) + str = Screen::keyToChar(k); + keys.insert(k); lua_pop(L, 1); } + // if we're injecting a text keybinding, ensure it is reflected in the enabler text buffer + std::string prev_input; + if (str) { + prev_input = (const char *)&df::global::enabler->last_text_input[0]; + df::global::enabler->last_text_input[0] = str; + df::global::enabler->last_text_input[1] = '\0'; + } + screen->feed(&keys); + + if (str) + strcpy((char *)&df::global::enabler->last_text_input[0], prev_input.c_str()); return 0; } From e7f5b1f949fe6903d3e0fd34a603302687a1dae7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 11:53:46 -0700 Subject: [PATCH 14/65] move command_result enum from Export to Core --- library/include/Core.h | 11 +++++++++++ library/include/Export.h | 14 -------------- library/include/RemoteClient.h | 1 + 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/library/include/Core.h b/library/include/Core.h index 92bf63d93..a574b029a 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -72,6 +72,17 @@ namespace DFHack struct Hide; } + enum command_result + { + CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error + CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console + CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded + CR_OK = 0, // Success + CR_FAILURE = 1, // Failure + CR_WRONG_USAGE = 2, // Wrong arguments or ui state + CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) + }; + enum state_change_event { SC_UNKNOWN = -1, diff --git a/library/include/Export.h b/library/include/Export.h index ad2b4ceec..9e2a78d4b 100644 --- a/library/include/Export.h +++ b/library/include/Export.h @@ -69,17 +69,3 @@ distribution. #else #define Wformat(type, fmtstr, vararg) #endif - -namespace DFHack -{ - enum command_result - { - CR_LINK_FAILURE = -3, // RPC call failed due to I/O or protocol error - CR_NEEDS_CONSOLE = -2, // Attempt to call interactive command without console - CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded - CR_OK = 0, // Success - CR_FAILURE = 1, // Failure - CR_WRONG_USAGE = 2, // Wrong arguments or ui state - CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) - }; -} diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index 17e296a2e..921d351c3 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -26,6 +26,7 @@ distribution. #include "Pragma.h" #include "Export.h" #include "ColorText.h" +#include "Core.h" class CPassiveSocket; class CActiveSocket; From 9ddb3813c1ea14e5be99d59c11d871ba7a53359a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 11:18:14 -0700 Subject: [PATCH 15/65] add new linkage dependency on dfhack --- library/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 5d2698bb0..40006a432 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -408,8 +408,8 @@ endif() target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_static dfhack-version ${PROJECT_LIBS}) set_target_properties(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") -target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static) -target_link_libraries(dfhack-run dfhack-client) +target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static dfhack) +target_link_libraries(dfhack-run dfhack-client dfhack) if(APPLE) add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...") From cbdb56a1ac35f9ed7eefd0a518c57f5ee0866a40 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 13:47:43 -0700 Subject: [PATCH 16/65] port API for unit lookup by noble role from quickfort --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 19 ++++++++++-- library/LuaApi.cpp | 10 ++++++ library/include/modules/Units.h | 2 ++ library/modules/Units.cpp | 55 +++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bf8182c73..814bb3909 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,11 +42,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Documentation ## API +- ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role ## Internals ## Lua - ``dfhack.items.markForTrade``: new API for marking items for trade +- ``dfhack.units.getUnitByNobleRole``, ``dfhack.units.getUnitsByNobleRole``: unit lookup API by role ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 33e27be2d..57e63dabb 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1438,10 +1438,25 @@ Units module Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to the arguments required by this function. +* ``dfhack.units.getUnitByNobleRole(role_name)`` + + Returns the unit assigned to the given noble role, if any. ``role_name`` must + be one of the position codes associated with the active fort government. + Normally, this includes: ``MILITIA_COMMANDER``, ``MILITIA_CAPTAIN``, + ``SHERIFF``, ``CAPTAIN_OF_THE_GUARD``, ``EXPEDITION_LEADER``, ``MAYOR``, + ``MANAGER``, ``CHIEF_MEDICAL_DWARF``, ``BROKER``, ``BOOKKEEPER``, + ``CHAMPION``, ``HAMMERER``, ``DUNGEON_MASTER``, and ``MESSENGER``. Note that + if more than one unit has the role, only the first will be returned. See + ``getUnitsByNobleRole`` below for retrieving all units with a particular role. + +* ``dfhack.units.getUnitsByNobleRole(role_name)`` + + Returns a list of units (possibly empty) assigned to the given noble role. + * ``dfhack.units.getCitizens([ignore_sanity])`` - Returns a table (list) of all citizens, which you would otherwise have to loop over all - units in world and test against ``isCitizen()`` to discover. + Returns a table (list) of all citizens, which you would otherwise have to + loop over all units in world and test against ``isCitizen()`` to discover. * ``dfhack.units.teleport(unit, pos)`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 5d9411434..65a899814 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1833,6 +1833,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, multiplyGroupActionTimers), WRAPM(Units, setActionTimers), WRAPM(Units, setGroupActionTimers), + WRAPM(Units, getUnitByNobleRole), { NULL, NULL } }; @@ -1921,6 +1922,14 @@ static int units_getCitizens(lua_State *L) { return 0; } +static int units_getUnitsByNobleRole(lua_State *L) { + std::string role_name = lua_tostring(L, -1); + std::vector units; + Units::getUnitsByNobleRole(units, role_name); + Lua::PushVector(L, units); + return 1; +} + static int units_getStressCutoffs(lua_State *L) { lua_newtable(L); @@ -1935,6 +1944,7 @@ static const luaL_Reg dfhack_units_funcs[] = { { "getNoblePositions", units_getNoblePositions }, { "getUnitsInBox", units_getUnitsInBox }, { "getCitizens", units_getCitizens }, + { "getUnitsByNobleRole", units_getUnitsByNobleRole}, { "getStressCutoffs", units_getStressCutoffs }, { NULL, NULL } }; diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 4fd9246aa..3c56d0890 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -148,6 +148,8 @@ DFHACK_EXPORT df::unit *getUnit(const int32_t index); DFHACK_EXPORT bool getUnitsInBox(std::vector &units, int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2); +DFHACK_EXPORT bool getUnitsByNobleRole(std::vector &units, std::string noble); +DFHACK_EXPORT df::unit *getUnitByNobleRole(std::string noble); DFHACK_EXPORT bool getCitizens(std::vector &citizens, bool ignore_sanity = false); DFHACK_EXPORT int32_t findIndexById(int32_t id); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 533b40ca8..9fb19cfb3 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -59,6 +59,7 @@ using namespace std; #include "df/entity_position_assignment.h" #include "df/entity_raw.h" #include "df/entity_raw_flags.h" +#include "df/entity_site_link.h" #include "df/identity_type.h" #include "df/game_mode.h" #include "df/histfig_entity_link_positionst.h" @@ -80,6 +81,8 @@ using namespace std; #include "df/unit_soul.h" #include "df/unit_wound.h" #include "df/world.h" +#include "df/world_data.h" +#include "df/world_site.h" #include "df/unit_action.h" #include "df/unit_action_type_group.h" @@ -770,6 +773,58 @@ bool Units::getUnitsInBox (std::vector &units, return true; } +static int32_t get_noble_position_id(const df::historical_entity::T_positions &positions, const string &noble) { + string target_id = toUpper(noble); + for (auto &position : positions.own) { + if (position->code == target_id) + return position->id; + } + return -1; +} + +static void get_assigned_noble_units(vector &units, const df::historical_entity::T_positions &positions, int32_t noble_position_id, size_t limit) { + units.clear(); + for (auto &assignment : positions.assignments) { + if (assignment->position_id != noble_position_id) + continue; + auto histfig = df::historical_figure::find(assignment->histfig); + if (!histfig) + continue; + auto unit = df::unit::find(histfig->unit_id); + if (!unit) + continue; + units.emplace_back(unit); + if (limit > 0 && units.size() >= limit) + break; + } +} + +static void get_units_by_noble_role(vector &units, string noble, size_t limit = 0) { + auto &site = df::global::world->world_data->active_site[0]; + for (auto &link : site->entity_links) { + auto gov = df::historical_entity::find(link->entity_id); + if (!gov || gov->type != df::historical_entity_type::SiteGovernment) + continue; + int32_t noble_position_id = get_noble_position_id(gov->positions, noble); + if (noble_position_id < 0) + return; + get_assigned_noble_units(units, gov->positions, noble_position_id, limit); + } +} + +bool Units::getUnitsByNobleRole(vector &units, std::string noble) { + get_units_by_noble_role(units, noble); + return !units.empty(); +} + +df::unit *Units::getUnitByNobleRole(string noble) { + vector units; + get_units_by_noble_role(units, noble, 1); + if (units.empty()) + return NULL; + return units[0]; +} + bool Units::getCitizens(std::vector &citizens, bool ignore_sanity) { for (auto &unit : world->units.active) { if (isCitizen(unit, ignore_sanity)) From 9e3094c0fcb7bb20997be7444c1858c25185bb32 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 02:43:29 +0000 Subject: [PATCH 17/65] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.23.1 → 0.23.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.23.1...0.23.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index efa59812d..1d481edce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.23.1 + rev: 0.23.2 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks From ba974aff9822030ebf7c8642bf53d93be5fe7089 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 4 Jul 2023 07:15:34 +0000 Subject: [PATCH 18/65] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 5da8a785c..0d4230a9d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 5da8a785cf176f831a56f934ec8ec6069a965ecf +Subproject commit 0d4230a9db2ebc381e73296bc4e19bee09551771 diff --git a/scripts b/scripts index ee8217978..086efd3d0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ee8217978d25bc8f9c3efa21156dd8ff99896f68 +Subproject commit 086efd3d0562837937065148d26090e04b819060 From e3d3affdbc9d4c91940a72fa448a7bd0f267733c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 10:25:07 -0700 Subject: [PATCH 19/65] Revert "add new linkage dependency on dfhack" This reverts commit 9ddb3813c1ea14e5be99d59c11d871ba7a53359a. --- library/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 40006a432..5d2698bb0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -408,8 +408,8 @@ endif() target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_static dfhack-version ${PROJECT_LIBS}) set_target_properties(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") -target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static dfhack) -target_link_libraries(dfhack-run dfhack-client dfhack) +target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static) +target_link_libraries(dfhack-run dfhack-client) if(APPLE) add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...") From 125e4c623b9a13048e698af2f4b4b16587091837 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 11:07:51 -0700 Subject: [PATCH 20/65] avoid inducing link dependency on dfhack --- library/Core.cpp | 9 +++++++++ library/include/Core.h | 8 ++------ library/modules/Gui.cpp | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 4b036a83e..8d632e107 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1471,6 +1471,10 @@ std::string Core::getHackPath() #endif } +df::viewscreen * Core::getTopViewscreen() { + return getInstance().top_viewscreen; +} + bool Core::InitMainThread() { Filesystem::init(); @@ -1855,6 +1859,11 @@ void *Core::GetData( std::string key ) } } +Core& Core::getInstance() { + static Core instance; + return instance; +} + bool Core::isSuspended(void) { return ownerThread.load() == std::this_thread::get_id(); diff --git a/library/include/Core.h b/library/include/Core.h index a574b029a..a41aff927 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -125,11 +125,7 @@ namespace DFHack friend bool ::dfhooks_ncurses_key(int key); public: /// Get the single Core instance or make one. - static Core& getInstance() - { - static Core instance; - return instance; - } + static Core& getInstance(); /// check if the activity lock is owned by this thread bool isSuspended(void); /// Is everything OK? @@ -177,7 +173,7 @@ namespace DFHack bool isWorldLoaded() { return (last_world_data_ptr != NULL); } bool isMapLoaded() { return (last_local_map_ptr != NULL && last_world_data_ptr != NULL); } - static df::viewscreen *getTopViewscreen() { return getInstance().top_viewscreen; } + static df::viewscreen *getTopViewscreen(); DFHack::Console &getConsole() { return con; } diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 39f600bbf..102fef9d6 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -1709,7 +1709,7 @@ bool Gui::autoDFAnnouncement(df::report_init r, string message) if (a_flags.bits.D_DISPLAY) { world->status.display_timer = r.display_timer; - Gui::writeToGamelog("x" + to_string(repeat_count + 1)); + Gui::writeToGamelog('x' + to_string(repeat_count + 1)); } DEBUG(gui).print("Announcement succeeded as repeat:\n%s\n", message.c_str()); return true; From 0bbb72b44f59cadb4246081ef6ff9d4121fcbf22 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 11:46:33 -0700 Subject: [PATCH 21/65] Revert "add new linkage dependency on dfhack" This reverts commit ae545aa1d6a3577f6b4a2fbf485335544f4b23f4. --- library/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 40006a432..5d2698bb0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -408,8 +408,8 @@ endif() target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_static dfhack-version ${PROJECT_LIBS}) set_target_properties(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") -target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static dfhack) -target_link_libraries(dfhack-run dfhack-client dfhack) +target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_static) +target_link_libraries(dfhack-run dfhack-client) if(APPLE) add_custom_command(TARGET dfhack-run COMMAND ${dfhack_SOURCE_DIR}/package/darwin/fix-libs.sh WORKING_DIRECTORY ../ COMMENT "Fixing library dependencies...") From 43ea9b94c3026ce05acddd297c31e4e9e720a826 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 12:08:18 -0700 Subject: [PATCH 22/65] move clipboard wrappers into DFSDL module (but not in the namespace) --- library/MiscUtils.cpp | 8 -------- library/include/MiscUtils.h | 4 ---- library/include/modules/DFSDL.h | 11 ++++++++--- library/modules/DFSDL.cpp | 28 +++++++++++++++++++++------- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 6b50da347..7ffd4e1c6 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -472,11 +472,3 @@ DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::stri { return out.is_console() ? DF2CONSOLE(in) : in; } - -DFHACK_EXPORT std::string getClipboardTextCp437() { - return UTF2DF(DFHack::DFSDL::DFSDL_GetClipboardText()); -} - -DFHACK_EXPORT bool setClipboardTextCp437(std::string text) { - return DFHack::DFSDL::DFSDL_SetClipboardText(DF2UTF(text).c_str()); -} diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 099e86581..d14bdb6e9 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -496,7 +496,3 @@ DFHACK_EXPORT std::string UTF2DF(const std::string &in); DFHACK_EXPORT std::string DF2UTF(const std::string &in); DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in); DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in); - -// System clipboard -- submitted and returned text must be in CP437 -DFHACK_EXPORT std::string getClipboardTextCp437(); -DFHACK_EXPORT bool setClipboardTextCp437(std::string text); diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index 6cee58342..36dd641d5 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -47,12 +47,17 @@ DFHACK_EXPORT void DFSDL_FreeSurface(SDL_Surface *surface); // DFHACK_EXPORT int DFSDL_SemWait(SDL_sem *sem); // DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem); DFHACK_EXPORT int DFSDL_PushEvent(SDL_Event *event); +DFHACK_EXPORT void DFSDL_free(void *ptr); // submitted and returned text is UTF-8 -// see wrapper functions in MiscUtils.h for cp-437 variants -DFHACK_EXPORT std::string DFSDL_GetClipboardText(); -DFHACK_EXPORT bool DFSDL_SetClipboardText(const char *text); +// see wrapper functions below for cp-437 variants +DFHACK_EXPORT char * DFSDL_GetClipboardText(); +DFHACK_EXPORT int DFSDL_SetClipboardText(const char *text); } +// System clipboard -- submitted and returned text must be in CP437 +DFHACK_EXPORT std::string getClipboardTextCp437(); +DFHACK_EXPORT bool setClipboardTextCp437(std::string text); + } diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 280fb04d6..bc50769d0 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -135,15 +135,29 @@ int DFSDL::DFSDL_PushEvent(SDL_Event *event) { return g_SDL_PushEvent(event); } -std::string DFSDL::DFSDL_GetClipboardText() { - if (g_SDL_HasClipboardText() != SDL_TRUE) +void DFSDL::DFSDL_free(void *ptr) { + g_SDL_free(ptr); +} + +char * DFSDL::DFSDL_GetClipboardText() { + return g_SDL_GetClipboardText(); +} + +int DFSDL::DFSDL_SetClipboardText(const char *text) { + return g_SDL_SetClipboardText(text); +} + +DFHACK_EXPORT std::string DFHack::getClipboardTextCp437() { + if (!g_sdl_handle || g_SDL_HasClipboardText() != SDL_TRUE) return ""; char *text = g_SDL_GetClipboardText(); - std::string ret = text; - g_SDL_free(text); - return ret; + std::string textcp437 = UTF2DF(text); + DFHack::DFSDL::DFSDL_free(text); + return textcp437; } -bool DFSDL::DFSDL_SetClipboardText(const char *text) { - return g_SDL_SetClipboardText(text) == 0; +DFHACK_EXPORT bool DFHack::setClipboardTextCp437(std::string text) { + if (!g_sdl_handle) + return false; + return 0 == DFHack::DFSDL::DFSDL_SetClipboardText(DF2UTF(text).c_str()); } From c587cad47150722c78b064e59215f00728c8a4fd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 17:37:49 -0700 Subject: [PATCH 23/65] generalize to civ roles as well --- docs/dev/Lua API.rst | 12 +++++------- library/modules/Units.cpp | 16 +++++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 57e63dabb..0554629a9 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1441,13 +1441,11 @@ Units module * ``dfhack.units.getUnitByNobleRole(role_name)`` Returns the unit assigned to the given noble role, if any. ``role_name`` must - be one of the position codes associated with the active fort government. - Normally, this includes: ``MILITIA_COMMANDER``, ``MILITIA_CAPTAIN``, - ``SHERIFF``, ``CAPTAIN_OF_THE_GUARD``, ``EXPEDITION_LEADER``, ``MAYOR``, - ``MANAGER``, ``CHIEF_MEDICAL_DWARF``, ``BROKER``, ``BOOKKEEPER``, - ``CHAMPION``, ``HAMMERER``, ``DUNGEON_MASTER``, and ``MESSENGER``. Note that - if more than one unit has the role, only the first will be returned. See - ``getUnitsByNobleRole`` below for retrieving all units with a particular role. + be one of the position codes associated with the active fort or civilization + government. For example: ``CAPTAIN_OF_THE_GUARD``, ``MAYOR``, or ``BARON``. + Note that if more than one unit has the role, only the first will be + returned. See ``getUnitsByNobleRole`` below for retrieving all units with a + particular role. * ``dfhack.units.getUnitsByNobleRole(role_name)`` diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 9fb19cfb3..737ff0982 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -782,8 +782,7 @@ static int32_t get_noble_position_id(const df::historical_entity::T_positions &p return -1; } -static void get_assigned_noble_units(vector &units, const df::historical_entity::T_positions &positions, int32_t noble_position_id, size_t limit) { - units.clear(); +static void add_assigned_noble_units(vector &units, const df::historical_entity::T_positions &positions, int32_t noble_position_id, size_t limit) { for (auto &assignment : positions.assignments) { if (assignment->position_id != noble_position_id) continue; @@ -802,17 +801,20 @@ static void get_assigned_noble_units(vector &units, const df::histor static void get_units_by_noble_role(vector &units, string noble, size_t limit = 0) { auto &site = df::global::world->world_data->active_site[0]; for (auto &link : site->entity_links) { - auto gov = df::historical_entity::find(link->entity_id); - if (!gov || gov->type != df::historical_entity_type::SiteGovernment) + auto he = df::historical_entity::find(link->entity_id); + if (!he || + (he->type != df::historical_entity_type::SiteGovernment && + he->type != df::historical_entity_type::Civilization)) continue; - int32_t noble_position_id = get_noble_position_id(gov->positions, noble); + int32_t noble_position_id = get_noble_position_id(he->positions, noble); if (noble_position_id < 0) - return; - get_assigned_noble_units(units, gov->positions, noble_position_id, limit); + continue; + add_assigned_noble_units(units, he->positions, noble_position_id, limit); } } bool Units::getUnitsByNobleRole(vector &units, std::string noble) { + units.clear(); get_units_by_noble_role(units, noble); return !units.empty(); } From 0b9f84a5da94ebcba9106e6f0df5de2a36842635 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 19:06:57 -0700 Subject: [PATCH 24/65] don't print error if a building is not selected --- plugins/lua/buildingplan/inspectoroverlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua index 5262eccc8..1fcf19028 100644 --- a/plugins/lua/buildingplan/inspectoroverlay.lua +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -124,7 +124,7 @@ local function mouse_is_over_resume_button(rect) end function InspectorOverlay:onInput(keys) - if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding(true)) then return false end if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then @@ -136,7 +136,7 @@ function InspectorOverlay:onInput(keys) end function InspectorOverlay:render(dc) - if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding(true)) then return end if reset_inspector_flag then From 785405b281d5c4cc09e87146fe26eed450216667 Mon Sep 17 00:00:00 2001 From: Myk Date: Wed, 5 Jul 2023 22:34:25 -0700 Subject: [PATCH 25/65] Update DFSDL.cpp --- library/modules/DFSDL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index bc50769d0..aa54cf66c 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -5,7 +5,7 @@ #include "Debug.h" #include "PluginManager.h" -#include +#include namespace DFHack { DBG_DECLARE(core, dfsdl, DebugCategory::LINFO); From c0190397a9a355d068686a106d9791db06a3fb79 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:16:05 +0000 Subject: [PATCH 26/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 086efd3d0..64aff9c2d 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 086efd3d0562837937065148d26090e04b819060 +Subproject commit 64aff9c2deb0dd49ee755257c43d2a9fc62f92bb From aa6d28bab60868e42b366af930e17219be54a828 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 6 Jul 2023 03:21:19 -0700 Subject: [PATCH 27/65] generalize dfhack.items.isRequestedTradeGood --- docs/dev/Lua API.rst | 15 + library/include/modules/Items.h | 5 + library/modules/Items.cpp | 495 ++++++++++++++++++++++++++++++++ 3 files changed, 515 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 785e477dd..7f99af2d3 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1770,7 +1770,22 @@ Items module * ``dfhack.items.getValue(item)`` +<<<<<<< Updated upstream Calculates the Basic Value of an item, as seen in the View Item screen. +======= + Calculates the value of an item. If a ``df.caravan_state`` object is given + (from ``df.global.plotinfo.caravans`` or + ``df.global.main_interface.trade.mer``), then the value is modified by civ + properties and any trade agreements that might be in effect. In this case, + specify ``caravan_buying`` as ``true`` to get the price the caravan will pay + for the item and ``false`` to get the price that the caravan will sell the + item for. + +* ``dfhack.items.isRequestedTradeGood(item[, caravan_state])`` + + Returns whether a caravan will pay extra for the given item. If caravan_state + is not given, checks all active caravans. +>>>>>>> Stashed changes * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)`` diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 08737fb2b..f3dfe6dae 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -202,6 +202,11 @@ DFHACK_EXPORT bool canTrade(df::item *item); DFHACK_EXPORT bool canTradeWithContents(df::item *item); /// marks the given item for trade at the given depot DFHACK_EXPORT bool markForTrade(df::item *item, df::building_tradedepotst *depot); +<<<<<<< Updated upstream +======= +/// Returns true if an active caravan will pay extra for the given item +DFHACK_EXPORT bool isRequestedTradeGood(df::item *item, df::caravan_state *caravan = NULL); +>>>>>>> Stashed changes /// Checks whether the item is an assigned hauling vehicle DFHACK_EXPORT bool isRouteVehicle(df::item *item); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index f98c7c46b..e67fc64a6 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1435,7 +1435,502 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return value; } +<<<<<<< Updated upstream int Items::getValue(df::item *item) +======= +static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { + static const int32_t DEFAULT_WAR_MULTIPLIER = 256; + + if (!caravan) + return DEFAULT_WAR_MULTIPLIER; + auto caravan_he = df::historical_entity::find(caravan->entity); + if (!caravan_he) + return DEFAULT_WAR_MULTIPLIER; + int32_t war_alignment = caravan_he->entity_raw->sphere_alignment[df::sphere_type::WAR]; + if (war_alignment == DEFAULT_WAR_MULTIPLIER) + return DEFAULT_WAR_MULTIPLIER; + switch (item->getType()) { + case df::item_type::WEAPON: + { + auto weap_def = df::itemdef_weaponst::find(item->getSubtype()); + auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); + if (!weap_def || !caravan_cre_raw || caravan_cre_raw->adultsize < weap_def->minimum_size) + return DEFAULT_WAR_MULTIPLIER; + break; + } + case df::item_type::ARMOR: + case df::item_type::SHOES: + case df::item_type::HELM: + case df::item_type::GLOVES: + case df::item_type::PANTS: + { + if (item->getEffectiveArmorLevel() <= 0) + return DEFAULT_WAR_MULTIPLIER; + auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); + auto maker_cre_raw = df::creature_raw::find(item->getMakerRace()); + if (!caravan_cre_raw || !maker_cre_raw) + return DEFAULT_WAR_MULTIPLIER; + if (caravan_cre_raw->adultsize < ((maker_cre_raw->adultsize * 6) / 7)) + return DEFAULT_WAR_MULTIPLIER; + if (caravan_cre_raw->adultsize > ((maker_cre_raw->adultsize * 8) / 7)) + return DEFAULT_WAR_MULTIPLIER; + break; + } + case df::item_type::SHIELD: + case df::item_type::AMMO: + case df::item_type::BACKPACK: + case df::item_type::QUIVER: + break; + default: + return DEFAULT_WAR_MULTIPLIER; + } + return war_alignment; +} + +static const int32_t DEFAULT_AGREEMENT_MULTIPLIER = 128; + +static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_prices *buy_prices) { + if (!buy_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + for (size_t idx = 0; idx < buy_prices->price.size(); ++idx) { + if (buy_prices->items->item_type[idx] != item_type) + continue; + if (buy_prices->items->item_subtype[idx] != -1 && buy_prices->items->item_subtype[idx] != item_subtype) + continue; + if (buy_prices->items->mat_types[idx] != -1 && buy_prices->items->mat_types[idx] != mat_type) + continue; + if (buy_prices->items->mat_indices[idx] != -1 && buy_prices->items->mat_indices[idx] != mat_subtype) + continue; + return buy_prices->price[idx]; + } + return DEFAULT_AGREEMENT_MULTIPLIER; +} + +template +static int get_price(const std::vector &res, int32_t val, const std::vector &pri) { + for (size_t idx = 0; idx < res.size(); ++idx) { + if (res[idx] == val && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +template +static int get_price(const std::vector &mat_res, int32_t mat, const std::vector &gloss_res, int32_t gloss, const std::vector &pri) { + for (size_t idx = 0; idx < mat_res.size(); ++idx) { + if (mat_res[idx] == mat && (gloss_res[idx] == -1 || gloss_res[idx] == gloss) && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +static const uint16_t PLANT_BASE = 419; +static const uint16_t NUM_PLANT_TYPES = 200; + +static int32_t get_sell_request_multiplier(df::item *item, const df::historical_entity::T_resources &resources, const std::vector *prices) { + static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); + static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn); + static const df::dfhack_material_category leather_cat(df::dfhack_material_category::mask_leather); + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + bool inorganic = mat_type == df::builtin_mats::INORGANIC; + bool is_plant = (uint16_t)(mat_type - PLANT_BASE) < NUM_PLANT_TYPES; + + switch (item_type) { + case df::item_type::BAR: + if (inorganic) { + if (int32_t price = get_price(resources.metals, mat_subtype, prices[df::entity_sell_category::MetalBars]); price != -1) + return price; + } + break; + case df::item_type::SMALLGEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::SmallCutGems]); price != -1) + return price; + } + break; + case df::item_type::BLOCKS: + if (inorganic) { + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::StoneBlocks]); price != -1) + return price; + } + break; + case df::item_type::ROUGH: + if (int32_t price = get_price(resources.misc_mat.glass.mat_type, mat_type, resources.misc_mat.glass.mat_index, mat_subtype, + prices[df::entity_sell_category::Glass]); price != -1) + return price; + break; + case df::item_type::BOULDER: + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::Stone]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.clay.mat_type, mat_type, resources.misc_mat.clay.mat_index, mat_subtype, + prices[df::entity_sell_category::Clay]); price != -1) + return price; + break; + case df::item_type::WOOD: + if (int32_t price = get_price(resources.organic.wood.mat_type, mat_type, resources.organic.wood.mat_index, mat_subtype, + prices[df::entity_sell_category::Wood]); price != -1) + return price; + break; + case df::item_type::CHAIN: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid()) { + if (mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesSilk]); price != -1) + return price; + } + if (mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesYarn]); price != -1) + return price; + } + } + } + break; + case df::item_type::FLASK: + if (int32_t price = get_price(resources.misc_mat.flasks.mat_type, mat_type, resources.misc_mat.flasks.mat_index, mat_subtype, + prices[df::entity_sell_category::FlasksWaterskins]); price != -1) + return price; + break; + case df::item_type::GOBLET: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::CupsMugsGoblets]); price != -1) + return price; + break; + case df::item_type::INSTRUMENT: + if (int32_t price = get_price(resources.instrument_type, mat_subtype, prices[df::entity_sell_category::Instruments]); price != -1) + return price; + break; + case df::item_type::TOY: + if (int32_t price = get_price(resources.toy_type, mat_subtype, prices[df::entity_sell_category::Toys]); price != -1) + return price; + break; + case df::item_type::CAGE: + if (int32_t price = get_price(resources.misc_mat.cages.mat_type, mat_type, resources.misc_mat.cages.mat_index, mat_subtype, + prices[df::entity_sell_category::Cages]); price != -1) + return price; + break; + case df::item_type::BARREL: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Barrels]); price != -1) + return price; + break; + case df::item_type::BUCKET: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Buckets]); price != -1) + return price; + break; + case df::item_type::WEAPON: + if (int32_t price = get_price(resources.weapon_type, mat_subtype, prices[df::entity_sell_category::Weapons]); price != -1) + return price; + if (int32_t price = get_price(resources.digger_type, mat_subtype, prices[df::entity_sell_category::DiggingImplements]); price != -1) + return price; + if (int32_t price = get_price(resources.training_weapon_type, mat_subtype, prices[df::entity_sell_category::TrainingWeapons]); price != -1) + return price; + break; + case df::item_type::ARMOR: + if (int32_t price = get_price(resources.armor_type, mat_subtype, prices[df::entity_sell_category::Bodywear]); price != -1) + return price; + break; + case df::item_type::SHOES: + if (int32_t price = get_price(resources.shoes_type, mat_subtype, prices[df::entity_sell_category::Footwear]); price != -1) + return price; + break; + case df::item_type::SHIELD: + if (int32_t price = get_price(resources.shield_type, mat_subtype, prices[df::entity_sell_category::Shields]); price != -1) + return price; + break; + case df::item_type::HELM: + if (int32_t price = get_price(resources.helm_type, mat_subtype, prices[df::entity_sell_category::Headwear]); price != -1) + return price; + break; + case df::item_type::GLOVES: + if (int32_t price = get_price(resources.gloves_type, mat_subtype, prices[df::entity_sell_category::Handwear]); price != -1) + return price; + break; + case df::item_type::BAG: + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(leather_cat)) { + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsLeather]); price != -1) + return price; + } + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsPlant]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsYarn]); price != -1) + return price; + } + } + break; + case df::item_type::FIGURINE: + case df::item_type::AMULET: + case df::item_type::SCEPTER: + case df::item_type::CROWN: + case df::item_type::RING: + case df::item_type::EARRING: + case df::item_type::BRACELET: + case df::item_type::TOTEM: + case df::item_type::BOOK: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::Crafts]); price != -1) + return price; + break; + case df::item_type::AMMO: + if (int32_t price = get_price(resources.ammo_type, mat_subtype, prices[df::entity_sell_category::Ammo]); price != -1) + return price; + break; + case df::item_type::GEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::LargeCutGems]); price != -1) + return price; + } + break; + case df::item_type::ANVIL: + if (int32_t price = get_price(resources.metal.anvil.mat_type, mat_type, resources.metal.anvil.mat_index, mat_subtype, + prices[df::entity_sell_category::Anvils]); price != -1) + return price; + break; + case df::item_type::MEAT: + if (int32_t price = get_price(resources.misc_mat.meat.mat_type, mat_type, resources.misc_mat.meat.mat_index, mat_subtype, + prices[df::entity_sell_category::Meat]); price != -1) + return price; + break; + case df::item_type::FISH: + case df::item_type::FISH_RAW: + if (int32_t price = get_price(resources.fish_races, mat_type, resources.fish_castes, mat_subtype, + prices[df::entity_sell_category::Fish]); price != -1) + return price; + break; + case df::item_type::VERMIN: + case df::item_type::PET: + if (int32_t price = get_price(resources.animals.pet_races, mat_type, resources.animals.pet_castes, mat_subtype, + prices[df::entity_sell_category::Pets]); price != -1) + return price; + break; + case df::item_type::SEEDS: + if (int32_t price = get_price(resources.seeds.mat_type, mat_type, resources.seeds.mat_index, mat_subtype, + prices[df::entity_sell_category::Seeds]); price != -1) + return price; + break; + case df::item_type::PLANT: + if (int32_t price = get_price(resources.plants.mat_type, mat_type, resources.plants.mat_index, mat_subtype, + prices[df::entity_sell_category::Plants]); price != -1) + return price; + break; + case df::item_type::SKIN_TANNED: + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::Leather]); price != -1) + return price; + break; + case df::item_type::PLANT_GROWTH: + if (is_plant) { + if (int32_t price = get_price(resources.tree_fruit_plants, mat_type, resources.tree_fruit_growths, mat_subtype, + prices[df::entity_sell_category::FruitsNuts]); price != -1) + return price; + if (int32_t price = get_price(resources.shrub_fruit_plants, mat_type, resources.shrub_fruit_growths, mat_subtype, + prices[df::entity_sell_category::GardenVegetables]); price != -1) + return price; + } + break; + case df::item_type::THREAD: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadYarn]); price != -1) + return price; + } + } + break; + case df::item_type::CLOTH: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothYarn]); price != -1) + return price; + } + } + break; + case df::item_type::PANTS: + if (int32_t price = get_price(resources.pants_type, mat_subtype, prices[df::entity_sell_category::Legwear]); price != -1) + return price; + break; + case df::item_type::BACKPACK: + if (int32_t price = get_price(resources.misc_mat.backpacks.mat_type, mat_type, resources.misc_mat.backpacks.mat_index, mat_subtype, + prices[df::entity_sell_category::Backpacks]); price != -1) + return price; + break; + case df::item_type::QUIVER: + if (int32_t price = get_price(resources.misc_mat.quivers.mat_type, mat_type, resources.misc_mat.quivers.mat_index, mat_subtype, + prices[df::entity_sell_category::Quivers]); price != -1) + return price; + break; + case df::item_type::TRAPCOMP: + if (int32_t price = get_price(resources.trapcomp_type, mat_subtype, prices[df::entity_sell_category::TrapComponents]); price != -1) + return price; + break; + case df::item_type::DRINK: + if (int32_t price = get_price(resources.misc_mat.booze.mat_type, mat_type, resources.misc_mat.booze.mat_index, mat_subtype, + prices[df::entity_sell_category::Drinks]); price != -1) + return price; + break; + case df::item_type::POWDER_MISC: + if (int32_t price = get_price(resources.misc_mat.powders.mat_type, mat_type, resources.misc_mat.powders.mat_index, mat_subtype, + prices[df::entity_sell_category::Powders]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.sand.mat_type, mat_type, resources.misc_mat.sand.mat_index, mat_subtype, + prices[df::entity_sell_category::Sand]); price != -1) + return price; + break; + case df::item_type::CHEESE: + if (int32_t price = get_price(resources.misc_mat.cheese.mat_type, mat_type, resources.misc_mat.cheese.mat_index, mat_subtype, + prices[df::entity_sell_category::Cheese]); price != -1) + return price; + break; + case df::item_type::LIQUID_MISC: + if (int32_t price = get_price(resources.misc_mat.extracts.mat_type, mat_type, resources.misc_mat.extracts.mat_index, mat_subtype, + prices[df::entity_sell_category::Extracts]); price != -1) + return price; + break; + case df::item_type::SPLINT: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Splints]); price != -1) + return price; + break; + case df::item_type::CRUTCH: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Crutches]); price != -1) + return price; + break; + case df::item_type::TOOL: + if (int32_t price = get_price(resources.tool_type, mat_subtype, prices[df::entity_sell_category::Tools]); price != -1) + return price; + break; + case df::item_type::EGG: + if (int32_t price = get_price(resources.egg_races, mat_type, resources.egg_castes, mat_subtype, + prices[df::entity_sell_category::Eggs]); price != -1) + return price; + break; + case df::item_type::SHEET: + if (int32_t price = get_price(resources.organic.parchment.mat_type, mat_type, resources.organic.parchment.mat_index, mat_subtype, + prices[df::entity_sell_category::Parchment]); price != -1) + return price; + break; + default: + break; + } + + for (size_t idx = 0; idx < resources.wood_products.item_type.size(); ++idx) { + if (resources.wood_products.item_type[idx] == item_type && + (resources.wood_products.item_subtype[idx] == -1 || resources.wood_products.item_subtype[idx] == item_subtype) && + resources.wood_products.material.mat_type[idx] == mat_type && + (resources.wood_products.material.mat_index[idx] == -1 || resources.wood_products.material.mat_index[idx] == mat_subtype) && + prices[df::entity_sell_category::Miscellaneous].size() > idx) + return prices[df::entity_sell_category::Miscellaneous][idx]; + } + + return DEFAULT_AGREEMENT_MULTIPLIER; +} + +static int32_t get_sell_request_multiplier(df::item *item, const df::caravan_state *caravan) { + const df::entity_sell_prices *sell_prices = caravan->sell_prices; + if (!sell_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + + auto caravan_he = df::historical_entity::find(caravan->entity); + if (!caravan_he) + return DEFAULT_AGREEMENT_MULTIPLIER; + + return get_sell_request_multiplier(item, caravan_he->resources, &sell_prices->price[0]); +} + +static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_state *caravan, bool caravan_buying) { + if (!caravan) + return DEFAULT_AGREEMENT_MULTIPLIER; + return caravan_buying ? get_buy_request_multiplier(item, caravan->buy_prices) + : get_sell_request_multiplier(item, caravan); +} + +static bool is_requested_trade_good(df::item *item, df::caravan_state *caravan) { + auto trade_state = caravan->trade_state; + if (caravan->time_remaining <= 0 || + (trade_state != df::caravan_state::T_trade_state::Approaching && + trade_state != df::caravan_state::T_trade_state::AtDepot)) + return false; + return get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER; +} + +bool Items::isRequestedTradeGood(df::item *item, df::caravan_state *caravan) { + if (caravan) + return is_requested_trade_good(item, caravan); + + for (auto caravan : df::global::plotinfo->caravans) { + auto trade_state = caravan->trade_state; + if (caravan->time_remaining <= 0 || + (trade_state != df::caravan_state::T_trade_state::Approaching && + trade_state != df::caravan_state::T_trade_state::AtDepot)) + continue; + if (get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER) + return true; + } + return false; +} + +int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) +>>>>>>> Stashed changes { CHECK_NULL_POINTER(item); From 9849885df6e370a17b658d2558f519592b05624f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 6 Jul 2023 03:23:17 -0700 Subject: [PATCH 28/65] Revert "generalize dfhack.items.isRequestedTradeGood" This reverts commit aa6d28bab60868e42b366af930e17219be54a828. --- docs/dev/Lua API.rst | 15 - library/include/modules/Items.h | 5 - library/modules/Items.cpp | 495 -------------------------------- 3 files changed, 515 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 7f99af2d3..785e477dd 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1770,22 +1770,7 @@ Items module * ``dfhack.items.getValue(item)`` -<<<<<<< Updated upstream Calculates the Basic Value of an item, as seen in the View Item screen. -======= - Calculates the value of an item. If a ``df.caravan_state`` object is given - (from ``df.global.plotinfo.caravans`` or - ``df.global.main_interface.trade.mer``), then the value is modified by civ - properties and any trade agreements that might be in effect. In this case, - specify ``caravan_buying`` as ``true`` to get the price the caravan will pay - for the item and ``false`` to get the price that the caravan will sell the - item for. - -* ``dfhack.items.isRequestedTradeGood(item[, caravan_state])`` - - Returns whether a caravan will pay extra for the given item. If caravan_state - is not given, checks all active caravans. ->>>>>>> Stashed changes * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)`` diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index f3dfe6dae..08737fb2b 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -202,11 +202,6 @@ DFHACK_EXPORT bool canTrade(df::item *item); DFHACK_EXPORT bool canTradeWithContents(df::item *item); /// marks the given item for trade at the given depot DFHACK_EXPORT bool markForTrade(df::item *item, df::building_tradedepotst *depot); -<<<<<<< Updated upstream -======= -/// Returns true if an active caravan will pay extra for the given item -DFHACK_EXPORT bool isRequestedTradeGood(df::item *item, df::caravan_state *caravan = NULL); ->>>>>>> Stashed changes /// Checks whether the item is an assigned hauling vehicle DFHACK_EXPORT bool isRouteVehicle(df::item *item); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index e67fc64a6..f98c7c46b 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1435,502 +1435,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return value; } -<<<<<<< Updated upstream int Items::getValue(df::item *item) -======= -static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { - static const int32_t DEFAULT_WAR_MULTIPLIER = 256; - - if (!caravan) - return DEFAULT_WAR_MULTIPLIER; - auto caravan_he = df::historical_entity::find(caravan->entity); - if (!caravan_he) - return DEFAULT_WAR_MULTIPLIER; - int32_t war_alignment = caravan_he->entity_raw->sphere_alignment[df::sphere_type::WAR]; - if (war_alignment == DEFAULT_WAR_MULTIPLIER) - return DEFAULT_WAR_MULTIPLIER; - switch (item->getType()) { - case df::item_type::WEAPON: - { - auto weap_def = df::itemdef_weaponst::find(item->getSubtype()); - auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); - if (!weap_def || !caravan_cre_raw || caravan_cre_raw->adultsize < weap_def->minimum_size) - return DEFAULT_WAR_MULTIPLIER; - break; - } - case df::item_type::ARMOR: - case df::item_type::SHOES: - case df::item_type::HELM: - case df::item_type::GLOVES: - case df::item_type::PANTS: - { - if (item->getEffectiveArmorLevel() <= 0) - return DEFAULT_WAR_MULTIPLIER; - auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); - auto maker_cre_raw = df::creature_raw::find(item->getMakerRace()); - if (!caravan_cre_raw || !maker_cre_raw) - return DEFAULT_WAR_MULTIPLIER; - if (caravan_cre_raw->adultsize < ((maker_cre_raw->adultsize * 6) / 7)) - return DEFAULT_WAR_MULTIPLIER; - if (caravan_cre_raw->adultsize > ((maker_cre_raw->adultsize * 8) / 7)) - return DEFAULT_WAR_MULTIPLIER; - break; - } - case df::item_type::SHIELD: - case df::item_type::AMMO: - case df::item_type::BACKPACK: - case df::item_type::QUIVER: - break; - default: - return DEFAULT_WAR_MULTIPLIER; - } - return war_alignment; -} - -static const int32_t DEFAULT_AGREEMENT_MULTIPLIER = 128; - -static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_prices *buy_prices) { - if (!buy_prices) - return DEFAULT_AGREEMENT_MULTIPLIER; - - int16_t item_type = item->getType(); - int16_t item_subtype = item->getSubtype(); - int16_t mat_type = item->getMaterial(); - int32_t mat_subtype = item->getMaterialIndex(); - - for (size_t idx = 0; idx < buy_prices->price.size(); ++idx) { - if (buy_prices->items->item_type[idx] != item_type) - continue; - if (buy_prices->items->item_subtype[idx] != -1 && buy_prices->items->item_subtype[idx] != item_subtype) - continue; - if (buy_prices->items->mat_types[idx] != -1 && buy_prices->items->mat_types[idx] != mat_type) - continue; - if (buy_prices->items->mat_indices[idx] != -1 && buy_prices->items->mat_indices[idx] != mat_subtype) - continue; - return buy_prices->price[idx]; - } - return DEFAULT_AGREEMENT_MULTIPLIER; -} - -template -static int get_price(const std::vector &res, int32_t val, const std::vector &pri) { - for (size_t idx = 0; idx < res.size(); ++idx) { - if (res[idx] == val && pri.size() > idx) - return pri[idx]; - } - return -1; -} - -template -static int get_price(const std::vector &mat_res, int32_t mat, const std::vector &gloss_res, int32_t gloss, const std::vector &pri) { - for (size_t idx = 0; idx < mat_res.size(); ++idx) { - if (mat_res[idx] == mat && (gloss_res[idx] == -1 || gloss_res[idx] == gloss) && pri.size() > idx) - return pri[idx]; - } - return -1; -} - -static const uint16_t PLANT_BASE = 419; -static const uint16_t NUM_PLANT_TYPES = 200; - -static int32_t get_sell_request_multiplier(df::item *item, const df::historical_entity::T_resources &resources, const std::vector *prices) { - static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); - static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn); - static const df::dfhack_material_category leather_cat(df::dfhack_material_category::mask_leather); - - int16_t item_type = item->getType(); - int16_t item_subtype = item->getSubtype(); - int16_t mat_type = item->getMaterial(); - int32_t mat_subtype = item->getMaterialIndex(); - - bool inorganic = mat_type == df::builtin_mats::INORGANIC; - bool is_plant = (uint16_t)(mat_type - PLANT_BASE) < NUM_PLANT_TYPES; - - switch (item_type) { - case df::item_type::BAR: - if (inorganic) { - if (int32_t price = get_price(resources.metals, mat_subtype, prices[df::entity_sell_category::MetalBars]); price != -1) - return price; - } - break; - case df::item_type::SMALLGEM: - if (inorganic) { - if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::SmallCutGems]); price != -1) - return price; - } - break; - case df::item_type::BLOCKS: - if (inorganic) { - if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::StoneBlocks]); price != -1) - return price; - } - break; - case df::item_type::ROUGH: - if (int32_t price = get_price(resources.misc_mat.glass.mat_type, mat_type, resources.misc_mat.glass.mat_index, mat_subtype, - prices[df::entity_sell_category::Glass]); price != -1) - return price; - break; - case df::item_type::BOULDER: - if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::Stone]); price != -1) - return price; - if (int32_t price = get_price(resources.misc_mat.clay.mat_type, mat_type, resources.misc_mat.clay.mat_index, mat_subtype, - prices[df::entity_sell_category::Clay]); price != -1) - return price; - break; - case df::item_type::WOOD: - if (int32_t price = get_price(resources.organic.wood.mat_type, mat_type, resources.organic.wood.mat_index, mat_subtype, - prices[df::entity_sell_category::Wood]); price != -1) - return price; - break; - case df::item_type::CHAIN: - if (is_plant) { - if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, - prices[df::entity_sell_category::RopesPlant]); price != -1) - return price; - } - { - MaterialInfo mi; - mi.decode(mat_type, mat_subtype); - if (mi.isValid()) { - if (mi.matches(silk_cat)) { - if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, - prices[df::entity_sell_category::RopesSilk]); price != -1) - return price; - } - if (mi.matches(yarn_cat)) { - if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, - prices[df::entity_sell_category::RopesYarn]); price != -1) - return price; - } - } - } - break; - case df::item_type::FLASK: - if (int32_t price = get_price(resources.misc_mat.flasks.mat_type, mat_type, resources.misc_mat.flasks.mat_index, mat_subtype, - prices[df::entity_sell_category::FlasksWaterskins]); price != -1) - return price; - break; - case df::item_type::GOBLET: - if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, - prices[df::entity_sell_category::CupsMugsGoblets]); price != -1) - return price; - break; - case df::item_type::INSTRUMENT: - if (int32_t price = get_price(resources.instrument_type, mat_subtype, prices[df::entity_sell_category::Instruments]); price != -1) - return price; - break; - case df::item_type::TOY: - if (int32_t price = get_price(resources.toy_type, mat_subtype, prices[df::entity_sell_category::Toys]); price != -1) - return price; - break; - case df::item_type::CAGE: - if (int32_t price = get_price(resources.misc_mat.cages.mat_type, mat_type, resources.misc_mat.cages.mat_index, mat_subtype, - prices[df::entity_sell_category::Cages]); price != -1) - return price; - break; - case df::item_type::BARREL: - if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, - prices[df::entity_sell_category::Barrels]); price != -1) - return price; - break; - case df::item_type::BUCKET: - if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, - prices[df::entity_sell_category::Buckets]); price != -1) - return price; - break; - case df::item_type::WEAPON: - if (int32_t price = get_price(resources.weapon_type, mat_subtype, prices[df::entity_sell_category::Weapons]); price != -1) - return price; - if (int32_t price = get_price(resources.digger_type, mat_subtype, prices[df::entity_sell_category::DiggingImplements]); price != -1) - return price; - if (int32_t price = get_price(resources.training_weapon_type, mat_subtype, prices[df::entity_sell_category::TrainingWeapons]); price != -1) - return price; - break; - case df::item_type::ARMOR: - if (int32_t price = get_price(resources.armor_type, mat_subtype, prices[df::entity_sell_category::Bodywear]); price != -1) - return price; - break; - case df::item_type::SHOES: - if (int32_t price = get_price(resources.shoes_type, mat_subtype, prices[df::entity_sell_category::Footwear]); price != -1) - return price; - break; - case df::item_type::SHIELD: - if (int32_t price = get_price(resources.shield_type, mat_subtype, prices[df::entity_sell_category::Shields]); price != -1) - return price; - break; - case df::item_type::HELM: - if (int32_t price = get_price(resources.helm_type, mat_subtype, prices[df::entity_sell_category::Headwear]); price != -1) - return price; - break; - case df::item_type::GLOVES: - if (int32_t price = get_price(resources.gloves_type, mat_subtype, prices[df::entity_sell_category::Handwear]); price != -1) - return price; - break; - case df::item_type::BAG: - { - MaterialInfo mi; - mi.decode(mat_type, mat_subtype); - if (mi.isValid() && mi.matches(leather_cat)) { - if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, - prices[df::entity_sell_category::BagsLeather]); price != -1) - return price; - } - if (is_plant) { - if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, - prices[df::entity_sell_category::BagsPlant]); price != -1) - return price; - } - if (mi.isValid() && mi.matches(silk_cat)) { - if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, - prices[df::entity_sell_category::BagsSilk]); price != -1) - return price; - } - if (mi.isValid() && mi.matches(yarn_cat)) { - if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, - prices[df::entity_sell_category::BagsYarn]); price != -1) - return price; - } - } - break; - case df::item_type::FIGURINE: - case df::item_type::AMULET: - case df::item_type::SCEPTER: - case df::item_type::CROWN: - case df::item_type::RING: - case df::item_type::EARRING: - case df::item_type::BRACELET: - case df::item_type::TOTEM: - case df::item_type::BOOK: - if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, - prices[df::entity_sell_category::Crafts]); price != -1) - return price; - break; - case df::item_type::AMMO: - if (int32_t price = get_price(resources.ammo_type, mat_subtype, prices[df::entity_sell_category::Ammo]); price != -1) - return price; - break; - case df::item_type::GEM: - if (inorganic) { - if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::LargeCutGems]); price != -1) - return price; - } - break; - case df::item_type::ANVIL: - if (int32_t price = get_price(resources.metal.anvil.mat_type, mat_type, resources.metal.anvil.mat_index, mat_subtype, - prices[df::entity_sell_category::Anvils]); price != -1) - return price; - break; - case df::item_type::MEAT: - if (int32_t price = get_price(resources.misc_mat.meat.mat_type, mat_type, resources.misc_mat.meat.mat_index, mat_subtype, - prices[df::entity_sell_category::Meat]); price != -1) - return price; - break; - case df::item_type::FISH: - case df::item_type::FISH_RAW: - if (int32_t price = get_price(resources.fish_races, mat_type, resources.fish_castes, mat_subtype, - prices[df::entity_sell_category::Fish]); price != -1) - return price; - break; - case df::item_type::VERMIN: - case df::item_type::PET: - if (int32_t price = get_price(resources.animals.pet_races, mat_type, resources.animals.pet_castes, mat_subtype, - prices[df::entity_sell_category::Pets]); price != -1) - return price; - break; - case df::item_type::SEEDS: - if (int32_t price = get_price(resources.seeds.mat_type, mat_type, resources.seeds.mat_index, mat_subtype, - prices[df::entity_sell_category::Seeds]); price != -1) - return price; - break; - case df::item_type::PLANT: - if (int32_t price = get_price(resources.plants.mat_type, mat_type, resources.plants.mat_index, mat_subtype, - prices[df::entity_sell_category::Plants]); price != -1) - return price; - break; - case df::item_type::SKIN_TANNED: - if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, - prices[df::entity_sell_category::Leather]); price != -1) - return price; - break; - case df::item_type::PLANT_GROWTH: - if (is_plant) { - if (int32_t price = get_price(resources.tree_fruit_plants, mat_type, resources.tree_fruit_growths, mat_subtype, - prices[df::entity_sell_category::FruitsNuts]); price != -1) - return price; - if (int32_t price = get_price(resources.shrub_fruit_plants, mat_type, resources.shrub_fruit_growths, mat_subtype, - prices[df::entity_sell_category::GardenVegetables]); price != -1) - return price; - } - break; - case df::item_type::THREAD: - if (is_plant) { - if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, - prices[df::entity_sell_category::ThreadPlant]); price != -1) - return price; - } - { - MaterialInfo mi; - mi.decode(mat_type, mat_subtype); - if (mi.isValid() && mi.matches(silk_cat)) { - if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, - prices[df::entity_sell_category::ThreadSilk]); price != -1) - return price; - } - if (mi.isValid() && mi.matches(yarn_cat)) { - if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, - prices[df::entity_sell_category::ThreadYarn]); price != -1) - return price; - } - } - break; - case df::item_type::CLOTH: - if (is_plant) { - if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, - prices[df::entity_sell_category::ClothPlant]); price != -1) - return price; - } - { - MaterialInfo mi; - mi.decode(mat_type, mat_subtype); - if (mi.isValid() && mi.matches(silk_cat)) { - if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, - prices[df::entity_sell_category::ClothSilk]); price != -1) - return price; - } - if (mi.isValid() && mi.matches(yarn_cat)) { - if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, - prices[df::entity_sell_category::ClothYarn]); price != -1) - return price; - } - } - break; - case df::item_type::PANTS: - if (int32_t price = get_price(resources.pants_type, mat_subtype, prices[df::entity_sell_category::Legwear]); price != -1) - return price; - break; - case df::item_type::BACKPACK: - if (int32_t price = get_price(resources.misc_mat.backpacks.mat_type, mat_type, resources.misc_mat.backpacks.mat_index, mat_subtype, - prices[df::entity_sell_category::Backpacks]); price != -1) - return price; - break; - case df::item_type::QUIVER: - if (int32_t price = get_price(resources.misc_mat.quivers.mat_type, mat_type, resources.misc_mat.quivers.mat_index, mat_subtype, - prices[df::entity_sell_category::Quivers]); price != -1) - return price; - break; - case df::item_type::TRAPCOMP: - if (int32_t price = get_price(resources.trapcomp_type, mat_subtype, prices[df::entity_sell_category::TrapComponents]); price != -1) - return price; - break; - case df::item_type::DRINK: - if (int32_t price = get_price(resources.misc_mat.booze.mat_type, mat_type, resources.misc_mat.booze.mat_index, mat_subtype, - prices[df::entity_sell_category::Drinks]); price != -1) - return price; - break; - case df::item_type::POWDER_MISC: - if (int32_t price = get_price(resources.misc_mat.powders.mat_type, mat_type, resources.misc_mat.powders.mat_index, mat_subtype, - prices[df::entity_sell_category::Powders]); price != -1) - return price; - if (int32_t price = get_price(resources.misc_mat.sand.mat_type, mat_type, resources.misc_mat.sand.mat_index, mat_subtype, - prices[df::entity_sell_category::Sand]); price != -1) - return price; - break; - case df::item_type::CHEESE: - if (int32_t price = get_price(resources.misc_mat.cheese.mat_type, mat_type, resources.misc_mat.cheese.mat_index, mat_subtype, - prices[df::entity_sell_category::Cheese]); price != -1) - return price; - break; - case df::item_type::LIQUID_MISC: - if (int32_t price = get_price(resources.misc_mat.extracts.mat_type, mat_type, resources.misc_mat.extracts.mat_index, mat_subtype, - prices[df::entity_sell_category::Extracts]); price != -1) - return price; - break; - case df::item_type::SPLINT: - if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, - prices[df::entity_sell_category::Splints]); price != -1) - return price; - break; - case df::item_type::CRUTCH: - if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, - prices[df::entity_sell_category::Crutches]); price != -1) - return price; - break; - case df::item_type::TOOL: - if (int32_t price = get_price(resources.tool_type, mat_subtype, prices[df::entity_sell_category::Tools]); price != -1) - return price; - break; - case df::item_type::EGG: - if (int32_t price = get_price(resources.egg_races, mat_type, resources.egg_castes, mat_subtype, - prices[df::entity_sell_category::Eggs]); price != -1) - return price; - break; - case df::item_type::SHEET: - if (int32_t price = get_price(resources.organic.parchment.mat_type, mat_type, resources.organic.parchment.mat_index, mat_subtype, - prices[df::entity_sell_category::Parchment]); price != -1) - return price; - break; - default: - break; - } - - for (size_t idx = 0; idx < resources.wood_products.item_type.size(); ++idx) { - if (resources.wood_products.item_type[idx] == item_type && - (resources.wood_products.item_subtype[idx] == -1 || resources.wood_products.item_subtype[idx] == item_subtype) && - resources.wood_products.material.mat_type[idx] == mat_type && - (resources.wood_products.material.mat_index[idx] == -1 || resources.wood_products.material.mat_index[idx] == mat_subtype) && - prices[df::entity_sell_category::Miscellaneous].size() > idx) - return prices[df::entity_sell_category::Miscellaneous][idx]; - } - - return DEFAULT_AGREEMENT_MULTIPLIER; -} - -static int32_t get_sell_request_multiplier(df::item *item, const df::caravan_state *caravan) { - const df::entity_sell_prices *sell_prices = caravan->sell_prices; - if (!sell_prices) - return DEFAULT_AGREEMENT_MULTIPLIER; - - auto caravan_he = df::historical_entity::find(caravan->entity); - if (!caravan_he) - return DEFAULT_AGREEMENT_MULTIPLIER; - - return get_sell_request_multiplier(item, caravan_he->resources, &sell_prices->price[0]); -} - -static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_state *caravan, bool caravan_buying) { - if (!caravan) - return DEFAULT_AGREEMENT_MULTIPLIER; - return caravan_buying ? get_buy_request_multiplier(item, caravan->buy_prices) - : get_sell_request_multiplier(item, caravan); -} - -static bool is_requested_trade_good(df::item *item, df::caravan_state *caravan) { - auto trade_state = caravan->trade_state; - if (caravan->time_remaining <= 0 || - (trade_state != df::caravan_state::T_trade_state::Approaching && - trade_state != df::caravan_state::T_trade_state::AtDepot)) - return false; - return get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER; -} - -bool Items::isRequestedTradeGood(df::item *item, df::caravan_state *caravan) { - if (caravan) - return is_requested_trade_good(item, caravan); - - for (auto caravan : df::global::plotinfo->caravans) { - auto trade_state = caravan->trade_state; - if (caravan->time_remaining <= 0 || - (trade_state != df::caravan_state::T_trade_state::Approaching && - trade_state != df::caravan_state::T_trade_state::AtDepot)) - continue; - if (get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER) - return true; - } - return false; -} - -int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) ->>>>>>> Stashed changes { CHECK_NULL_POINTER(item); From c2e647b79b7befc81dd56f9e011a647967413a22 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 18:05:30 -0700 Subject: [PATCH 29/65] fix item prices and algorithm adjust prices according to race WAR affinity --- docs/changelog.txt | 2 + docs/dev/Lua API.rst | 12 +++- library/include/modules/Items.h | 5 +- library/modules/Items.cpp | 115 ++++++++++++++++++++++++++++---- 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8971dd0ac..4a8a100df 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,10 +47,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role ## Internals +- Price calculations fixed for many item types ## Lua - ``dfhack.items.markForTrade``: new API for marking items for trade - ``dfhack.units.getUnitByNobleRole``, ``dfhack.units.getUnitsByNobleRole``: unit lookup API by role +- ``dfhack.items.getValue``: gained optional ``caravan`` and ``caravan_buying`` parameters for prices that take trader races and agreements into account ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 785e477dd..e81a34de3 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1768,9 +1768,15 @@ Items module Calculates the base value for an item of the specified type and material. -* ``dfhack.items.getValue(item)`` - - Calculates the Basic Value of an item, as seen in the View Item screen. +* ``dfhack.items.getValue(item[, caravan_state, caravan_buying])`` + + Calculates the value of an item. If a ``df.caravan_state`` object is given + (from ``df.global.plotinfo.caravans`` or + ``df.global.main_interface.trade.mer``), then the value is modified by civ + properties and any trade agreements that might be in effect. In this case, + specify ``caravan_buying`` as ``true`` to get the price the caravan will pay + for the item and ``false`` to get the price that the caravan will sell the + item for. * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)`` diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 08737fb2b..6cce5d2f1 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -34,6 +34,7 @@ distribution. #include "DataDefs.h" #include "df/building_tradedepotst.h" +#include "df/caravan_state.h" #include "df/item.h" #include "df/item_type.h" #include "df/general_ref.h" @@ -189,8 +190,8 @@ DFHACK_EXPORT df::proj_itemst *makeProjectile(MapExtras::MapCache &mc, df::item /// Gets value of base-quality item with specified type and material DFHACK_EXPORT int getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_subtype); -/// Gets the value of a specific item, ignoring civ values and trade agreements -DFHACK_EXPORT int getValue(df::item *item); +/// Gets the value of a specific item, taking into account civ values and trade agreements if a caravan is given +DFHACK_EXPORT int getValue(df::item *item, df::caravan_state *caravan = NULL, bool caravan_buying = false); DFHACK_EXPORT int32_t createItem(df::item_type type, int16_t item_subtype, int16_t mat_type, int32_t mat_index, df::unit* creator); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index f98c7c46b..a2107aa1d 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -50,8 +50,10 @@ using namespace std; #include "df/building.h" #include "df/building_actual.h" #include "df/building_tradedepotst.h" +#include "df/caravan_state.h" #include "df/caste_raw.h" #include "df/creature_raw.h" +#include "df/entity_raw.h" #include "df/general_ref.h" #include "df/general_ref_building_holderst.h" #include "df/general_ref_contained_in_itemst.h" @@ -828,15 +830,15 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) item->getItemDescription(&tmp, type); if (decorate) { - if (item->flags.bits.foreign) - tmp = "(" + tmp + ")"; - addQuality(tmp, item->getQuality()); if (item->isImproved()) { tmp = '\xAE' + tmp + '\xAF'; // («) + tmp + (») addQuality(tmp, item->getImprovementQuality()); } + + if (item->flags.bits.foreign) + tmp = "(" + tmp + ")"; } return tmp; @@ -1197,18 +1199,24 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat switch (item_type) { case item_type::BAR: - case item_type::SMALLGEM: case item_type::BLOCKS: case item_type::SKIN_TANNED: value = 5; break; - case item_type::ROUGH: + case item_type::SMALLGEM: + value = 20; + break; + case item_type::BOULDER: case item_type::WOOD: value = 3; break; + case item_type::ROUGH: + value = 6; + break; + case item_type::DOOR: case item_type::FLOODGATE: case item_type::BED: @@ -1224,6 +1232,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat case item_type::TABLE: case item_type::COFFIN: case item_type::BOX: + case item_type::BAG: case item_type::BIN: case item_type::ARMORSTAND: case item_type::WEAPONRACK: @@ -1367,6 +1376,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat case item_type::COIN: case item_type::GLOB: case item_type::ORTHOPEDIC_CAST: + case item_type::BRANCH: value = 1; break; @@ -1435,7 +1445,61 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat return value; } -int Items::getValue(df::item *item) +static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { + static const int32_t DEFAULT_MULTIPLIER = 256; + + if (!caravan) + return DEFAULT_MULTIPLIER; + auto caravan_he = df::historical_entity::find(caravan->entity); + if (!caravan_he) + return DEFAULT_MULTIPLIER; + int32_t war_alignment = caravan_he->entity_raw->sphere_alignment[df::sphere_type::WAR]; + if (war_alignment == DEFAULT_MULTIPLIER) + return DEFAULT_MULTIPLIER; + switch (item->getType()) { + case df::item_type::WEAPON: + { + auto weap_def = df::itemdef_weaponst::find(item->getSubtype()); + auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); + if (!weap_def || !caravan_cre_raw || caravan_cre_raw->adultsize < weap_def->minimum_size) + return DEFAULT_MULTIPLIER; + break; + } + case df::item_type::ARMOR: + case df::item_type::SHOES: + case df::item_type::HELM: + case df::item_type::GLOVES: + case df::item_type::PANTS: + { + if (item->getEffectiveArmorLevel() <= 0) + return DEFAULT_MULTIPLIER; + auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); + auto maker_cre_raw = df::creature_raw::find(item->getMakerRace()); + if (!caravan_cre_raw || !maker_cre_raw) + return DEFAULT_MULTIPLIER; + if (caravan_cre_raw->adultsize < ((maker_cre_raw->adultsize * 6) / 7)) + return DEFAULT_MULTIPLIER; + if (caravan_cre_raw->adultsize > ((maker_cre_raw->adultsize * 8) / 7)) + return DEFAULT_MULTIPLIER; + break; + } + case df::item_type::SHIELD: + case df::item_type::AMMO: + case df::item_type::BACKPACK: + case df::item_type::QUIVER: + break; + default: + return DEFAULT_MULTIPLIER; + } + return war_alignment; +} + +// returns 0 if the multiplier would be equal to 1.0 +static float get_trade_agreement_multiplier(df::item *item, df::caravan_state *caravan, bool caravan_buying) { + return 0; +} + +int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) { CHECK_NULL_POINTER(item); @@ -1447,16 +1511,38 @@ int Items::getValue(df::item *item) // Get base value for item type, subtype, and material int value = getItemBaseValue(item_type, item_subtype, mat_type, mat_subtype); - // Ignore entity value modifications + // entity value modifications + value *= get_war_multiplier(item, caravan); + value >>= 8; // Improve value based on quality - int quality = item->getQuality(); - value *= (quality + 1); - if (quality == 5) + switch (item->getQuality()) { + case 1: + value *= 1.1; + value += 3; + break; + case 2: + value *= 1.2; + value += 6; + break; + case 3: + value *= 1.333; + value += 10; + break; + case 4: + value *= 1.5; + value += 15; + break; + case 5: value *= 2; + value += 30; + break; + default: + break; + } // Add improvement values - int impValue = item->getThreadDyeValue(NULL) + item->getImprovementsValue(NULL); + int impValue = item->getThreadDyeValue(caravan) + item->getImprovementsValue(caravan); if (item_type == item_type::AMMO) // Ammo improvements are worth less impValue /= 30; value += impValue; @@ -1481,12 +1567,17 @@ int Items::getValue(df::item *item) if (item->flags.bits.artifact_mood) value *= 10; + // modify buy/sell prices if a caravan is given + float trade_agreement_multiplier = get_trade_agreement_multiplier(item, caravan, caravan_buying); + if (trade_agreement_multiplier > 0) + value *= trade_agreement_multiplier; + // Boost value from stack size value *= item->getStackSize(); // ...but not for coins if (item_type == item_type::COIN) { - value /= 500; + value /= 50; if (!value) value = 1; } From 2aeb86ba8f553c209a646842e2b9615f00685605 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Jul 2023 18:45:33 -0700 Subject: [PATCH 30/65] implement trade agreement buy prices --- library/modules/Items.cpp | 66 ++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index a2107aa1d..63c2a1992 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -53,6 +53,9 @@ using namespace std; #include "df/caravan_state.h" #include "df/caste_raw.h" #include "df/creature_raw.h" +#include "df/entity_buy_prices.h" +#include "df/entity_buy_requests.h" +#include "df/entity_sell_prices.h" #include "df/entity_raw.h" #include "df/general_ref.h" #include "df/general_ref_building_holderst.h" @@ -1446,23 +1449,23 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat } static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { - static const int32_t DEFAULT_MULTIPLIER = 256; + static const int32_t DEFAULT_WAR_MULTIPLIER = 256; if (!caravan) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; auto caravan_he = df::historical_entity::find(caravan->entity); if (!caravan_he) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; int32_t war_alignment = caravan_he->entity_raw->sphere_alignment[df::sphere_type::WAR]; - if (war_alignment == DEFAULT_MULTIPLIER) - return DEFAULT_MULTIPLIER; + if (war_alignment == DEFAULT_WAR_MULTIPLIER) + return DEFAULT_WAR_MULTIPLIER; switch (item->getType()) { case df::item_type::WEAPON: { auto weap_def = df::itemdef_weaponst::find(item->getSubtype()); auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); if (!weap_def || !caravan_cre_raw || caravan_cre_raw->adultsize < weap_def->minimum_size) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; break; } case df::item_type::ARMOR: @@ -1472,15 +1475,15 @@ static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { case df::item_type::PANTS: { if (item->getEffectiveArmorLevel() <= 0) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; auto caravan_cre_raw = df::creature_raw::find(caravan_he->race); auto maker_cre_raw = df::creature_raw::find(item->getMakerRace()); if (!caravan_cre_raw || !maker_cre_raw) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; if (caravan_cre_raw->adultsize < ((maker_cre_raw->adultsize * 6) / 7)) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; if (caravan_cre_raw->adultsize > ((maker_cre_raw->adultsize * 8) / 7)) - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; break; } case df::item_type::SHIELD: @@ -1489,14 +1492,42 @@ static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { case df::item_type::QUIVER: break; default: - return DEFAULT_MULTIPLIER; + return DEFAULT_WAR_MULTIPLIER; } return war_alignment; } -// returns 0 if the multiplier would be equal to 1.0 -static float get_trade_agreement_multiplier(df::item *item, df::caravan_state *caravan, bool caravan_buying) { - return 0; +static const int32_t DEFAULT_AGREEMENT_MULTIPLIER = 128; + +static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_prices *buy_prices) { + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + for (size_t idx = 0; idx < buy_prices->price.size(); ++idx) { + if (buy_prices->items->item_type[idx] != item_type) + continue; + if (buy_prices->items->item_subtype[idx] != -1 && buy_prices->items->item_subtype[idx] != item_subtype) + continue; + if (buy_prices->items->mat_types[idx] != -1 && buy_prices->items->mat_types[idx] != mat_type) + continue; + if (buy_prices->items->mat_indices[idx] != -1 && buy_prices->items->mat_indices[idx] != mat_subtype) + continue; + return buy_prices->price[idx]; + } + return DEFAULT_AGREEMENT_MULTIPLIER; +} + +static int32_t get_sell_request_multiplier(df::item *item, const df::entity_sell_prices *sell_prices) { + return DEFAULT_AGREEMENT_MULTIPLIER; +} + +static int32_t get_trade_agreement_multiplier(df::item *item, df::caravan_state *caravan, bool caravan_buying) { + if (!caravan) + return DEFAULT_AGREEMENT_MULTIPLIER; + return caravan_buying ? get_buy_request_multiplier(item, caravan->buy_prices) + : get_sell_request_multiplier(item, caravan->sell_prices); } int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) @@ -1567,10 +1598,9 @@ int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buy if (item->flags.bits.artifact_mood) value *= 10; - // modify buy/sell prices if a caravan is given - float trade_agreement_multiplier = get_trade_agreement_multiplier(item, caravan, caravan_buying); - if (trade_agreement_multiplier > 0) - value *= trade_agreement_multiplier; + // modify buy/sell prices + value *= get_trade_agreement_multiplier(item, caravan, caravan_buying); + value >>= 7; // Boost value from stack size value *= item->getStackSize(); From c45dcdd7b0aa1d458d06e5ee2cc0dfd2a0866c0f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 4 Jul 2023 01:53:45 -0700 Subject: [PATCH 31/65] implement sell_prices adjustments --- library/modules/Items.cpp | 393 +++++++++++++++++++++++++++++++++++++- 1 file changed, 390 insertions(+), 3 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 63c2a1992..537be7c3a 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -50,11 +50,14 @@ using namespace std; #include "df/building.h" #include "df/building_actual.h" #include "df/building_tradedepotst.h" +#include "df/builtin_mats.h" #include "df/caravan_state.h" #include "df/caste_raw.h" #include "df/creature_raw.h" +#include "df/dfhack_material_category.h" #include "df/entity_buy_prices.h" #include "df/entity_buy_requests.h" +#include "df/entity_sell_category.h" #include "df/entity_sell_prices.h" #include "df/entity_raw.h" #include "df/general_ref.h" @@ -1500,6 +1503,9 @@ static int32_t get_war_multiplier(df::item *item, df::caravan_state *caravan) { static const int32_t DEFAULT_AGREEMENT_MULTIPLIER = 128; static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_prices *buy_prices) { + if (!buy_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + int16_t item_type = item->getType(); int16_t item_subtype = item->getSubtype(); int16_t mat_type = item->getMaterial(); @@ -1519,15 +1525,396 @@ static int32_t get_buy_request_multiplier(df::item *item, const df::entity_buy_p return DEFAULT_AGREEMENT_MULTIPLIER; } -static int32_t get_sell_request_multiplier(df::item *item, const df::entity_sell_prices *sell_prices) { +template +static int get_price(const std::vector &res, int32_t val, const std::vector &pri) { + for (size_t idx = 0; idx < res.size(); ++idx) { + if (res[idx] == val && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +template +static int get_price(const std::vector &mat_res, int32_t mat, const std::vector &gloss_res, int32_t gloss, const std::vector &pri) { + for (size_t idx = 0; idx < mat_res.size(); ++idx) { + if (mat_res[idx] == mat && (gloss_res[idx] == -1 || gloss_res[idx] == gloss) && pri.size() > idx) + return pri[idx]; + } + return -1; +} + +static const uint16_t PLANT_BASE = 419; +static const uint16_t NUM_PLANT_TYPES = 200; + +static int32_t get_sell_request_multiplier(df::item *item, const df::historical_entity::T_resources &resources, const std::vector *prices) { + static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); + static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn); + static const df::dfhack_material_category leather_cat(df::dfhack_material_category::mask_leather); + + int16_t item_type = item->getType(); + int16_t item_subtype = item->getSubtype(); + int16_t mat_type = item->getMaterial(); + int32_t mat_subtype = item->getMaterialIndex(); + + bool inorganic = mat_type == df::builtin_mats::INORGANIC; + bool is_plant = (uint16_t)(mat_type - PLANT_BASE) < NUM_PLANT_TYPES; + + switch (item_type) { + case df::item_type::BAR: + if (inorganic) { + if (int32_t price = get_price(resources.metals, mat_subtype, prices[df::entity_sell_category::MetalBars]); price != -1) + return price; + } + break; + case df::item_type::SMALLGEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::SmallCutGems]); price != -1) + return price; + } + break; + case df::item_type::BLOCKS: + if (inorganic) { + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::StoneBlocks]); price != -1) + return price; + } + break; + case df::item_type::ROUGH: + if (int32_t price = get_price(resources.misc_mat.glass.mat_type, mat_type, resources.misc_mat.glass.mat_index, mat_subtype, + prices[df::entity_sell_category::Glass]); price != -1) + return price; + break; + case df::item_type::BOULDER: + if (int32_t price = get_price(resources.stones, mat_subtype, prices[df::entity_sell_category::Stone]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.clay.mat_type, mat_type, resources.misc_mat.clay.mat_index, mat_subtype, + prices[df::entity_sell_category::Clay]); price != -1) + return price; + break; + case df::item_type::WOOD: + if (int32_t price = get_price(resources.organic.wood.mat_type, mat_type, resources.organic.wood.mat_index, mat_subtype, + prices[df::entity_sell_category::Wood]); price != -1) + return price; + break; + case df::item_type::CHAIN: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid()) { + if (mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesSilk]); price != -1) + return price; + } + if (mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::RopesYarn]); price != -1) + return price; + } + } + } + break; + case df::item_type::FLASK: + if (int32_t price = get_price(resources.misc_mat.flasks.mat_type, mat_type, resources.misc_mat.flasks.mat_index, mat_subtype, + prices[df::entity_sell_category::FlasksWaterskins]); price != -1) + return price; + break; + case df::item_type::GOBLET: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::CupsMugsGoblets]); price != -1) + return price; + break; + case df::item_type::INSTRUMENT: + if (int32_t price = get_price(resources.instrument_type, mat_subtype, prices[df::entity_sell_category::Instruments]); price != -1) + return price; + break; + case df::item_type::TOY: + if (int32_t price = get_price(resources.toy_type, mat_subtype, prices[df::entity_sell_category::Toys]); price != -1) + return price; + break; + case df::item_type::CAGE: + if (int32_t price = get_price(resources.misc_mat.cages.mat_type, mat_type, resources.misc_mat.cages.mat_index, mat_subtype, + prices[df::entity_sell_category::Cages]); price != -1) + return price; + break; + case df::item_type::BARREL: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Barrels]); price != -1) + return price; + break; + case df::item_type::BUCKET: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Buckets]); price != -1) + return price; + break; + case df::item_type::WEAPON: + if (int32_t price = get_price(resources.weapon_type, mat_subtype, prices[df::entity_sell_category::Weapons]); price != -1) + return price; + if (int32_t price = get_price(resources.digger_type, mat_subtype, prices[df::entity_sell_category::DiggingImplements]); price != -1) + return price; + if (int32_t price = get_price(resources.training_weapon_type, mat_subtype, prices[df::entity_sell_category::TrainingWeapons]); price != -1) + return price; + break; + case df::item_type::ARMOR: + if (int32_t price = get_price(resources.armor_type, mat_subtype, prices[df::entity_sell_category::Bodywear]); price != -1) + return price; + break; + case df::item_type::SHOES: + if (int32_t price = get_price(resources.shoes_type, mat_subtype, prices[df::entity_sell_category::Footwear]); price != -1) + return price; + break; + case df::item_type::SHIELD: + if (int32_t price = get_price(resources.shield_type, mat_subtype, prices[df::entity_sell_category::Shields]); price != -1) + return price; + break; + case df::item_type::HELM: + if (int32_t price = get_price(resources.helm_type, mat_subtype, prices[df::entity_sell_category::Headwear]); price != -1) + return price; + break; + case df::item_type::GLOVES: + if (int32_t price = get_price(resources.gloves_type, mat_subtype, prices[df::entity_sell_category::Handwear]); price != -1) + return price; + break; + case df::item_type::BAG: + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(leather_cat)) { + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsLeather]); price != -1) + return price; + } + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsPlant]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::BagsYarn]); price != -1) + return price; + } + } + break; + case df::item_type::FIGURINE: + case df::item_type::AMULET: + case df::item_type::SCEPTER: + case df::item_type::CROWN: + case df::item_type::RING: + case df::item_type::EARRING: + case df::item_type::BRACELET: + case df::item_type::TOTEM: + case df::item_type::BOOK: + if (int32_t price = get_price(resources.misc_mat.crafts.mat_type, mat_type, resources.misc_mat.crafts.mat_index, mat_subtype, + prices[df::entity_sell_category::Crafts]); price != -1) + return price; + break; + case df::item_type::AMMO: + if (int32_t price = get_price(resources.ammo_type, mat_subtype, prices[df::entity_sell_category::Ammo]); price != -1) + return price; + break; + case df::item_type::GEM: + if (inorganic) { + if (int32_t price = get_price(resources.gems, mat_subtype, prices[df::entity_sell_category::LargeCutGems]); price != -1) + return price; + } + break; + case df::item_type::ANVIL: + if (int32_t price = get_price(resources.metal.anvil.mat_type, mat_type, resources.metal.anvil.mat_index, mat_subtype, + prices[df::entity_sell_category::Anvils]); price != -1) + return price; + break; + case df::item_type::MEAT: + if (int32_t price = get_price(resources.misc_mat.meat.mat_type, mat_type, resources.misc_mat.meat.mat_index, mat_subtype, + prices[df::entity_sell_category::Meat]); price != -1) + return price; + break; + case df::item_type::FISH: + case df::item_type::FISH_RAW: + if (int32_t price = get_price(resources.fish_races, mat_type, resources.fish_castes, mat_subtype, + prices[df::entity_sell_category::Fish]); price != -1) + return price; + break; + case df::item_type::VERMIN: + case df::item_type::PET: + if (int32_t price = get_price(resources.animals.pet_races, mat_type, resources.animals.pet_castes, mat_subtype, + prices[df::entity_sell_category::Pets]); price != -1) + return price; + break; + case df::item_type::SEEDS: + if (int32_t price = get_price(resources.seeds.mat_type, mat_type, resources.seeds.mat_index, mat_subtype, + prices[df::entity_sell_category::Seeds]); price != -1) + return price; + break; + case df::item_type::PLANT: + if (int32_t price = get_price(resources.plants.mat_type, mat_type, resources.plants.mat_index, mat_subtype, + prices[df::entity_sell_category::Plants]); price != -1) + return price; + break; + case df::item_type::SKIN_TANNED: + if (int32_t price = get_price(resources.organic.leather.mat_type, mat_type, resources.organic.leather.mat_index, mat_subtype, + prices[df::entity_sell_category::Leather]); price != -1) + return price; + break; + case df::item_type::PLANT_GROWTH: + if (is_plant) { + if (int32_t price = get_price(resources.tree_fruit_plants, mat_type, resources.tree_fruit_growths, mat_subtype, + prices[df::entity_sell_category::FruitsNuts]); price != -1) + return price; + if (int32_t price = get_price(resources.shrub_fruit_plants, mat_type, resources.shrub_fruit_growths, mat_subtype, + prices[df::entity_sell_category::GardenVegetables]); price != -1) + return price; + } + break; + case df::item_type::THREAD: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ThreadYarn]); price != -1) + return price; + } + } + break; + case df::item_type::CLOTH: + if (is_plant) { + if (int32_t price = get_price(resources.organic.fiber.mat_type, mat_type, resources.organic.fiber.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothPlant]); price != -1) + return price; + } + { + MaterialInfo mi; + mi.decode(mat_type, mat_subtype); + if (mi.isValid() && mi.matches(silk_cat)) { + if (int32_t price = get_price(resources.organic.silk.mat_type, mat_type, resources.organic.silk.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothSilk]); price != -1) + return price; + } + if (mi.isValid() && mi.matches(yarn_cat)) { + if (int32_t price = get_price(resources.organic.wool.mat_type, mat_type, resources.organic.wool.mat_index, mat_subtype, + prices[df::entity_sell_category::ClothYarn]); price != -1) + return price; + } + } + break; + case df::item_type::PANTS: + if (int32_t price = get_price(resources.pants_type, mat_subtype, prices[df::entity_sell_category::Legwear]); price != -1) + return price; + break; + case df::item_type::BACKPACK: + if (int32_t price = get_price(resources.misc_mat.backpacks.mat_type, mat_type, resources.misc_mat.backpacks.mat_index, mat_subtype, + prices[df::entity_sell_category::Backpacks]); price != -1) + return price; + break; + case df::item_type::QUIVER: + if (int32_t price = get_price(resources.misc_mat.quivers.mat_type, mat_type, resources.misc_mat.quivers.mat_index, mat_subtype, + prices[df::entity_sell_category::Quivers]); price != -1) + return price; + break; + case df::item_type::TRAPCOMP: + if (int32_t price = get_price(resources.trapcomp_type, mat_subtype, prices[df::entity_sell_category::TrapComponents]); price != -1) + return price; + break; + case df::item_type::DRINK: + if (int32_t price = get_price(resources.misc_mat.booze.mat_type, mat_type, resources.misc_mat.booze.mat_index, mat_subtype, + prices[df::entity_sell_category::Drinks]); price != -1) + return price; + break; + case df::item_type::POWDER_MISC: + if (int32_t price = get_price(resources.misc_mat.powders.mat_type, mat_type, resources.misc_mat.powders.mat_index, mat_subtype, + prices[df::entity_sell_category::Powders]); price != -1) + return price; + if (int32_t price = get_price(resources.misc_mat.sand.mat_type, mat_type, resources.misc_mat.sand.mat_index, mat_subtype, + prices[df::entity_sell_category::Sand]); price != -1) + return price; + break; + case df::item_type::CHEESE: + if (int32_t price = get_price(resources.misc_mat.cheese.mat_type, mat_type, resources.misc_mat.cheese.mat_index, mat_subtype, + prices[df::entity_sell_category::Cheese]); price != -1) + return price; + break; + case df::item_type::LIQUID_MISC: + if (int32_t price = get_price(resources.misc_mat.extracts.mat_type, mat_type, resources.misc_mat.extracts.mat_index, mat_subtype, + prices[df::entity_sell_category::Extracts]); price != -1) + return price; + break; + case df::item_type::SPLINT: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Splints]); price != -1) + return price; + break; + case df::item_type::CRUTCH: + if (int32_t price = get_price(resources.misc_mat.barrels.mat_type, mat_type, resources.misc_mat.barrels.mat_index, mat_subtype, + prices[df::entity_sell_category::Crutches]); price != -1) + return price; + break; + case df::item_type::TOOL: + if (int32_t price = get_price(resources.tool_type, mat_subtype, prices[df::entity_sell_category::Tools]); price != -1) + return price; + break; + case df::item_type::EGG: + if (int32_t price = get_price(resources.egg_races, mat_type, resources.egg_castes, mat_subtype, + prices[df::entity_sell_category::Eggs]); price != -1) + return price; + break; + case df::item_type::SHEET: + if (int32_t price = get_price(resources.organic.parchment.mat_type, mat_type, resources.organic.parchment.mat_index, mat_subtype, + prices[df::entity_sell_category::Parchment]); price != -1) + return price; + break; + default: + break; + } + + for (size_t idx = 0; idx < resources.wood_products.item_type.size(); ++idx) { + if (resources.wood_products.item_type[idx] == item_type && + (resources.wood_products.item_subtype[idx] == -1 || resources.wood_products.item_subtype[idx] == item_subtype) && + resources.wood_products.material.mat_type[idx] == mat_type && + (resources.wood_products.material.mat_index[idx] == -1 || resources.wood_products.material.mat_index[idx] == mat_subtype) && + prices[df::entity_sell_category::Miscellaneous].size() > idx) + return prices[df::entity_sell_category::Miscellaneous][idx]; + } + return DEFAULT_AGREEMENT_MULTIPLIER; } -static int32_t get_trade_agreement_multiplier(df::item *item, df::caravan_state *caravan, bool caravan_buying) { +static int32_t get_sell_request_multiplier(df::item *item, const df::caravan_state *caravan) { + const df::entity_sell_prices *sell_prices = caravan->sell_prices; + if (!sell_prices) + return DEFAULT_AGREEMENT_MULTIPLIER; + + auto caravan_he = df::historical_entity::find(caravan->entity); + if (!caravan_he) + return DEFAULT_AGREEMENT_MULTIPLIER; + + return get_sell_request_multiplier(item, caravan_he->resources, &sell_prices->price[0]); +} + +static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_state *caravan, bool caravan_buying) { if (!caravan) return DEFAULT_AGREEMENT_MULTIPLIER; return caravan_buying ? get_buy_request_multiplier(item, caravan->buy_prices) - : get_sell_request_multiplier(item, caravan->sell_prices); + : get_sell_request_multiplier(item, caravan); } int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) From b938891e113238f2b9f7e365d62b998115271d55 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 4 Jul 2023 03:58:45 -0700 Subject: [PATCH 32/65] add dfhack.items.isRequestedTradeGood --- docs/changelog.txt | 3 ++- docs/dev/Lua API.rst | 4 ++++ library/LuaApi.cpp | 1 + library/include/modules/Items.h | 2 ++ library/modules/Items.cpp | 13 +++++++++++++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a8a100df..e6a9dd36d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -50,8 +50,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Price calculations fixed for many item types ## Lua -- ``dfhack.items.markForTrade``: new API for marking items for trade - ``dfhack.units.getUnitByNobleRole``, ``dfhack.units.getUnitsByNobleRole``: unit lookup API by role +- ``dfhack.items.markForTrade``: mark items for trade +- ``dfhack.items.isRequestedTradeGood``: discover whether an item is named in a trade agreement with an active caravan - ``dfhack.items.getValue``: gained optional ``caravan`` and ``caravan_buying`` parameters for prices that take trader races and agreements into account ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index e81a34de3..8553d4e1a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1778,6 +1778,10 @@ Items module for the item and ``false`` to get the price that the caravan will sell the item for. +* ``dfhack.items.isRequestedTradeGood(item)`` + + Returns whether any active caravan will pay extra for the given item. + * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)`` Creates an item, similar to the `createitem` plugin. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 7fb14baeb..7afb3b9ea 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2020,6 +2020,7 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, getSubtypeDef), WRAPM(Items, getItemBaseValue), WRAPM(Items, getValue), + WRAPM(Items, isRequestedTradeGood), WRAPM(Items, createItem), WRAPM(Items, checkMandates), WRAPM(Items, canTrade), diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 6cce5d2f1..c473654e3 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -203,6 +203,8 @@ DFHACK_EXPORT bool canTrade(df::item *item); DFHACK_EXPORT bool canTradeWithContents(df::item *item); /// marks the given item for trade at the given depot DFHACK_EXPORT bool markForTrade(df::item *item, df::building_tradedepotst *depot); +/// Returns true if an active caravan will pay extra for the given item +DFHACK_EXPORT bool isRequestedTradeGood(df::item *item); /// Checks whether the item is an assigned hauling vehicle DFHACK_EXPORT bool isRouteVehicle(df::item *item); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 537be7c3a..06741894d 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1917,6 +1917,19 @@ static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_ : get_sell_request_multiplier(item, caravan); } +bool Items::isRequestedTradeGood(df::item *item) { + for (auto caravan : df::global::plotinfo->caravans) { + auto trade_state = caravan->trade_state; + if (caravan->time_remaining <= 0 || + (trade_state != df::caravan_state::T_trade_state::Approaching && + trade_state != df::caravan_state::T_trade_state::AtDepot)) + continue; + if (get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER) + return true; + } + return false; +} + int Items::getValue(df::item *item, df::caravan_state *caravan, bool caravan_buying) { CHECK_NULL_POINTER(item); From 7d3c8bd0406fd6d89fc01300510b5b479528982e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 5 Jul 2023 12:16:36 -0700 Subject: [PATCH 33/65] add notes to the API section of the changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index e6a9dd36d..bbd682c2d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role +- ``Items::markForTrade()``, ``Items::isRequestedTradeGood()``, ``Items::getValue``: see Lua notes below ## Internals - Price calculations fixed for many item types From 6a8522ab5e762ae5df6f5e7e26e3ef2babd32a94 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 6 Jul 2023 03:21:19 -0700 Subject: [PATCH 34/65] generalize dfhack.items.isRequestedTradeGood --- docs/dev/Lua API.rst | 5 +++-- library/include/modules/Items.h | 2 +- library/modules/Items.cpp | 14 +++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8553d4e1a..11bac3f61 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1778,9 +1778,10 @@ Items module for the item and ``false`` to get the price that the caravan will sell the item for. -* ``dfhack.items.isRequestedTradeGood(item)`` +* ``dfhack.items.isRequestedTradeGood(item[, caravan_state])`` - Returns whether any active caravan will pay extra for the given item. + Returns whether a caravan will pay extra for the given item. If caravan_state + is not given, checks all active caravans. * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)`` diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index c473654e3..7541660f4 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -204,7 +204,7 @@ DFHACK_EXPORT bool canTradeWithContents(df::item *item); /// marks the given item for trade at the given depot DFHACK_EXPORT bool markForTrade(df::item *item, df::building_tradedepotst *depot); /// Returns true if an active caravan will pay extra for the given item -DFHACK_EXPORT bool isRequestedTradeGood(df::item *item); +DFHACK_EXPORT bool isRequestedTradeGood(df::item *item, df::caravan_state *caravan = NULL); /// Checks whether the item is an assigned hauling vehicle DFHACK_EXPORT bool isRouteVehicle(df::item *item); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 06741894d..3fc08813c 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1917,7 +1917,19 @@ static int32_t get_trade_agreement_multiplier(df::item *item, const df::caravan_ : get_sell_request_multiplier(item, caravan); } -bool Items::isRequestedTradeGood(df::item *item) { +static bool is_requested_trade_good(df::item *item, df::caravan_state *caravan) { + auto trade_state = caravan->trade_state; + if (caravan->time_remaining <= 0 || + (trade_state != df::caravan_state::T_trade_state::Approaching && + trade_state != df::caravan_state::T_trade_state::AtDepot)) + return false; + return get_buy_request_multiplier(item, caravan->buy_prices) > DEFAULT_AGREEMENT_MULTIPLIER; +} + +bool Items::isRequestedTradeGood(df::item *item, df::caravan_state *caravan) { + if (caravan) + return is_requested_trade_good(item, caravan); + for (auto caravan : df::global::plotinfo->caravans) { auto trade_state = caravan->trade_state; if (caravan->time_remaining <= 0 || From 0b1ca6913c84a4a8a676a2070d623bb799c8b3f2 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 6 Jul 2023 10:37:31 +0000 Subject: [PATCH 35/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 64aff9c2d..36b1dd308 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 64aff9c2deb0dd49ee755257c43d2a9fc62f92bb +Subproject commit 36b1dd308920fc404cf2a67d5ad697b375ec984c From b657e8d8016e91ffeb002b4297af85c1ef84d984 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 7 Jul 2023 07:15:19 +0000 Subject: [PATCH 36/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 36b1dd308..e7a9aab2c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 36b1dd308920fc404cf2a67d5ad697b375ec984c +Subproject commit e7a9aab2cd0065a7b90b03c27919b692fba44a0b From 0aa2146e11b2b6af03b50d00e380e691bdd4e405 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Jul 2023 13:06:42 -0700 Subject: [PATCH 37/65] bump version number to 50.09-r2rc1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dde3a05b..f3d1b4e1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,8 @@ project(dfhack) # set up versioning. set(DF_VERSION "50.09") -set(DFHACK_RELEASE "r1") -set(DFHACK_PRERELEASE FALSE) +set(DFHACK_RELEASE "r2rc1") +set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_ABI_VERSION 1) From 36c6c5ee939bcf68d092fc77d83a7f59e926b50b Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 8 Apr 2023 12:11:15 -0500 Subject: [PATCH 38/65] 3dveins - tradeoffs for v50 allow dropping orphan veins; this is necessitated by what appears to be a bug in the gem spire generation code, which appears to occasionally spew gems in places that are quite distant from the gem spires themselves --- plugins/3dveins.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index eaf741caf..f79949370 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -914,6 +914,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z) for (int y = 0; y < 16; y++) { df::coord2d tile(x,y); + GeoLayer *layer = mapLayer(b, tile); if (!layer) continue; @@ -1363,7 +1364,7 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from) ENUM_KEY_STR(inclusion_type, key.second).c_str() ); - return false; + return true; } for (auto it = best.begin(); size > 0 && it != best.end(); ++it) From 9a5867219004b921ded7129fc8590cabf115b02f Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 4 Jul 2023 10:07:50 -0500 Subject: [PATCH 39/65] reenable 3dveins --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4029b8e2e..63e5274ae 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -73,7 +73,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) # If you are adding a plugin that you do not intend to commit to the DFHack repo, # see instructions for adding "external" plugins at the end of this file. -#dfhack_plugin(3dveins 3dveins.cpp) +dfhack_plugin(3dveins 3dveins.cpp) dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) From 02a4b7f63f4a6ea9d59b5693855e66e57e2eb2f6 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 4 Jul 2023 11:01:26 -0500 Subject: [PATCH 40/65] 3dveins: use debugfilter --- plugins/3dveins.cpp | 69 ++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index f79949370..659f16ecc 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -7,15 +7,16 @@ #include "Core.h" #include "Console.h" +#include "DataDefs.h" +#include "Debug.h" #include "Export.h" +#include "MiscUtils.h" #include "PluginManager.h" + #include "modules/MapCache.h" #include "modules/Random.h" #include "modules/World.h" -#include "MiscUtils.h" - -#include "DataDefs.h" #include "df/world.h" #include "df/world_data.h" #include "df/world_region_details.h" @@ -47,6 +48,10 @@ DFHACK_PLUGIN("3dveins"); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(gametype); +namespace DFHack { + DBG_DECLARE(_3dveins, process, DebugCategory::LINFO); +} + command_result cmd_3dveins(color_ostream &out, std::vector & parameters); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -431,11 +436,13 @@ struct GeoLayer void print_mineral_stats(color_ostream &out) { for (auto it = mineral_count.begin(); it != mineral_count.end(); ++it) - out << " " << MaterialInfo(0,it->first.first).getToken() - << " " << ENUM_KEY_STR(inclusion_type,it->first.second) - << ": \t\t" << it->second << " (" << (float(it->second)/unmined_tiles) << ")" << std::endl; + INFO(process, out).print("3dveins: %s %s: %d (%f)\n", + MaterialInfo(0, it->first.first).getToken(), + ENUM_KEY_STR(inclusion_type, it->first.second), + it->second, + (float(it->second) / unmined_tiles)); - out.print(" Total tiles: %d (%d unmined)\n", tiles, unmined_tiles); + INFO(process, out).print ("3dveins: Total tiles: %d (%d unmined)\n", tiles, unmined_tiles); } bool form_veins(color_ostream &out); @@ -467,12 +474,12 @@ struct GeoBiome void print_mineral_stats(color_ostream &out) { - out.print("Geological biome %d:\n", info.geo_index); + INFO(process,out).print("3dveins: Geological biome %d:\n", info.geo_index); for (size_t i = 0; i < layers.size(); i++) if (layers[i]) { - out << " Layer " << i << std::endl; + INFO(process, out).print("3dveins: Layer %d\n", i); layers[i]->print_mineral_stats(out); } } @@ -586,7 +593,7 @@ bool VeinGenerator::init_biomes() if (info.geo_index < 0 || !info.geobiome) { - out.printerr("Biome %zd is not defined.\n", i); + ERR(process, out).print("Biome %zd is not defined.\n", i); return false; } @@ -797,8 +804,7 @@ bool VeinGenerator::scan_layer_depth(Block *b, df::coord2d column, int z) { if (z != min_level[idx]-1 && min_level[idx] <= top_solid) { - out.printerr( - "Discontinuous layer %d at (%d,%d,%d).\n", + ERR(process, out).print("Discontinuous layer %d at (%d,%d,%d).\n", layer->index, x+column.x*16, y+column.y*16, z ); return false; @@ -848,7 +854,7 @@ bool VeinGenerator::adjust_layer_depth(df::coord2d column) if (max_level[i+1] != min_level[i]-1) { - out.printerr( + ERR(process, out).print( "Gap or overlap with next layer %d at (%d,%d,%d-%d).\n", i+1, x+column.x*16, y+column.y*16, max_level[i+1], min_level[i] ); @@ -891,7 +897,7 @@ bool VeinGenerator::adjust_layer_depth(df::coord2d column) } } - out.printerr( + ERR(process, out).print( "Layer height change in layer %d at (%d,%d,%d): %d instead of %d.\n", i, x+column.x*16, y+column.y*16, max_level[i], size, biome->layers[i]->thickness @@ -933,7 +939,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z) if (unsigned(key.first) >= materials.size() || unsigned(key.second) >= NUM_INCLUSIONS) { - out.printerr("Invalid vein code: %d %d - aborting.\n",key.first,key.second); + ERR(process, out).print("Invalid vein code: %d %d - aborting.\n",key.first,key.second); return false; } @@ -942,7 +948,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z) if (status == -1) { // Report first occurence of unreasonable vein spec - out.printerr( + WARN(process, out).print( "Unexpected vein %s %s - ", MaterialInfo(0,key.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str() @@ -950,9 +956,9 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z) status = materials[key.first].default_type; if (status < 0) - out.printerr("will be left in place.\n"); + WARN(process, out).print("will be left in place.\n"); else - out.printerr( + WARN(process, out).print( "correcting to %s.\n", ENUM_KEY_STR(inclusion_type, df::inclusion_type(status)).c_str() ); @@ -1091,7 +1097,7 @@ void VeinGenerator::write_block_tiles(Block *b, df::coord2d column, int z) if (!ok) { - out.printerr( + ERR(process, out).print( "Couldn't write %d vein at (%d,%d,%d)\n", mat, x+column.x*16, y+column.y*16, z ); @@ -1282,7 +1288,7 @@ bool GeoLayer::form_veins(color_ostream &out) if (parent_id >= (int)refs.size()) { - out.printerr("Forward vein reference in biome %d.\n", biome->info.geo_index); + ERR(process, out).print("Forward vein reference in biome %d.\n", biome->info.geo_index); return false; } @@ -1302,7 +1308,7 @@ bool GeoLayer::form_veins(color_ostream &out) if (vptr->parent) ctx = "only be in "+MaterialInfo(0,vptr->parent_mat()).getToken(); - out.printerr( + WARN(process, out).print( "Duplicate vein %s %s in biome %d layer %d - will %s.\n", MaterialInfo(0,key.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str(), @@ -1358,7 +1364,7 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from) if (best.empty()) { - out.printerr( + ERR(process,out).print( "Could not place orphaned vein %s %s anywhere.\n", MaterialInfo(0,key.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str() @@ -1392,7 +1398,7 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from) if (size > 0) { - out.printerr( + WARN(process, out).print( "Could not place all of orphaned vein %s %s: %d left.\n", MaterialInfo(0,key.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str(), @@ -1542,7 +1548,7 @@ bool VeinGenerator::place_veins(bool verbose) if (!isStoneInorganic(key.first)) { - out.printerr( + ERR(process, out).print( "Invalid vein material: %s\n", MaterialInfo(0, key.first).getToken().c_str() ); @@ -1552,7 +1558,7 @@ bool VeinGenerator::place_veins(bool verbose) if (!is_valid_enum_item(key.second)) { - out.printerr("Invalid vein type: %d\n", key.second); + ERR(process, out).print("Invalid vein type: %d\n", key.second); return false; } @@ -1565,13 +1571,13 @@ bool VeinGenerator::place_veins(bool verbose) sort(queue.begin(), queue.end(), vein_cmp); // Place tiles - out.print("Processing... (%zu)", queue.size()); + TRACE(process,out).print("Processing... (%zu)", queue.size()); for (size_t j = 0; j < queue.size(); j++) { if (queue[j]->parent && !queue[j]->parent->placed) { - out.printerr( + ERR(process, out).print( "\nParent vein not placed for %s %s.\n", MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str() @@ -1583,9 +1589,9 @@ bool VeinGenerator::place_veins(bool verbose) if (verbose) { if (j > 0) - out.print("done."); + TRACE(process, out).print("done."); - out.print( + TRACE(process, out).print( "\nVein layer %zu of %zu: %s %s (%.2f%%)... ", j+1, queue.size(), MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), @@ -1595,14 +1601,13 @@ bool VeinGenerator::place_veins(bool verbose) } else { - out.print("\rVein layer %zu of %zu... ", j+1, queue.size()); - out.flush(); + TRACE(process, out).print("\rVein layer %zu of %zu... ", j+1, queue.size()); } queue[j]->place_tiles(); } - out.print("done.\n"); + TRACE(process, out).print("done.\n"); return true; } From d346bbfe6f363f63802d89cccc3d90ec09c93446 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 4 Jul 2023 11:18:39 -0500 Subject: [PATCH 41/65] 3dveins: satisfy gcc's overly aggressive linter --- plugins/3dveins.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 659f16ecc..71115d38a 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -436,8 +436,8 @@ struct GeoLayer void print_mineral_stats(color_ostream &out) { for (auto it = mineral_count.begin(); it != mineral_count.end(); ++it) - INFO(process, out).print("3dveins: %s %s: %d (%f)\n", - MaterialInfo(0, it->first.first).getToken(), + INFO(process, out).print("3dveins: %s %s: %ld (%f)\n", + MaterialInfo(0, it->first.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, it->first.second), it->second, (float(it->second) / unmined_tiles)); @@ -1589,7 +1589,9 @@ bool VeinGenerator::place_veins(bool verbose) if (verbose) { if (j > 0) + { TRACE(process, out).print("done."); + } TRACE(process, out).print( "\nVein layer %zu of %zu: %s %s (%.2f%%)... ", From 4b669ec83a85125f60fafbedeee3734bdd79c222 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 4 Jul 2023 11:22:34 -0500 Subject: [PATCH 42/65] uncross eyes and try again --- plugins/3dveins.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 71115d38a..8fd7ac900 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -436,9 +436,9 @@ struct GeoLayer void print_mineral_stats(color_ostream &out) { for (auto it = mineral_count.begin(); it != mineral_count.end(); ++it) - INFO(process, out).print("3dveins: %s %s: %ld (%f)\n", + INFO(process, out).print("3dveins: %s %s: %d (%f)\n", MaterialInfo(0, it->first.first).getToken().c_str(), - ENUM_KEY_STR(inclusion_type, it->first.second), + ENUM_KEY_STR(inclusion_type, it->first.second).c_str(), it->second, (float(it->second) / unmined_tiles)); @@ -479,7 +479,7 @@ struct GeoBiome for (size_t i = 0; i < layers.size(); i++) if (layers[i]) { - INFO(process, out).print("3dveins: Layer %d\n", i); + INFO(process, out).print("3dveins: Layer %ld\n", i); layers[i]->print_mineral_stats(out); } } From 6997b6be21b1697e1192e336287ab2dd668a9bbb Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 7 Jul 2023 16:00:39 -0500 Subject: [PATCH 43/65] add changelog for 3dveins --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index bbd682c2d..891a9a93e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- ``3dveins``: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels ## Fixes - Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing From 734de0dcd126eed3a48884926d3c199089efab07 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Jul 2023 14:49:10 -0700 Subject: [PATCH 44/65] make EditField ignore Ctrl-A until we get Home key support --- library/lua/gui/widgets.lua | 7 ++++--- scripts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d6dcafd63..b034ed1e8 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -756,9 +756,10 @@ function EditField:onInput(keys) find('.*[%w_%-][^%w_%-]') self:setCursor(prev_word_end or 1) return true - elseif keys.CUSTOM_CTRL_A then -- home - self:setCursor(1) - return true + -- commented out until we get HOME key support from DF + -- elseif keys.CUSTOM_CTRL_A then -- home + -- self:setCursor(1) + -- return true elseif keys.KEYBOARD_CURSOR_RIGHT then self:setCursor(self.cursor + 1) return true diff --git a/scripts b/scripts index e7a9aab2c..33ecf539b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e7a9aab2cd0065a7b90b03c27919b692fba44a0b +Subproject commit 33ecf539b0dc993818d6830ce26a234a321b9ae3 From 0ac05197c616ff011f26cc698e12c004f8db2f03 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 7 Jul 2023 17:21:25 -0500 Subject: [PATCH 45/65] fix an ambiguity in def'n of operator== this resolve an error that arises when compiling with msvc 1936 which was previously ignored due to a bug in the compiler --- library/include/Core.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/include/Core.h b/library/include/Core.h index a41aff927..dc2408ae9 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -106,10 +106,14 @@ namespace DFHack StateChangeScript(state_change_event event, std::string path, bool save_specific = false) :event(event), path(path), save_specific(save_specific) { } - bool operator==(const StateChangeScript& other) + bool const operator==(const StateChangeScript& other) { return event == other.event && path == other.path && save_specific == other.save_specific; } + bool const operator!=(const StateChangeScript& other) + { + return !(operator==(other)); + } }; // Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF. From 44c41d2df946580b9aa96445c824591724473cbb Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 7 Jul 2023 22:29:43 +0000 Subject: [PATCH 46/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 33ecf539b..37701795f 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 33ecf539b0dc993818d6830ce26a234a321b9ae3 +Subproject commit 37701795f5705f5498df9543afdfb1255595976e From fb9e72e487f13bcf854cbe1739848a72cc5eaf69 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 7 Jul 2023 17:39:50 -0500 Subject: [PATCH 47/65] =?UTF-8?q?3dveins:=20ERR=20=E2=86=92=20WARN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/3dveins.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/3dveins.cpp b/plugins/3dveins.cpp index 8fd7ac900..6b95c2e87 100644 --- a/plugins/3dveins.cpp +++ b/plugins/3dveins.cpp @@ -593,7 +593,7 @@ bool VeinGenerator::init_biomes() if (info.geo_index < 0 || !info.geobiome) { - ERR(process, out).print("Biome %zd is not defined.\n", i); + WARN(process, out).print("Biome %zd is not defined.\n", i); return false; } @@ -804,7 +804,7 @@ bool VeinGenerator::scan_layer_depth(Block *b, df::coord2d column, int z) { if (z != min_level[idx]-1 && min_level[idx] <= top_solid) { - ERR(process, out).print("Discontinuous layer %d at (%d,%d,%d).\n", + WARN(process, out).print("Discontinuous layer %d at (%d,%d,%d).\n", layer->index, x+column.x*16, y+column.y*16, z ); return false; @@ -854,7 +854,7 @@ bool VeinGenerator::adjust_layer_depth(df::coord2d column) if (max_level[i+1] != min_level[i]-1) { - ERR(process, out).print( + WARN(process, out).print( "Gap or overlap with next layer %d at (%d,%d,%d-%d).\n", i+1, x+column.x*16, y+column.y*16, max_level[i+1], min_level[i] ); @@ -897,7 +897,7 @@ bool VeinGenerator::adjust_layer_depth(df::coord2d column) } } - ERR(process, out).print( + WARN(process, out).print( "Layer height change in layer %d at (%d,%d,%d): %d instead of %d.\n", i, x+column.x*16, y+column.y*16, max_level[i], size, biome->layers[i]->thickness @@ -939,7 +939,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z) if (unsigned(key.first) >= materials.size() || unsigned(key.second) >= NUM_INCLUSIONS) { - ERR(process, out).print("Invalid vein code: %d %d - aborting.\n",key.first,key.second); + WARN(process, out).print("Invalid vein code: %d %d - aborting.\n",key.first,key.second); return false; } @@ -1097,7 +1097,7 @@ void VeinGenerator::write_block_tiles(Block *b, df::coord2d column, int z) if (!ok) { - ERR(process, out).print( + WARN(process, out).print( "Couldn't write %d vein at (%d,%d,%d)\n", mat, x+column.x*16, y+column.y*16, z ); @@ -1288,7 +1288,7 @@ bool GeoLayer::form_veins(color_ostream &out) if (parent_id >= (int)refs.size()) { - ERR(process, out).print("Forward vein reference in biome %d.\n", biome->info.geo_index); + WARN(process, out).print("Forward vein reference in biome %d.\n", biome->info.geo_index); return false; } @@ -1364,7 +1364,7 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from) if (best.empty()) { - ERR(process,out).print( + WARN(process,out).print( "Could not place orphaned vein %s %s anywhere.\n", MaterialInfo(0,key.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str() @@ -1548,7 +1548,7 @@ bool VeinGenerator::place_veins(bool verbose) if (!isStoneInorganic(key.first)) { - ERR(process, out).print( + WARN(process, out).print( "Invalid vein material: %s\n", MaterialInfo(0, key.first).getToken().c_str() ); @@ -1558,7 +1558,7 @@ bool VeinGenerator::place_veins(bool verbose) if (!is_valid_enum_item(key.second)) { - ERR(process, out).print("Invalid vein type: %d\n", key.second); + WARN(process, out).print("Invalid vein type: %d\n", key.second); return false; } @@ -1577,7 +1577,7 @@ bool VeinGenerator::place_veins(bool verbose) { if (queue[j]->parent && !queue[j]->parent->placed) { - ERR(process, out).print( + WARN(process, out).print( "\nParent vein not placed for %s %s.\n", MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str() From 6f4816102111a7f8615f170b1cedeaee32dd5adc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Jul 2023 15:57:24 -0700 Subject: [PATCH 48/65] fix quoting in changelog --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 891a9a93e..fef273c64 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,7 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- ``3dveins``: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels +- `3dveins`: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels ## Fixes - Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing From ad49c6735f6bc52c797750dc1c2de7f02e434867 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 8 Jul 2023 00:42:40 +0000 Subject: [PATCH 49/65] Auto-update submodules plugins/stonesense: master --- plugins/stonesense | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stonesense b/plugins/stonesense index d7fa20079..367d602a2 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit d7fa20079e89cc6516a0f5406a5ad112436066bb +Subproject commit 367d602a2949ab7121e2d9233c29a7fb1b9e6bec From 1fba8b16f5f2cdf89b80fe8d89acffc98bd5e48a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 7 Jul 2023 19:26:45 -0500 Subject: [PATCH 50/65] replace `dts:make_unique` with `std::make_unique` 2014 here we come! --- library/Core.cpp | 6 +++--- library/include/MiscUtils.h | 22 ---------------------- library/modules/Graphic.cpp | 2 +- library/modules/Materials.cpp | 2 +- plugins/autofarm.cpp | 2 +- plugins/tailor.cpp | 2 +- 6 files changed, 7 insertions(+), 29 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 8d632e107..e4713c07f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1404,7 +1404,7 @@ Core::~Core() } Core::Core() : - d(dts::make_unique()), + d(std::make_unique()), script_path_mutex{}, HotkeyMutex{}, HotkeyCond{}, @@ -1501,7 +1501,7 @@ bool Core::InitMainThread() { #else const char * path = "hack\\symbols.xml"; #endif - auto local_vif = dts::make_unique(); + auto local_vif = std::make_unique(); std::cerr << "Identifying DF version.\n"; try { @@ -1517,7 +1517,7 @@ bool Core::InitMainThread() { return false; } vif = std::move(local_vif); - auto local_p = dts::make_unique(*vif); + auto local_p = std::make_unique(*vif); local_p->ValidateDescriptionOS(); vinfo = local_p->getDescriptor(); diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index d14bdb6e9..59adcdbe5 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -61,28 +61,6 @@ namespace DFHack { class color_ostream; } -/*! \namespace dts - * std.reverse() == dts, The namespace that include forward compatible helpers - * which can be used from newer standards. The preprocessor check prefers - * standard version if one is available. The standard version gets imported with - * using. - */ -namespace dts { -// Check if lib supports the feature test macro or version is over c++14. -#if __cpp_lib_make_unique < 201304 && __cplusplus < 201402L -//! Insert c++14 make_unique to be forward compatible. Array versions are -//! missing -template -typename std::enable_if::value, std::unique_ptr >::type -make_unique(Args&&... args) -{ - return std::unique_ptr{new T{std::forward(args)...}}; -} -#else /* >= c++14 */ -using std::make_unique; -#endif -} - template void print_bits ( T val, std::ostream& out ) { diff --git a/library/modules/Graphic.cpp b/library/modules/Graphic.cpp index 7f6b8f4d3..b55ee83ed 100644 --- a/library/modules/Graphic.cpp +++ b/library/modules/Graphic.cpp @@ -44,7 +44,7 @@ using namespace DFHack; std::unique_ptr DFHack::createGraphic() { - return dts::make_unique(); + return std::make_unique(); } bool Graphic::Register(DFTileSurface* (*func)(int,int)) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 7a1ef249f..7922b5417 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -604,7 +604,7 @@ bool DFHack::isStoneInorganic(int material) std::unique_ptr DFHack::createMaterials() { - return dts::make_unique(); + return std::make_unique(); } Materials::Materials() diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 44253b2f9..e1511153c 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -436,7 +436,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector ()); + autofarmInstance = std::move(std::make_unique()); autofarmInstance->load_state(out); return CR_OK; } diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 2b44d11ae..df8781caa 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -588,7 +588,7 @@ static int do_cycle(color_ostream &out); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(config,out).print("initializing %s\n", plugin_name); - tailor_instance = dts::make_unique(); + tailor_instance = std::make_unique(); // provide a configuration interface for the plugin commands.push_back(PluginCommand( From 0b78eb7cbcbc4a426b1e2726df5eb57b2f628664 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 8 Jul 2023 20:58:26 +0000 Subject: [PATCH 51/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 37701795f..f28878046 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 37701795f5705f5498df9543afdfb1255595976e +Subproject commit f288780464352648b1829afb3a81128ee3cb0aeb From 8740288a9bd0cae6c049fb6558155e84a44f0959 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 8 Jul 2023 21:15:21 +0000 Subject: [PATCH 52/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f28878046..14552dde1 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f288780464352648b1829afb3a81128ee3cb0aeb +Subproject commit 14552dde1fca00bec8fbcab4b361f36727b5f75c From 88732d724c5eb21b463ab3dea98c5586f02a60c6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 8 Jul 2023 14:16:04 -0700 Subject: [PATCH 53/65] update version to 50.09-r2rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f3d1b4e1c..f4cb3a9f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(dfhack) # set up versioning. set(DF_VERSION "50.09") -set(DFHACK_RELEASE "r2rc1") +set(DFHACK_RELEASE "r2rc2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From f987bca8f9d61174aaf8e9233cc493d8b2ebec43 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 24 May 2023 13:13:42 -0700 Subject: [PATCH 54/65] prevent DF interface events for handled hotkeys --- library/Core.cpp | 37 ++++++++++++++++++++++++++++--------- library/include/Core.h | 1 - 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index e4713c07f..78d74a476 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2359,8 +2359,10 @@ bool Core::DFH_ncurses_key(int key) // returns true if the event is handled bool Core::DFH_SDL_Event(SDL_Event* ev) { + static std::map hotkey_states; + // do NOT process events before we are ready. - if(!started || !ev) + if (!started || !ev) return false; if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { @@ -2372,23 +2374,40 @@ bool Core::DFH_SDL_Event(SDL_Event* ev) if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { auto &ke = ev->key; + auto &sym = ke.keysym.sym; - if (ke.keysym.sym == SDLK_LSHIFT || ke.keysym.sym == SDLK_RSHIFT) + if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT) modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_SHIFT : modstate & ~DFH_MOD_SHIFT; - else if (ke.keysym.sym == SDLK_LCTRL || ke.keysym.sym == SDLK_RCTRL) + else if (sym == SDLK_LCTRL || sym == SDLK_RCTRL) modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_CTRL : modstate & ~DFH_MOD_CTRL; - else if (ke.keysym.sym == SDLK_LALT || ke.keysym.sym == SDLK_RALT) + else if (sym == SDLK_LALT || sym == SDLK_RALT) modstate = (ev->type == SDL_KEYDOWN) ? modstate | DFH_MOD_ALT : modstate & ~DFH_MOD_ALT; - else if (ke.state == SDL_PRESSED && !hotkey_states[ke.keysym.sym]) + else if (ke.state == SDL_PRESSED && !hotkey_states[sym]) { - hotkey_states[ke.keysym.sym] = true; - SelectHotkey(ke.keysym.sym, modstate); + // the check against hotkey_states[sym] ensures we only process keybindings once per keypress + DEBUG(keybinding).print("key down: sym=%d (%c)\n", sym, sym); + bool handled = SelectHotkey(sym, modstate); + if (handled) { + DEBUG(keybinding).print("inhibiting SDL key down event\n"); + hotkey_states[sym] = true; + return true; + } } - else if(ke.state == SDL_RELEASED) + else if (ke.state == SDL_RELEASED) { - hotkey_states[ke.keysym.sym] = false; + DEBUG(keybinding).print("key up: sym=%d (%c)\n", sym, sym); + hotkey_states[sym] = false; } } + else if (ev->type == SDL_TEXTINPUT) { + auto &te = ev->text; + DEBUG(keybinding).print("text input: '%s'\n", te.text); + if (strlen(te.text) == 1 && hotkey_states[te.text[0]]) { + DEBUG(keybinding).print("inhibiting SDL text event\n"); + return true; + } + } + return false; } diff --git a/library/include/Core.h b/library/include/Core.h index dc2408ae9..180b5cc7b 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -250,7 +250,6 @@ namespace DFHack int8_t modstate; std::map > key_bindings; - std::map hotkey_states; std::string hotkey_cmd; enum hotkey_set_t { NO, From 5a719f0e6694a0986dc2b84bb524ba39f08eb105 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 7 Jun 2023 01:10:20 -0700 Subject: [PATCH 55/65] update docs --- docs/builtins/keybinding.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/builtins/keybinding.rst b/docs/builtins/keybinding.rst index c9665a048..c6553e48c 100644 --- a/docs/builtins/keybinding.rst +++ b/docs/builtins/keybinding.rst @@ -33,6 +33,11 @@ The ```` parameter above has the following **case-sensitive** syntax:: where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote optional parts. +DFHack commands can advertise the contexts in which they can be usefully run. +For example, a command that acts on a selected unit can tell `keybinding` that +it is not "applicable" in the current context if a unit is not actively +selected. + When multiple commands are bound to the same key combination, DFHack selects the first applicable one. Later ``add`` commands, and earlier entries within one ``add`` command have priority. Commands that are not specifically intended for From 7a618fd113152520ff7c14932ef25dd6ca3c663a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 8 Jul 2023 18:39:38 -0700 Subject: [PATCH 56/65] make event suppression configurable --- library/Core.cpp | 14 +++++++++++++- library/lua/dfhack.lua | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 78d74a476..13717ad30 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2356,6 +2356,18 @@ bool Core::DFH_ncurses_key(int key) return ncurses_wgetch(key, dummy); } +static bool getSuppressDuplicateKeyboardEvents() { + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + Lua::StackUnwinder top(L); + bool suppress = false; + Lua::CallLuaModuleFunction(out, L, "dfhack", "getSuppressDuplicateKeyboardEvents", 0, 1, + Lua::DEFAULT_LUA_LAMBDA, [&](lua_State* L) { + suppress = lua_toboolean(L, -1); + }, false); + return suppress; +} + // returns true if the event is handled bool Core::DFH_SDL_Event(SDL_Event* ev) { @@ -2390,7 +2402,7 @@ bool Core::DFH_SDL_Event(SDL_Event* ev) if (handled) { DEBUG(keybinding).print("inhibiting SDL key down event\n"); hotkey_states[sym] = true; - return true; + return getSuppressDuplicateKeyboardEvents(); } } else if (ke.state == SDL_RELEASED) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 8ea5e9dac..860b56cc1 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -63,6 +63,11 @@ function dfhack.getHideArmokTools() return dfhack.HIDE_ARMOK_TOOLS end +dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS = true +function dfhack.getSuppressDuplicateKeyboardEvents() + return dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS +end + -- Error handling safecall = dfhack.safecall From ed8bf1cff1af4b09807e2c68deb69fdc7ee0570a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 8 Jul 2023 18:40:53 -0700 Subject: [PATCH 57/65] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index fef273c64..3ff8ad217 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V +- Suppress DF keyboard events when a DFHack keybinding is matched. This prevents, for example, a backtick from appearing in a textbox as text when you launch `gui/launcher` from the backtick keybinding. ## Documentation From ff6f7c9454b4b4ff7406f1e9e3b0a620720947be Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Jul 2023 10:18:10 -0700 Subject: [PATCH 58/65] realign the misery docs to the actual implementation --- docs/changelog.txt | 1 + docs/plugins/misery.rst | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fef273c64..7313f6f8a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.EditField``: DFHack edit fields now support cut/copy/paste with the system clipboard with Ctrl-X/Ctrl-C/Ctrl-V ## Documentation +- `misery`: rewrite the documentation to clarify the actual effects of the plugin ## API - ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 8a65d8419..458253bb5 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -2,11 +2,12 @@ misery ====== .. dfhack-tool:: - :summary: Increase the intensity of your citizens' negative thoughts. + :summary: Lower the baseline for your citizens' happiness. :tags: fort gameplay units -When enabled, negative thoughts that your citizens have will multiply by the -specified factor. This makes it more challenging to keep them happy. +When enabled, all of your citizens receive a negative thought about a +particularly nasty soapy bath. You can vary the strength of this negative +thought to increase or decrease the difficulty of keeping your citizens happy. Usage ----- @@ -18,18 +19,18 @@ Usage misery misery clear -The default misery factor is ``2``, meaning that your dwarves will become -miserable twice as fast. +The default misery factor is ``2``, which will result in a moderate hit to your +dwarves' happiness. Larger numbers increase the challenge. Examples -------- ``enable misery`` - Start multiplying bad thoughts for your citizens! + Start adding bad thoughts about nasty soapy baths to your citizens! ``misery 5`` - Make dwarves become unhappy 5 times faster than normal -- this is quite - challenging to handle! + Change the strength of the soapy bath negative thought to something quite + large -- this is very challenging to handle! ``misery clear`` Clear away negative thoughts added by ``misery``. Note that this will not From 206c05feafdb36884845be99093f1fce45466ee2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Jul 2023 10:39:26 -0700 Subject: [PATCH 59/65] document preference variables --- docs/Core.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/Core.rst b/docs/Core.rst index 763858b61..ac17401dc 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -377,6 +377,26 @@ Other (non-DFHack-specific) variables that affect DFHack: sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this should be the case in most UTF-8-capable \*nix terminal emulators already. +Core preferences +================ + +There are a few settings that can be changed dynamically via +`gui/control-panel` to affect runtime behavior. You can also toggle these from +the commandline using the `lua` command, e.g. +``lua dfhack.HIDE_ARMOK_TOOLS=true`` or by editing the generated +``dfhack-config/init/dfhack.control-panel-preferences.init`` file and +restarting DF. + +- ``dfhack.HIDE_CONSOLE_ON_STARTUP``: Whether to hide the external DFHack + terminal window on startup. This, of course, is not useful to change + dynamically. You'll have to use `gui/control-panel` or edit the init file + directly and restart DF for it to have an effect. + +- ``dfhack.HIDE_ARMOK_TOOLS``: Whether to hide "armok" tools in command lists. + +- ``dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS``: Whether to prevent DFHack + keybindings from producing DF key events. + Miscellaneous notes =================== This section is for odd but important notes that don't fit anywhere else. From 6e565f327573ce6f43552f5b9fe5f2656a820b45 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Jul 2023 15:42:43 -0700 Subject: [PATCH 60/65] fix/update summaries --- docs/plugins/misery.rst | 2 +- plugins/misery.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 458253bb5..3bcd5588a 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -2,7 +2,7 @@ misery ====== .. dfhack-tool:: - :summary: Lower the baseline for your citizens' happiness. + :summary: Make citizens more miserable. :tags: fort gameplay units When enabled, all of your citizens receive a negative thought about a diff --git a/plugins/misery.cpp b/plugins/misery.cpp index f241394ae..5e3bf6e1f 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -72,7 +72,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector Date: Sun, 9 Jul 2023 15:50:56 -0700 Subject: [PATCH 61/65] simplify retrieval of setting property --- library/Core.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 13717ad30..0a1c19351 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2360,12 +2360,8 @@ static bool getSuppressDuplicateKeyboardEvents() { auto L = Lua::Core::State; color_ostream_proxy out(Core::getInstance().getConsole()); Lua::StackUnwinder top(L); - bool suppress = false; - Lua::CallLuaModuleFunction(out, L, "dfhack", "getSuppressDuplicateKeyboardEvents", 0, 1, - Lua::DEFAULT_LUA_LAMBDA, [&](lua_State* L) { - suppress = lua_toboolean(L, -1); - }, false); - return suppress; + return DFHack::Lua::PushModulePublic(out, L, "dfhack", "SUPPRESS_DUPLICATE_KEYBOARD_EVENTS") && + lua_toboolean(L, -1); } // returns true if the event is handled From 49d21bad90ce6db48db1e32dc880c9a4c3f07b4c Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:27:54 +0000 Subject: [PATCH 62/65] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 14552dde1..fafe4677c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 14552dde1fca00bec8fbcab4b361f36727b5f75c +Subproject commit fafe4677cd26d1915c417828368b6fe94e2caa95 From d0d12414bf71e67953a095514c870faf95c2ed3f Mon Sep 17 00:00:00 2001 From: egocarib Date: Mon, 10 Jul 2023 18:43:50 -0500 Subject: [PATCH 63/65] Fix small typo --- docs/dev/compile/Compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index 22b9a7b1a..d9a0fa4c8 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -89,7 +89,7 @@ assistance. All Platforms ============= Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, -but from command line is the usual way to do this; thought the Windows section below points out some +but from command line is the usual way to do this; though the Windows section below points out some Windows batch files that can be used to avoid opening a terminal/command-prompt. You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See From 755ddd9db31e6744f6e9f161471d18f786306b15 Mon Sep 17 00:00:00 2001 From: egocarib Date: Mon, 10 Jul 2023 19:36:05 -0500 Subject: [PATCH 64/65] Additional sentence improvements --- docs/dev/compile/Compile.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index d9a0fa4c8..5e605e391 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -88,9 +88,9 @@ assistance. All Platforms ============= -Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, -but from command line is the usual way to do this; though the Windows section below points out some -Windows batch files that can be used to avoid opening a terminal/command-prompt. +Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this +for you, but it's more common to do it from the command line. Windows developers can refer to the +Windows section below for batch files that can be used to avoid opening a terminal/command-prompt. You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See the `build-options` page for help finding the DFHack build options relevant to you. From e2a91ed3bbac5f26021a46a49d92adf3902ef4e1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 13 Jul 2023 07:14:52 +0000 Subject: [PATCH 65/65] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 0d4230a9d..19dd3a941 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0d4230a9db2ebc381e73296bc4e19bee09551771 +Subproject commit 19dd3a94162a2ff3374cbdb1cadc92b55a626c92