Merge branch 'develop' into patch-1

develop
Myk 2023-07-13 17:02:39 -07:00 committed by GitHub
commit 1643eac7e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 982 additions and 182 deletions

@ -20,7 +20,7 @@ repos:
args: ['--fix=lf'] args: ['--fix=lf']
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.23.1 rev: 0.23.2
hooks: hooks:
- id: check-github-workflows - id: check-github-workflows
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks

@ -8,8 +8,8 @@ project(dfhack)
# set up versioning. # set up versioning.
set(DF_VERSION "50.09") set(DF_VERSION "50.09")
set(DFHACK_RELEASE "r1") set(DFHACK_RELEASE "r2rc2")
set(DFHACK_PRERELEASE FALSE) set(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
set(DFHACK_ABI_VERSION 1) set(DFHACK_ABI_VERSION 1)
@ -35,9 +35,10 @@ option(REMOVE_SYMBOLS_FROM_DF_STUBS "Remove debug symbols from DF stubs. (Reduce
macro(CHECK_GCC compiler_path) macro(CHECK_GCC compiler_path)
execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT) execute_process(COMMAND ${compiler_path} -dumpversion OUTPUT_VARIABLE GCC_VERSION_OUT)
string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT) string(STRIP "${GCC_VERSION_OUT}" GCC_VERSION_OUT)
if(${GCC_VERSION_OUT} VERSION_LESS "4.8") if(${GCC_VERSION_OUT} VERSION_LESS "10")
message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 4.8 or later") message(SEND_ERROR "${compiler_path} version ${GCC_VERSION_OUT} cannot be used - use GCC 10 or later")
elseif(${GCC_VERSION_OUT} VERSION_GREATER "4.9.9") # 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. # GCC 5 changes ABI name mangling to enable C++11 changes.
# This must be disabled to enable linking against DF. # This must be disabled to enable linking against DF.
# http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/ # http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/
@ -66,8 +67,8 @@ if(WIN32)
endif() endif()
endif() endif()
# Ask for C++11 standard from compilers # Ask for C++-20 standard from compilers
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 20)
# Require the standard support from compilers. # Require the standard support from compilers.
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Use only standard c++ to keep code portable # Use only standard c++ to keep code portable
@ -226,9 +227,7 @@ if(UNIX)
## flags for GCC ## flags for GCC
# default to hidden symbols # default to hidden symbols
# ensure compatibility with older CPUs # ensure compatibility with older CPUs
# enable C++11 features
add_definitions(-DLINUX_BUILD) add_definitions(-DLINUX_BUILD)
add_definitions(-D_GLIBCXX_USE_C99)
set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror") set(GCC_COMMON_FLAGS "-fvisibility=hidden -mtune=generic -Wall -Werror")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMMON_FLAGS}")

@ -4,11 +4,19 @@ add_subdirectory(lua)
add_subdirectory(md5) add_subdirectory(md5)
add_subdirectory(protobuf) add_subdirectory(protobuf)
if(UNIX)
set_target_properties(lua PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-deprecated-enum-enum-conversion")
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 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) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF)
add_subdirectory(googletest) add_subdirectory(googletest)
if(UNIX) 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()
endif() endif()

@ -46,6 +46,10 @@
#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__
#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ #define GOOGLE_PROTOBUF_REPEATED_FIELD_H__
#ifdef __GNUC__
#pragma GCC system_header
#endif
#include <string> #include <string>
#include <iterator> #include <iterator>
#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/common.h>

@ -377,6 +377,26 @@ Other (non-DFHack-specific) variables that affect DFHack:
sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this sensitive), ``DF2CONSOLE()`` will produce UTF-8-encoded text. Note that this
should be the case in most UTF-8-capable \*nix terminal emulators already. 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 Miscellaneous notes
=================== ===================
This section is for odd but important notes that don't fit anywhere else. This section is for odd but important notes that don't fit anywhere else.

@ -33,6 +33,11 @@ The ``<key>`` parameter above has the following **case-sensitive** syntax::
where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote where the ``KEY`` part can be any recognized key and :kbd:`[`:kbd:`]` denote
optional parts. 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 When multiple commands are bound to the same key combination, DFHack selects
the first applicable one. Later ``add`` commands, and earlier entries within one the first applicable one. Later ``add`` commands, and earlier entries within one
``add`` command have priority. Commands that are not specifically intended for ``add`` command have priority. Commands that are not specifically intended for

@ -34,20 +34,31 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future # Future
## New Plugins ## 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 ## 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 ## Misc Improvements
- `autonick`: add more variety to nicknames based on famous literary dwarves - `autonick`: add more variety to nicknames based on famous literary dwarves
- ``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 ## Documentation
- `misery`: rewrite the documentation to clarify the actual effects of the plugin
## API ## API
- ``Units::getUnitByNobleRole``, ``Units::getUnitsByNobleRole``: unit lookup API by role
- ``Items::markForTrade()``, ``Items::isRequestedTradeGood()``, ``Items::getValue``: see Lua notes below
## Internals ## Internals
- Price calculations fixed for many item types
## Lua ## 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 ## Removed

@ -1438,10 +1438,23 @@ Units module
Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to
the arguments required by this function. 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 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)``
Returns a list of units (possibly empty) assigned to the given noble role.
* ``dfhack.units.getCitizens([ignore_sanity])`` * ``dfhack.units.getCitizens([ignore_sanity])``
Returns a table (list) of all citizens, which you would otherwise have to loop over all Returns a table (list) of all citizens, which you would otherwise have to
units in world and test against ``isCitizen()`` to discover. loop over all units in world and test against ``isCitizen()`` to discover.
* ``dfhack.units.teleport(unit, pos)`` * ``dfhack.units.teleport(unit, pos)``
@ -1755,9 +1768,20 @@ Items module
Calculates the base value for an item of the specified type and material. Calculates the base value for an item of the specified type and material.
* ``dfhack.items.getValue(item)`` * ``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.isRequestedTradeGood(item[, caravan_state])``
Calculates the Basic Value of an item, as seen in the View Item screen. 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)`` * ``dfhack.items.createItem(item_type, item_subtype, mat_type, mat_index, unit)``
@ -2819,6 +2843,13 @@ and are only documented here for completeness:
Returns 0 if the address is not found. Returns 0 if the address is not found.
Requires a heap snapshot. Requires a heap snapshot.
* ``dfhack.internal.getClipboardTextCp437()``
Gets the system clipboard text (and converts text to CP437 encoding).
* ``dfhack.internal.setClipboardTextCp437(text)``
Sets the system clipboard text from a CP437 string.
.. _lua-core-context: .. _lua-core-context:
@ -4692,6 +4723,12 @@ following keyboard hotkeys:
- Ctrl-B/Ctrl-F: move the cursor one word back or forward. - 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. - 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: The ``EditField`` class also provides the following functions:
* ``editfield:setCursor([cursor_pos])`` * ``editfield:setCursor([cursor_pos])``

@ -88,9 +88,9 @@ assistance.
All Platforms All Platforms
============= =============
Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, 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 for you, but it's more common to do it from the command line. Windows developers can refer to the
Windows batch files that can be used to avoid opening a terminal/command-prompt. 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 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. the `build-options` page for help finding the DFHack build options relevant to you.

@ -2,11 +2,12 @@ misery
====== ======
.. dfhack-tool:: .. dfhack-tool::
:summary: Increase the intensity of your citizens' negative thoughts. :summary: Make citizens more miserable.
:tags: fort gameplay units :tags: fort gameplay units
When enabled, negative thoughts that your citizens have will multiply by the When enabled, all of your citizens receive a negative thought about a
specified factor. This makes it more challenging to keep them happy. 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 Usage
----- -----
@ -18,18 +19,18 @@ Usage
misery <factor> misery <factor>
misery clear misery clear
The default misery factor is ``2``, meaning that your dwarves will become The default misery factor is ``2``, which will result in a moderate hit to your
miserable twice as fast. dwarves' happiness. Larger numbers increase the challenge.
Examples Examples
-------- --------
``enable misery`` ``enable misery``
Start multiplying bad thoughts for your citizens! Start adding bad thoughts about nasty soapy baths to your citizens!
``misery 5`` ``misery 5``
Make dwarves become unhappy 5 times faster than normal -- this is quite Change the strength of the soapy bath negative thought to something quite
challenging to handle! large -- this is very challenging to handle!
``misery clear`` ``misery clear``
Clear away negative thoughts added by ``misery``. Note that this will not Clear away negative thoughts added by ``misery``. Note that this will not

@ -1404,7 +1404,7 @@ Core::~Core()
} }
Core::Core() : Core::Core() :
d(dts::make_unique<Private>()), d(std::make_unique<Private>()),
script_path_mutex{}, script_path_mutex{},
HotkeyMutex{}, HotkeyMutex{},
HotkeyCond{}, HotkeyCond{},
@ -1471,6 +1471,10 @@ std::string Core::getHackPath()
#endif #endif
} }
df::viewscreen * Core::getTopViewscreen() {
return getInstance().top_viewscreen;
}
bool Core::InitMainThread() { bool Core::InitMainThread() {
Filesystem::init(); Filesystem::init();
@ -1497,7 +1501,7 @@ bool Core::InitMainThread() {
#else #else
const char * path = "hack\\symbols.xml"; const char * path = "hack\\symbols.xml";
#endif #endif
auto local_vif = dts::make_unique<DFHack::VersionInfoFactory>(); auto local_vif = std::make_unique<DFHack::VersionInfoFactory>();
std::cerr << "Identifying DF version.\n"; std::cerr << "Identifying DF version.\n";
try try
{ {
@ -1513,7 +1517,7 @@ bool Core::InitMainThread() {
return false; return false;
} }
vif = std::move(local_vif); vif = std::move(local_vif);
auto local_p = dts::make_unique<DFHack::Process>(*vif); auto local_p = std::make_unique<DFHack::Process>(*vif);
local_p->ValidateDescriptionOS(); local_p->ValidateDescriptionOS();
vinfo = local_p->getDescriptor(); vinfo = local_p->getDescriptor();
@ -1855,6 +1859,11 @@ void *Core::GetData( std::string key )
} }
} }
Core& Core::getInstance() {
static Core instance;
return instance;
}
bool Core::isSuspended(void) bool Core::isSuspended(void)
{ {
return ownerThread.load() == std::this_thread::get_id(); return ownerThread.load() == std::this_thread::get_id();
@ -2347,11 +2356,21 @@ bool Core::DFH_ncurses_key(int key)
return ncurses_wgetch(key, dummy); return ncurses_wgetch(key, dummy);
} }
static bool getSuppressDuplicateKeyboardEvents() {
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
Lua::StackUnwinder top(L);
return DFHack::Lua::PushModulePublic(out, L, "dfhack", "SUPPRESS_DUPLICATE_KEYBOARD_EVENTS") &&
lua_toboolean(L, -1);
}
// returns true if the event is handled // returns true if the event is handled
bool Core::DFH_SDL_Event(SDL_Event* ev) bool Core::DFH_SDL_Event(SDL_Event* ev)
{ {
static std::map<int, bool> hotkey_states;
// do NOT process events before we are ready. // do NOT process events before we are ready.
if(!started || !ev) if (!started || !ev)
return false; return false;
if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { if (ev->type == SDL_WINDOWEVENT && ev->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
@ -2363,23 +2382,40 @@ bool Core::DFH_SDL_Event(SDL_Event* ev)
if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
auto &ke = ev->key; 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; 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; 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; 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; // the check against hotkey_states[sym] ensures we only process keybindings once per keypress
SelectHotkey(ke.keysym.sym, modstate); 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 getSuppressDuplicateKeyboardEvents();
}
} }
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; return false;
} }

@ -213,12 +213,12 @@ std::string pointer_identity::getFullName()
std::string container_identity::getFullName(type_identity *item) 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) 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 *) std::string bit_container_identity::getFullName(type_identity *)

@ -26,6 +26,7 @@ redistribute it freely, subject to the following restrictions:
#include "Debug.h" #include "Debug.h"
#include "DebugManager.h" #include "DebugManager.h"
#include <algorithm>
#include <chrono> #include <chrono>
#include <iomanip> #include <iomanip>
#include <thread> #include <thread>

@ -1833,6 +1833,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, multiplyGroupActionTimers), WRAPM(Units, multiplyGroupActionTimers),
WRAPM(Units, setActionTimers), WRAPM(Units, setActionTimers),
WRAPM(Units, setGroupActionTimers), WRAPM(Units, setGroupActionTimers),
WRAPM(Units, getUnitByNobleRole),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1921,6 +1922,14 @@ static int units_getCitizens(lua_State *L) {
return 0; return 0;
} }
static int units_getUnitsByNobleRole(lua_State *L) {
std::string role_name = lua_tostring(L, -1);
std::vector<df::unit *> units;
Units::getUnitsByNobleRole(units, role_name);
Lua::PushVector(L, units);
return 1;
}
static int units_getStressCutoffs(lua_State *L) static int units_getStressCutoffs(lua_State *L)
{ {
lua_newtable(L); lua_newtable(L);
@ -1935,6 +1944,7 @@ static const luaL_Reg dfhack_units_funcs[] = {
{ "getNoblePositions", units_getNoblePositions }, { "getNoblePositions", units_getNoblePositions },
{ "getUnitsInBox", units_getUnitsInBox }, { "getUnitsInBox", units_getUnitsInBox },
{ "getCitizens", units_getCitizens }, { "getCitizens", units_getCitizens },
{ "getUnitsByNobleRole", units_getUnitsByNobleRole},
{ "getStressCutoffs", units_getStressCutoffs }, { "getStressCutoffs", units_getStressCutoffs },
{ NULL, NULL } { NULL, NULL }
}; };
@ -2010,6 +2020,7 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getSubtypeDef), WRAPM(Items, getSubtypeDef),
WRAPM(Items, getItemBaseValue), WRAPM(Items, getItemBaseValue),
WRAPM(Items, getValue), WRAPM(Items, getValue),
WRAPM(Items, isRequestedTradeGood),
WRAPM(Items, createItem), WRAPM(Items, createItem),
WRAPM(Items, checkMandates), WRAPM(Items, checkMandates),
WRAPM(Items, canTrade), WRAPM(Items, canTrade),
@ -2604,14 +2615,29 @@ static int screen_doSimulateInput(lua_State *L)
int sz = lua_rawlen(L, 2); int sz = lua_rawlen(L, 2);
std::set<df::interface_key> keys; std::set<df::interface_key> keys;
char str = '\0';
for (int j = 1; j <= sz; j++) for (int j = 1; j <= sz; j++)
{ {
lua_rawgeti(L, 2, 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); 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); screen->feed(&keys);
if (str)
strcpy((char *)&df::global::enabler->last_text_input[0], prev_input.c_str());
return 0; return 0;
} }
@ -3014,6 +3040,8 @@ static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAPN(getAddressSizeInHeap, get_address_size_in_heap), WRAPN(getAddressSizeInHeap, get_address_size_in_heap),
WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object), WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object),
WRAPN(msizeAddress, msize_address), WRAPN(msizeAddress, msize_address),
WRAP(getClipboardTextCp437),
WRAP(setClipboardTextCp437),
{ NULL, NULL } { NULL, NULL }
}; };

@ -27,6 +27,8 @@ distribution.
#include "MiscUtils.h" #include "MiscUtils.h"
#include "ColorText.h" #include "ColorText.h"
#include "modules/DFSDL.h"
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
// We don't want min and max macros // We don't want min and max macros
#define NOMINMAX #define NOMINMAX

@ -40,8 +40,6 @@ distribution.
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include "RemoteClient.h"
#define DFH_MOD_SHIFT 1 #define DFH_MOD_SHIFT 1
#define DFH_MOD_CTRL 2 #define DFH_MOD_CTRL 2
#define DFH_MOD_ALT 4 #define DFH_MOD_ALT 4
@ -74,6 +72,17 @@ namespace DFHack
struct Hide; 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 enum state_change_event
{ {
SC_UNKNOWN = -1, SC_UNKNOWN = -1,
@ -97,10 +106,14 @@ namespace DFHack
StateChangeScript(state_change_event event, std::string path, bool save_specific = false) StateChangeScript(state_change_event event, std::string path, bool save_specific = false)
:event(event), path(path), save_specific(save_specific) :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; 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. // Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
@ -116,11 +129,7 @@ namespace DFHack
friend bool ::dfhooks_ncurses_key(int key); friend bool ::dfhooks_ncurses_key(int key);
public: public:
/// Get the single Core instance or make one. /// Get the single Core instance or make one.
static Core& getInstance() static Core& getInstance();
{
static Core instance;
return instance;
}
/// check if the activity lock is owned by this thread /// check if the activity lock is owned by this thread
bool isSuspended(void); bool isSuspended(void);
/// Is everything OK? /// Is everything OK?
@ -168,7 +177,7 @@ namespace DFHack
bool isWorldLoaded() { return (last_world_data_ptr != NULL); } bool isWorldLoaded() { return (last_world_data_ptr != NULL); }
bool isMapLoaded() { return (last_local_map_ptr != NULL && 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; } DFHack::Console &getConsole() { return con; }
@ -241,7 +250,6 @@ namespace DFHack
int8_t modstate; int8_t modstate;
std::map<int, std::vector<KeyBinding> > key_bindings; std::map<int, std::vector<KeyBinding> > key_bindings;
std::map<int, bool> hotkey_states;
std::string hotkey_cmd; std::string hotkey_cmd;
enum hotkey_set_t { enum hotkey_set_t {
NO, NO,

@ -61,28 +61,6 @@ namespace DFHack {
class color_ostream; 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 T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T> >::type
make_unique(Args&&... args)
{
return std::unique_ptr<T>{new T{std::forward<Args>(args)...}};
}
#else /* >= c++14 */
using std::make_unique;
#endif
}
template <typename T> template <typename T>
void print_bits ( T val, std::ostream& out ) void print_bits ( T val, std::ostream& out )
{ {

@ -35,8 +35,6 @@ distribution.
#include "Core.h" #include "Core.h"
#include "DataFuncs.h" #include "DataFuncs.h"
#include "RemoteClient.h"
typedef struct lua_State lua_State; typedef struct lua_State lua_State;
namespace tthread namespace tthread

@ -26,6 +26,7 @@ distribution.
#include "Pragma.h" #include "Pragma.h"
#include "Export.h" #include "Export.h"
#include "ColorText.h" #include "ColorText.h"
#include "Core.h"
class CPassiveSocket; class CPassiveSocket;
class CActiveSocket; class CActiveSocket;
@ -39,17 +40,6 @@ namespace DFHack
using dfproto::IntMessage; using dfproto::IntMessage;
using dfproto::StringMessage; 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 { enum DFHackReplyCode : int16_t {
RPC_REPLY_RESULT = -1, RPC_REPLY_RESULT = -1,
RPC_REPLY_FAIL = -2, RPC_REPLY_FAIL = -2,

@ -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, * 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. * the block's items are checked for anything on the ground within that stockpile.
*/ */
class DFHACK_EXPORT StockpileIterator : public std::iterator<std::input_iterator_tag, df::item> class DFHACK_EXPORT StockpileIterator
{ {
df::building_stockpilest* stockpile; df::building_stockpilest* stockpile;
df::map_block* block; df::map_block* block;
@ -245,6 +245,12 @@ class DFHACK_EXPORT StockpileIterator : public std::iterator<std::input_iterator
df::item *item; df::item *item;
public: public:
using iterator_category = std::input_iterator_tag;
using value_type = df::item*;
using difference_type = std::ptrdiff_t;
using pointer = df::item**;
using reference = df::item*&;
StockpileIterator() { StockpileIterator() {
stockpile = NULL; stockpile = NULL;
block = NULL; block = NULL;

@ -47,7 +47,17 @@ DFHACK_EXPORT void DFSDL_FreeSurface(SDL_Surface *surface);
// DFHACK_EXPORT int DFSDL_SemWait(SDL_sem *sem); // DFHACK_EXPORT int DFSDL_SemWait(SDL_sem *sem);
// DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem); // DFHACK_EXPORT int DFSDL_SemPost(SDL_sem *sem);
DFHACK_EXPORT int DFSDL_PushEvent(SDL_Event *event); 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 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);
} }

@ -34,6 +34,7 @@ distribution.
#include "DataDefs.h" #include "DataDefs.h"
#include "df/building_tradedepotst.h" #include "df/building_tradedepotst.h"
#include "df/caravan_state.h"
#include "df/item.h" #include "df/item.h"
#include "df/item_type.h" #include "df/item_type.h"
#include "df/general_ref.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 /// 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); 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 /// 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); 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); DFHACK_EXPORT int32_t createItem(df::item_type type, int16_t item_subtype, int16_t mat_type, int32_t mat_index, df::unit* creator);
@ -202,6 +203,8 @@ DFHACK_EXPORT bool canTrade(df::item *item);
DFHACK_EXPORT bool canTradeWithContents(df::item *item); DFHACK_EXPORT bool canTradeWithContents(df::item *item);
/// marks the given item for trade at the given depot /// marks the given item for trade at the given depot
DFHACK_EXPORT bool markForTrade(df::item *item, df::building_tradedepotst *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, df::caravan_state *caravan = NULL);
/// Checks whether the item is an assigned hauling vehicle /// Checks whether the item is an assigned hauling vehicle
DFHACK_EXPORT bool isRouteVehicle(df::item *item); DFHACK_EXPORT bool isRouteVehicle(df::item *item);

@ -229,8 +229,8 @@ namespace DFHack
DFHACK_EXPORT bool hasActiveScreens(Plugin *p); DFHACK_EXPORT bool hasActiveScreens(Plugin *p);
DFHACK_EXPORT void raise(df::viewscreen *screen); DFHACK_EXPORT void raise(df::viewscreen *screen);
// returns a new set with text interface keys from the text buffer added in (if any) // returns a new set of interface keys that ensures that string input matches the DF text buffer
DFHACK_EXPORT std::set<df::interface_key> add_text_keys(const std::set<df::interface_key>& keys); DFHACK_EXPORT std::set<df::interface_key> normalize_text_keys(const std::set<df::interface_key>& keys);
/// Retrieve the string representation of the bound key. /// Retrieve the string representation of the bound key.
DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key);

@ -148,6 +148,8 @@ DFHACK_EXPORT df::unit *getUnit(const int32_t index);
DFHACK_EXPORT bool getUnitsInBox(std::vector<df::unit*> &units, DFHACK_EXPORT bool getUnitsInBox(std::vector<df::unit*> &units,
int16_t x1, int16_t y1, int16_t z1, int16_t x1, int16_t y1, int16_t z1,
int16_t x2, int16_t y2, int16_t z2); int16_t x2, int16_t y2, int16_t z2);
DFHACK_EXPORT bool getUnitsByNobleRole(std::vector<df::unit *> &units, std::string noble);
DFHACK_EXPORT df::unit *getUnitByNobleRole(std::string noble);
DFHACK_EXPORT bool getCitizens(std::vector<df::unit *> &citizens, bool ignore_sanity = false); DFHACK_EXPORT bool getCitizens(std::vector<df::unit *> &citizens, bool ignore_sanity = false);
DFHACK_EXPORT int32_t findIndexById(int32_t id); DFHACK_EXPORT int32_t findIndexById(int32_t id);

@ -63,6 +63,11 @@ function dfhack.getHideArmokTools()
return dfhack.HIDE_ARMOK_TOOLS return dfhack.HIDE_ARMOK_TOOLS
end end
dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS = true
function dfhack.getSuppressDuplicateKeyboardEvents()
return dfhack.SUPPRESS_DUPLICATE_KEYBOARD_EVENTS
end
-- Error handling -- Error handling
safecall = dfhack.safecall safecall = dfhack.safecall

@ -640,8 +640,12 @@ function EditField:setCursor(cursor)
end end
function EditField:setText(text, cursor) function EditField:setText(text, cursor)
local old = self.text
self.text = text self.text = text
self:setCursor(cursor) self:setCursor(cursor)
if self.on_change and text ~= old then
self.on_change(self.text, old)
end
end end
function EditField:postUpdateLayout() function EditField:postUpdateLayout()
@ -699,11 +703,7 @@ function EditField:onInput(keys)
end end
if self.key and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then if self.key and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then
local old = self.text
self:setText(self.saved_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) self:setFocus(false)
return true return true
end end
@ -747,9 +747,6 @@ function EditField:onInput(keys)
return self.modal return self.modal
end end
end end
if self.on_change and self.text ~= old then
self.on_change(self.text, old)
end
return true return true
elseif keys.KEYBOARD_CURSOR_LEFT then elseif keys.KEYBOARD_CURSOR_LEFT then
self:setCursor(self.cursor - 1) self:setCursor(self.cursor - 1)
@ -759,9 +756,10 @@ function EditField:onInput(keys)
find('.*[%w_%-][^%w_%-]') find('.*[%w_%-][^%w_%-]')
self:setCursor(prev_word_end or 1) self:setCursor(prev_word_end or 1)
return true return true
elseif keys.CUSTOM_CTRL_A then -- home -- commented out until we get HOME key support from DF
self:setCursor(1) -- elseif keys.CUSTOM_CTRL_A then -- home
return true -- self:setCursor(1)
-- return true
elseif keys.KEYBOARD_CURSOR_RIGHT then elseif keys.KEYBOARD_CURSOR_RIGHT then
self:setCursor(self.cursor + 1) self:setCursor(self.cursor + 1)
return true return true
@ -772,6 +770,16 @@ function EditField:onInput(keys)
elseif keys.CUSTOM_CTRL_E then -- end elseif keys.CUSTOM_CTRL_E then -- end
self:setCursor() self:setCursor()
return true return true
elseif keys.CUSTOM_CTRL_C then
dfhack.internal.setClipboardTextCp437(self.text)
return true
elseif keys.CUSTOM_CTRL_X then
dfhack.internal.setClipboardTextCp437(self.text)
self:setText('')
return true
elseif keys.CUSTOM_CTRL_V then
self:insert(dfhack.internal.getClipboardTextCp437())
return true
end end
-- if we're modal, then unconditionally eat all the input -- if we're modal, then unconditionally eat all the input

@ -5,6 +5,8 @@
#include "Debug.h" #include "Debug.h"
#include "PluginManager.h" #include "PluginManager.h"
#include <SDL_stdinc.h>
namespace DFHack { namespace DFHack {
DBG_DECLARE(core, dfsdl, DebugCategory::LINFO); DBG_DECLARE(core, dfsdl, DebugCategory::LINFO);
} }
@ -35,6 +37,10 @@ void (*g_SDL_FreeSurface)(SDL_Surface *) = nullptr;
// int (*g_SDL_SemWait)(DFSDL_sem *) = nullptr; // int (*g_SDL_SemWait)(DFSDL_sem *) = nullptr;
// int (*g_SDL_SemPost)(DFSDL_sem *) = nullptr; // int (*g_SDL_SemPost)(DFSDL_sem *) = nullptr;
int (*g_SDL_PushEvent)(SDL_Event *) = 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) { bool DFSDL::init(color_ostream &out) {
for (auto &lib_str : SDL_LIBS) { for (auto &lib_str : SDL_LIBS) {
@ -71,6 +77,10 @@ bool DFSDL::init(color_ostream &out) {
// bind(g_sdl_handle, SDL_SemWait); // bind(g_sdl_handle, SDL_SemWait);
// bind(g_sdl_handle, SDL_SemPost); // bind(g_sdl_handle, SDL_SemPost);
bind(g_sdl_handle, SDL_PushEvent); 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 #undef bind
DEBUG(dfsdl,out).print("sdl successfully loaded\n"); DEBUG(dfsdl,out).print("sdl successfully loaded\n");
@ -124,3 +134,30 @@ void DFSDL::DFSDL_FreeSurface(SDL_Surface *surface) {
int DFSDL::DFSDL_PushEvent(SDL_Event *event) { int DFSDL::DFSDL_PushEvent(SDL_Event *event) {
return g_SDL_PushEvent(event); return g_SDL_PushEvent(event);
} }
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 textcp437 = UTF2DF(text);
DFHack::DFSDL::DFSDL_free(text);
return textcp437;
}
DFHACK_EXPORT bool DFHack::setClipboardTextCp437(std::string text) {
if (!g_sdl_handle)
return false;
return 0 == DFHack::DFSDL::DFSDL_SetClipboardText(DF2UTF(text).c_str());
}

@ -108,7 +108,7 @@ static bool is_running_on_wine() {
return !!pwine_get_version; return !!pwine_get_version;
} }
static DWORD findProcess(LPWSTR name) { static DWORD findProcess(LPCWSTR name) {
PROCESSENTRY32W entry; PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W); entry.dwSize = sizeof(PROCESSENTRY32W);
@ -150,11 +150,14 @@ static bool launchDFHack(color_ostream& out) {
si.cb = sizeof(si); si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi)); 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, // 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. // and the Steam API in the launchdf process will think it is in DF's context.
BOOL res = CreateProcessW(L"hack/launchdf.exe", BOOL res = CreateProcessW(procname,
NULL, NULL, NULL, FALSE, 0, "\0", NULL, &si, &pi); NULL, NULL, NULL, FALSE, 0, (LPVOID)env, NULL, &si, &pi);
return !!res; return !!res;
} }

@ -44,7 +44,7 @@ using namespace DFHack;
std::unique_ptr<Module> DFHack::createGraphic() std::unique_ptr<Module> DFHack::createGraphic()
{ {
return dts::make_unique<Graphic>(); return std::make_unique<Graphic>();
} }
bool Graphic::Register(DFTileSurface* (*func)(int,int)) bool Graphic::Register(DFTileSurface* (*func)(int,int))

@ -182,23 +182,23 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
if (game->main_interface.info.open) { if (game->main_interface.info.open) {
newFocusString = baseFocus; newFocusString = baseFocus;
newFocusString += "/Info"; 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) { switch(game->main_interface.info.current_mode) {
case df::enums::info_interface_mode_type::CREATURES: 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; break;
case df::enums::info_interface_mode_type::BUILDINGS: 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; break;
case df::enums::info_interface_mode_type::LABOR: 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; break;
case df::enums::info_interface_mode_type::ARTIFACTS: 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; break;
case df::enums::info_interface_mode_type::JUSTICE: 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; break;
case df::enums::info_interface_mode_type::WORK_ORDERS: case df::enums::info_interface_mode_type::WORK_ORDERS:
if (game->main_interface.info.work_orders.conditions.open) if (game->main_interface.info.work_orders.conditions.open)
@ -215,7 +215,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
if (game->main_interface.view_sheets.open) { if (game->main_interface.view_sheets.open) {
newFocusString = baseFocus; newFocusString = baseFocus;
newFocusString += "/ViewSheets"; newFocusString += "/ViewSheets";
newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet); newFocusString += '/' + enum_item_key(game->main_interface.view_sheets.active_sheet);
if (game->main_interface.view_sheets.active_sheet == df::view_sheet_type::BUILDING) { if (game->main_interface.view_sheets.active_sheet == df::view_sheet_type::BUILDING) {
auto bld = df::building::find(game->main_interface.view_sheets.viewing_bldid); auto bld = df::building::find(game->main_interface.view_sheets.viewing_bldid);
if (bld) if (bld)
@ -244,7 +244,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
newFocusString += "/Zone"; newFocusString += "/Zone";
if (game->main_interface.civzone.cur_bld) { if (game->main_interface.civzone.cur_bld) {
newFocusString += "/Some"; 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; break;
case df::enums::main_bottom_mode_type::ZONE_PAINT: case df::enums::main_bottom_mode_type::ZONE_PAINT:
@ -519,7 +519,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode)
if (!adventure) if (!adventure)
return; return;
focus += "/" + enum_item_key(adventure->menu); focus += '/' + enum_item_key(adventure->menu);
} }
*/ */
@ -1447,7 +1447,7 @@ DFHACK_EXPORT int Gui::makeAnnouncement(df::announcement_type type, df::announce
if (flags.bits.D_DISPLAY) if (flags.bits.D_DISPLAY)
{ {
world->status.display_timer = ANNOUNCE_DISPLAY_TIME; 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; return -1;
} }
@ -1709,7 +1709,7 @@ bool Gui::autoDFAnnouncement(df::report_init r, string message)
if (a_flags.bits.D_DISPLAY) if (a_flags.bits.D_DISPLAY)
{ {
world->status.display_timer = r.display_timer; 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()); DEBUG(gui).print("Announcement succeeded as repeat:\n%s\n", message.c_str());
return true; return true;

@ -50,8 +50,16 @@ using namespace std;
#include "df/building.h" #include "df/building.h"
#include "df/building_actual.h" #include "df/building_actual.h"
#include "df/building_tradedepotst.h" #include "df/building_tradedepotst.h"
#include "df/builtin_mats.h"
#include "df/caravan_state.h"
#include "df/caste_raw.h" #include "df/caste_raw.h"
#include "df/creature_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" #include "df/general_ref.h"
#include "df/general_ref_building_holderst.h" #include "df/general_ref_building_holderst.h"
#include "df/general_ref_contained_in_itemst.h" #include "df/general_ref_contained_in_itemst.h"
@ -828,15 +836,15 @@ std::string Items::getDescription(df::item *item, int type, bool decorate)
item->getItemDescription(&tmp, type); item->getItemDescription(&tmp, type);
if (decorate) { if (decorate) {
if (item->flags.bits.foreign)
tmp = "(" + tmp + ")";
addQuality(tmp, item->getQuality()); addQuality(tmp, item->getQuality());
if (item->isImproved()) { if (item->isImproved()) {
tmp = '\xAE' + tmp + '\xAF'; // («) + tmp + (») tmp = '\xAE' + tmp + '\xAF'; // («) + tmp + (»)
addQuality(tmp, item->getImprovementQuality()); addQuality(tmp, item->getImprovementQuality());
} }
if (item->flags.bits.foreign)
tmp = "(" + tmp + ")";
} }
return tmp; return tmp;
@ -1197,18 +1205,24 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
switch (item_type) switch (item_type)
{ {
case item_type::BAR: case item_type::BAR:
case item_type::SMALLGEM:
case item_type::BLOCKS: case item_type::BLOCKS:
case item_type::SKIN_TANNED: case item_type::SKIN_TANNED:
value = 5; value = 5;
break; break;
case item_type::ROUGH: case item_type::SMALLGEM:
value = 20;
break;
case item_type::BOULDER: case item_type::BOULDER:
case item_type::WOOD: case item_type::WOOD:
value = 3; value = 3;
break; break;
case item_type::ROUGH:
value = 6;
break;
case item_type::DOOR: case item_type::DOOR:
case item_type::FLOODGATE: case item_type::FLOODGATE:
case item_type::BED: case item_type::BED:
@ -1224,6 +1238,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
case item_type::TABLE: case item_type::TABLE:
case item_type::COFFIN: case item_type::COFFIN:
case item_type::BOX: case item_type::BOX:
case item_type::BAG:
case item_type::BIN: case item_type::BIN:
case item_type::ARMORSTAND: case item_type::ARMORSTAND:
case item_type::WEAPONRACK: case item_type::WEAPONRACK:
@ -1367,6 +1382,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
case item_type::COIN: case item_type::COIN:
case item_type::GLOB: case item_type::GLOB:
case item_type::ORTHOPEDIC_CAST: case item_type::ORTHOPEDIC_CAST:
case item_type::BRANCH:
value = 1; value = 1;
break; break;
@ -1435,7 +1451,498 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
return value; 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_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<typename T>
static int get_price(const std::vector<T> &res, int32_t val, const std::vector<int32_t> &pri) {
for (size_t idx = 0; idx < res.size(); ++idx) {
if (res[idx] == val && pri.size() > idx)
return pri[idx];
}
return -1;
}
template<typename T1, typename T2>
static int get_price(const std::vector<T1> &mat_res, int32_t mat, const std::vector<T2> &gloss_res, int32_t gloss, const std::vector<int32_t> &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<int32_t> *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)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);
@ -1447,16 +1954,38 @@ int Items::getValue(df::item *item)
// Get base value for item type, subtype, and material // Get base value for item type, subtype, and material
int value = getItemBaseValue(item_type, item_subtype, mat_type, mat_subtype); 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 // Improve value based on quality
int quality = item->getQuality(); switch (item->getQuality()) {
value *= (quality + 1); case 1:
if (quality == 5) 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 *= 2;
value += 30;
break;
default:
break;
}
// Add improvement values // 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 if (item_type == item_type::AMMO) // Ammo improvements are worth less
impValue /= 30; impValue /= 30;
value += impValue; value += impValue;
@ -1481,12 +2010,16 @@ int Items::getValue(df::item *item)
if (item->flags.bits.artifact_mood) if (item->flags.bits.artifact_mood)
value *= 10; value *= 10;
// modify buy/sell prices
value *= get_trade_agreement_multiplier(item, caravan, caravan_buying);
value >>= 7;
// Boost value from stack size // Boost value from stack size
value *= item->getStackSize(); value *= item->getStackSize();
// ...but not for coins // ...but not for coins
if (item_type == item_type::COIN) if (item_type == item_type::COIN)
{ {
value /= 500; value /= 50;
if (!value) if (!value)
value = 1; value = 1;
} }

@ -604,7 +604,7 @@ bool DFHack::isStoneInorganic(int material)
std::unique_ptr<Module> DFHack::createMaterials() std::unique_ptr<Module> DFHack::createMaterials()
{ {
return dts::make_unique<Materials>(); return std::make_unique<Materials>();
} }
Materials::Materials() Materials::Materials()

@ -585,8 +585,10 @@ void Hide::merge() {
} }
} } } }
std::set<df::interface_key> Screen::add_text_keys(const std::set<df::interface_key>& keys) { std::set<df::interface_key> Screen::normalize_text_keys(const std::set<df::interface_key>& keys) {
std::set<df::interface_key> combined_keys(keys); std::set<df::interface_key> 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]) { if (df::global::enabler->last_text_input[0]) {
char c = df::global::enabler->last_text_input[0]; char c = df::global::enabler->last_text_input[0];
df::interface_key key = charToKey(c); df::interface_key key = charToKey(c);
@ -952,7 +954,7 @@ int dfhack_lua_viewscreen::do_input(lua_State *L)
} }
lua_pushvalue(L, -2); 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); lua_call(L, 2, 0);
self->update_focus(L, -1); self->update_focus(L, -1);

@ -59,6 +59,7 @@ using namespace std;
#include "df/entity_position_assignment.h" #include "df/entity_position_assignment.h"
#include "df/entity_raw.h" #include "df/entity_raw.h"
#include "df/entity_raw_flags.h" #include "df/entity_raw_flags.h"
#include "df/entity_site_link.h"
#include "df/identity_type.h" #include "df/identity_type.h"
#include "df/game_mode.h" #include "df/game_mode.h"
#include "df/histfig_entity_link_positionst.h" #include "df/histfig_entity_link_positionst.h"
@ -80,6 +81,8 @@ using namespace std;
#include "df/unit_soul.h" #include "df/unit_soul.h"
#include "df/unit_wound.h" #include "df/unit_wound.h"
#include "df/world.h" #include "df/world.h"
#include "df/world_data.h"
#include "df/world_site.h"
#include "df/unit_action.h" #include "df/unit_action.h"
#include "df/unit_action_type_group.h" #include "df/unit_action_type_group.h"
@ -770,6 +773,60 @@ bool Units::getUnitsInBox (std::vector<df::unit*> &units,
return true; 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 add_assigned_noble_units(vector<df::unit *> &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;
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<df::unit *> &units, string noble, size_t limit = 0) {
auto &site = df::global::world->world_data->active_site[0];
for (auto &link : site->entity_links) {
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(he->positions, noble);
if (noble_position_id < 0)
continue;
add_assigned_noble_units(units, he->positions, noble_position_id, limit);
}
}
bool Units::getUnitsByNobleRole(vector<df::unit *> &units, std::string noble) {
units.clear();
get_units_by_noble_role(units, noble);
return !units.empty();
}
df::unit *Units::getUnitByNobleRole(string noble) {
vector<df::unit *> units;
get_units_by_noble_role(units, noble, 1);
if (units.empty())
return NULL;
return units[0];
}
bool Units::getCitizens(std::vector<df::unit *> &citizens, bool ignore_sanity) { bool Units::getCitizens(std::vector<df::unit *> &citizens, bool ignore_sanity) {
for (auto &unit : world->units.active) { for (auto &unit : world->units.active) {
if (isCitizen(unit, ignore_sanity)) if (isCitizen(unit, ignore_sanity))

@ -1 +1 @@
Subproject commit 5da8a785cf176f831a56f934ec8ec6069a965ecf Subproject commit 19dd3a94162a2ff3374cbdb1cadc92b55a626c92

@ -7,15 +7,16 @@
#include "Core.h" #include "Core.h"
#include "Console.h" #include "Console.h"
#include "DataDefs.h"
#include "Debug.h"
#include "Export.h" #include "Export.h"
#include "MiscUtils.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "modules/MapCache.h" #include "modules/MapCache.h"
#include "modules/Random.h" #include "modules/Random.h"
#include "modules/World.h" #include "modules/World.h"
#include "MiscUtils.h"
#include "DataDefs.h"
#include "df/world.h" #include "df/world.h"
#include "df/world_data.h" #include "df/world_data.h"
#include "df/world_region_details.h" #include "df/world_region_details.h"
@ -47,6 +48,10 @@ DFHACK_PLUGIN("3dveins");
REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(world);
REQUIRE_GLOBAL(gametype); REQUIRE_GLOBAL(gametype);
namespace DFHack {
DBG_DECLARE(_3dveins, process, DebugCategory::LINFO);
}
command_result cmd_3dveins(color_ostream &out, std::vector <std::string> & parameters); command_result cmd_3dveins(color_ostream &out, std::vector <std::string> & parameters);
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
@ -431,11 +436,13 @@ struct GeoLayer
void print_mineral_stats(color_ostream &out) void print_mineral_stats(color_ostream &out)
{ {
for (auto it = mineral_count.begin(); it != mineral_count.end(); ++it) for (auto it = mineral_count.begin(); it != mineral_count.end(); ++it)
out << " " << MaterialInfo(0,it->first.first).getToken() INFO(process, out).print("3dveins: %s %s: %d (%f)\n",
<< " " << ENUM_KEY_STR(inclusion_type,it->first.second) MaterialInfo(0, it->first.first).getToken().c_str(),
<< ": \t\t" << it->second << " (" << (float(it->second)/unmined_tiles) << ")" << std::endl; ENUM_KEY_STR(inclusion_type, it->first.second).c_str(),
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); bool form_veins(color_ostream &out);
@ -467,12 +474,12 @@ struct GeoBiome
void print_mineral_stats(color_ostream &out) 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++) for (size_t i = 0; i < layers.size(); i++)
if (layers[i]) if (layers[i])
{ {
out << " Layer " << i << std::endl; INFO(process, out).print("3dveins: Layer %ld\n", i);
layers[i]->print_mineral_stats(out); layers[i]->print_mineral_stats(out);
} }
} }
@ -586,7 +593,7 @@ bool VeinGenerator::init_biomes()
if (info.geo_index < 0 || !info.geobiome) if (info.geo_index < 0 || !info.geobiome)
{ {
out.printerr("Biome %zd is not defined.\n", i); WARN(process, out).print("Biome %zd is not defined.\n", i);
return false; 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) if (z != min_level[idx]-1 && min_level[idx] <= top_solid)
{ {
out.printerr( WARN(process, out).print("Discontinuous layer %d at (%d,%d,%d).\n",
"Discontinuous layer %d at (%d,%d,%d).\n",
layer->index, x+column.x*16, y+column.y*16, z layer->index, x+column.x*16, y+column.y*16, z
); );
return false; return false;
@ -848,7 +854,7 @@ bool VeinGenerator::adjust_layer_depth(df::coord2d column)
if (max_level[i+1] != min_level[i]-1) if (max_level[i+1] != min_level[i]-1)
{ {
out.printerr( WARN(process, out).print(
"Gap or overlap with next layer %d at (%d,%d,%d-%d).\n", "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] 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( WARN(process, out).print(
"Layer height change in layer %d at (%d,%d,%d): %d instead of %d.\n", "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], i, x+column.x*16, y+column.y*16, max_level[i],
size, biome->layers[i]->thickness size, biome->layers[i]->thickness
@ -914,6 +920,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z)
for (int y = 0; y < 16; y++) for (int y = 0; y < 16; y++)
{ {
df::coord2d tile(x,y); df::coord2d tile(x,y);
GeoLayer *layer = mapLayer(b, tile); GeoLayer *layer = mapLayer(b, tile);
if (!layer) if (!layer)
continue; continue;
@ -932,7 +939,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z)
if (unsigned(key.first) >= materials.size() || if (unsigned(key.first) >= materials.size() ||
unsigned(key.second) >= NUM_INCLUSIONS) unsigned(key.second) >= NUM_INCLUSIONS)
{ {
out.printerr("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; return false;
} }
@ -941,7 +948,7 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z)
if (status == -1) if (status == -1)
{ {
// Report first occurence of unreasonable vein spec // Report first occurence of unreasonable vein spec
out.printerr( WARN(process, out).print(
"Unexpected vein %s %s - ", "Unexpected vein %s %s - ",
MaterialInfo(0,key.first).getToken().c_str(), MaterialInfo(0,key.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, key.second).c_str() ENUM_KEY_STR(inclusion_type, key.second).c_str()
@ -949,9 +956,9 @@ bool VeinGenerator::scan_block_tiles(Block *b, df::coord2d column, int z)
status = materials[key.first].default_type; status = materials[key.first].default_type;
if (status < 0) if (status < 0)
out.printerr("will be left in place.\n"); WARN(process, out).print("will be left in place.\n");
else else
out.printerr( WARN(process, out).print(
"correcting to %s.\n", "correcting to %s.\n",
ENUM_KEY_STR(inclusion_type, df::inclusion_type(status)).c_str() ENUM_KEY_STR(inclusion_type, df::inclusion_type(status)).c_str()
); );
@ -1090,7 +1097,7 @@ void VeinGenerator::write_block_tiles(Block *b, df::coord2d column, int z)
if (!ok) if (!ok)
{ {
out.printerr( WARN(process, out).print(
"Couldn't write %d vein at (%d,%d,%d)\n", "Couldn't write %d vein at (%d,%d,%d)\n",
mat, x+column.x*16, y+column.y*16, z mat, x+column.x*16, y+column.y*16, z
); );
@ -1281,7 +1288,7 @@ bool GeoLayer::form_veins(color_ostream &out)
if (parent_id >= (int)refs.size()) if (parent_id >= (int)refs.size())
{ {
out.printerr("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; return false;
} }
@ -1301,7 +1308,7 @@ bool GeoLayer::form_veins(color_ostream &out)
if (vptr->parent) if (vptr->parent)
ctx = "only be in "+MaterialInfo(0,vptr->parent_mat()).getToken(); 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", "Duplicate vein %s %s in biome %d layer %d - will %s.\n",
MaterialInfo(0,key.first).getToken().c_str(), MaterialInfo(0,key.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, key.second).c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str(),
@ -1357,13 +1364,13 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from)
if (best.empty()) if (best.empty())
{ {
out.printerr( WARN(process,out).print(
"Could not place orphaned vein %s %s anywhere.\n", "Could not place orphaned vein %s %s anywhere.\n",
MaterialInfo(0,key.first).getToken().c_str(), MaterialInfo(0,key.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, key.second).c_str() ENUM_KEY_STR(inclusion_type, key.second).c_str()
); );
return false; return true;
} }
for (auto it = best.begin(); size > 0 && it != best.end(); ++it) for (auto it = best.begin(); size > 0 && it != best.end(); ++it)
@ -1391,7 +1398,7 @@ bool VeinGenerator::place_orphan(t_veinkey key, int size, GeoLayer *from)
if (size > 0) if (size > 0)
{ {
out.printerr( WARN(process, out).print(
"Could not place all of orphaned vein %s %s: %d left.\n", "Could not place all of orphaned vein %s %s: %d left.\n",
MaterialInfo(0,key.first).getToken().c_str(), MaterialInfo(0,key.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, key.second).c_str(), ENUM_KEY_STR(inclusion_type, key.second).c_str(),
@ -1541,7 +1548,7 @@ bool VeinGenerator::place_veins(bool verbose)
if (!isStoneInorganic(key.first)) if (!isStoneInorganic(key.first))
{ {
out.printerr( WARN(process, out).print(
"Invalid vein material: %s\n", "Invalid vein material: %s\n",
MaterialInfo(0, key.first).getToken().c_str() MaterialInfo(0, key.first).getToken().c_str()
); );
@ -1551,7 +1558,7 @@ bool VeinGenerator::place_veins(bool verbose)
if (!is_valid_enum_item(key.second)) if (!is_valid_enum_item(key.second))
{ {
out.printerr("Invalid vein type: %d\n", key.second); WARN(process, out).print("Invalid vein type: %d\n", key.second);
return false; return false;
} }
@ -1564,13 +1571,13 @@ bool VeinGenerator::place_veins(bool verbose)
sort(queue.begin(), queue.end(), vein_cmp); sort(queue.begin(), queue.end(), vein_cmp);
// Place tiles // 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++) for (size_t j = 0; j < queue.size(); j++)
{ {
if (queue[j]->parent && !queue[j]->parent->placed) if (queue[j]->parent && !queue[j]->parent->placed)
{ {
out.printerr( WARN(process, out).print(
"\nParent vein not placed for %s %s.\n", "\nParent vein not placed for %s %s.\n",
MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), MaterialInfo(0,queue[j]->vein.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str() ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str()
@ -1582,9 +1589,11 @@ bool VeinGenerator::place_veins(bool verbose)
if (verbose) if (verbose)
{ {
if (j > 0) 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%%)... ", "\nVein layer %zu of %zu: %s %s (%.2f%%)... ",
j+1, queue.size(), j+1, queue.size(),
MaterialInfo(0,queue[j]->vein.first).getToken().c_str(), MaterialInfo(0,queue[j]->vein.first).getToken().c_str(),
@ -1594,14 +1603,13 @@ bool VeinGenerator::place_veins(bool verbose)
} }
else else
{ {
out.print("\rVein layer %zu of %zu... ", j+1, queue.size()); TRACE(process, out).print("\rVein layer %zu of %zu... ", j+1, queue.size());
out.flush();
} }
queue[j]->place_tiles(); queue[j]->place_tiles();
} }
out.print("done.\n"); TRACE(process, out).print("done.\n");
return true; return true;
} }

@ -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, # 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. # 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(add-spatter add-spatter.cpp)
dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua)
dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua)

@ -436,7 +436,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector <Plugin
"Automatically manage farm crop selection.", "Automatically manage farm crop selection.",
autofarm)); autofarm));
} }
autofarmInstance = std::move(dts::make_unique<AutoFarm>()); autofarmInstance = std::move(std::make_unique<AutoFarm>());
autofarmInstance->load_state(out); autofarmInstance->load_state(out);
return CR_OK; return CR_OK;
} }

@ -103,7 +103,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
WARN(manager).print(" has %d access\n", access); WARN(manager).print(" has %d access\n", access);
cavein_possible = config.riskaverse; cavein_possible = config.riskaverse;
cavein_candidates.emplace(pos, access); cavein_candidates.emplace(pos, access);
least_access = min(access, least_access); least_access = std::min(access, least_access);
} }
} else if (config.insta_dig && isEntombed(miner_pos, pos)) { } else if (config.insta_dig && isEntombed(miner_pos, pos)) {
manage_one(pos, true, false); manage_one(pos, true, false);
@ -141,7 +141,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
for (df::block_square_event* event: block->block_events) { for (df::block_square_event* event: block->block_events) {
if (auto evT = virtual_cast<df::block_square_event_designation_priorityst>(event)) { if (auto evT = virtual_cast<df::block_square_event_designation_priorityst>(event)) {
// we want to let the user keep some designations free of being managed // 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); auto v = 1000 + (b * 1700);
DEBUG(manager).print("(" COORD ") 1000+1000(%d) -> %d {least-access: %d}\n",COORDARGS(pos), b, v, least_access); DEBUG(manager).print("(" COORD ") 1000+1000(%d) -> %d {least-access: %d}\n",COORDARGS(pos), b, v, least_access);
evT->priority[Coord(local)] = v; evT->priority[Coord(local)] = v;

@ -23,7 +23,7 @@ namespace CSP {
inline uint32_t calc_distance(df::coord p1, df::coord p2) { inline uint32_t calc_distance(df::coord p1, df::coord p2) {
// calculate chebyshev (chessboard) distance // calculate chebyshev (chessboard) distance
uint32_t distance = abs(p2.z - p1.z); 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; return distance;
} }

@ -411,7 +411,7 @@ public:
Screen::paintTile(corner_ur, x2, y1); Screen::paintTile(corner_ur, x2, y1);
Screen::paintTile(corner_dl, x1, y2); Screen::paintTile(corner_dl, x1, y2);
Screen::paintTile(corner_dr, x2, y2); Screen::paintTile(corner_dr, x2, y2);
string title = " " + get_title() + " "; string title = ' ' + get_title() + ' ';
Screen::paintString(Screen::Pen(' ', COLOR_DARKGREY, COLOR_BLACK), Screen::paintString(Screen::Pen(' ', COLOR_DARKGREY, COLOR_BLACK),
x2 - 6, y1, "DFHack"); x2 - 6, y1, "DFHack");
Screen::paintString(Screen::Pen(' ', COLOR_BLACK, COLOR_GREY), Screen::paintString(Screen::Pen(' ', COLOR_BLACK, COLOR_GREY),

@ -103,7 +103,7 @@ static void find_active_keybindings(color_ostream &out, df::viewscreen *screen,
} }
for (int i = 1; i <= 12; i++) { 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("`"); valid_keys.push_back("`");

@ -124,7 +124,7 @@ local function mouse_is_over_resume_button(rect)
end end
function InspectorOverlay:onInput(keys) 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 return false
end end
if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then 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 end
function InspectorOverlay:render(dc) 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 return
end end
if reset_inspector_flag then if reset_inspector_flag then

@ -72,7 +72,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
// provide a configuration interface for the plugin // provide a configuration interface for the plugin
commands.push_back(PluginCommand( commands.push_back(PluginCommand(
plugin_name, plugin_name,
"Increase the intensity of negative dwarven thoughts.", "Make citizens more miserable.",
do_command)); do_command));
return CR_OK; return CR_OK;

@ -76,7 +76,7 @@ struct viewscreen_overlay : T {
[&](lua_State *L) { [&](lua_State *L) {
Lua::Push(L, T::_identity.getName()); Lua::Push(L, T::_identity.getName());
Lua::Push(L, this); Lua::Push(L, this);
Lua::PushInterfaceKeys(L, Screen::add_text_keys(*input)); Lua::PushInterfaceKeys(L, Screen::normalize_text_keys(*input));
}, [&](lua_State *L) { }, [&](lua_State *L) {
input_is_handled = lua_toboolean(L, -1); input_is_handled = lua_toboolean(L, -1);
}); });

@ -1 +1 @@
Subproject commit d7fa20079e89cc6516a0f5406a5ad112436066bb Subproject commit 367d602a2949ab7121e2d9233c29a7fb1b9e6bec

@ -588,7 +588,7 @@ static int do_cycle(color_ostream &out);
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) { DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
DEBUG(config,out).print("initializing %s\n", plugin_name); DEBUG(config,out).print("initializing %s\n", plugin_name);
tailor_instance = dts::make_unique<Tailor>(); tailor_instance = std::make_unique<Tailor>();
// provide a configuration interface for the plugin // provide a configuration interface for the plugin
commands.push_back(PluginCommand( commands.push_back(PluginCommand(

@ -1 +1 @@
Subproject commit ee8217978d25bc8f9c3efa21156dd8ff99896f68 Subproject commit fafe4677cd26d1915c417828368b6fe94e2caa95