Merge branch 'develop' of https://github.com/DFHack/dfhack into remote_reader

# Conflicts:
#	docs/changelog.txt
develop
JapaMala 2018-07-08 19:23:56 +05:30
commit 4b64d2dec6
120 changed files with 2698 additions and 7924 deletions

3
.gitmodules vendored

@ -13,3 +13,6 @@
[submodule "scripts2"]
path = scripts
url = ../../DFHack/scripts.git
[submodule "depends/jsoncpp"]
path = depends/jsoncpp-sub
url = ../../open-source-parsers/jsoncpp.git

@ -17,7 +17,7 @@ default_flags = [
'-I','depends/protobuf',
'-I','depends/lua/include',
'-I','depends/md5',
'-I','depends/jsoncpp',
'-I','depends/jsoncpp/include',
'-I','depends/tinyxml',
'-I','depends/tthread',
'-I','depends/clsocket/src',

@ -11,6 +11,7 @@ else(CMAKE_CONFIGURATION_TYPES)
endif(CMAKE_CONFIGURATION_TYPES)
OPTION(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF)
OPTION(REMOVE_SYMBOLS_FROM_DF_STUBS "Remove debug symbols from DF stubs. (Reduces libdfhack size to about half but removes a few useful symbols)" ON)
## some generic CMake magic
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
@ -136,6 +137,21 @@ ${CMAKE_MODULE_PATH}
# generates compile_commands.json, used for autocompletion by some editors
SET(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include(CheckCXXSourceCompiles)
CHECK_CXX_SOURCE_COMPILES("
#include <cstdlib>
#include <cuchar>
int main(void) {
char32_t in = 0;
char out[MB_CUR_MAX];
std::mbstate_t state{};
std::c32rtomb(out, in, &state);
return 0;
}" HAVE_CUCHAR2)
if(HAVE_CUCHAR2)
add_definitions("-DHAVE_CUCHAR")
endif()
# mixing the build system with the source code is ugly and stupid. enforce the opposite :)
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "In-source builds are not allowed.")
@ -147,9 +163,9 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif()
# set up versioning.
set(DF_VERSION "0.44.10")
set(DFHACK_RELEASE "r1")
set(DFHACK_PRERELEASE FALSE)
set(DF_VERSION "0.44.12")
set(DFHACK_RELEASE "alpha0")
set(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
@ -348,7 +364,6 @@ find_package(ZLIB REQUIRED)
include_directories(depends/protobuf)
include_directories(depends/lua/include)
include_directories(depends/md5)
include_directories(depends/jsoncpp)
# Support linking against external tinyxml
# If we find an external tinyxml, set the DFHACK_TINYXML variable to "tinyxml"

@ -9,7 +9,9 @@ if(NOT TinyXML_FOUND)
endif()
add_subdirectory(tthread)
add_subdirectory(jsoncpp)
OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" OFF)
OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" OFF)
add_subdirectory(jsoncpp-sub)
# build clsocket static and only as a dependency. Setting those options here overrides its own default settings.
OPTION(CLSOCKET_SHARED "Build clsocket lib as shared." OFF)
OPTION(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON)

@ -0,0 +1 @@
Subproject commit ddabf50f72cf369bf652a95c4d9fe31a1865a781

@ -1,2 +0,0 @@
PROJECT(jsoncpp)
ADD_LIBRARY(jsoncpp STATIC jsoncpp.cpp)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -19,10 +19,11 @@ Antalia tamarakorr
Anuradha Dissanayake falconne
Atkana Atkana
AtomicChicken AtomicChicken
Bearskie Bearskie
belal jimhester
Ben Lubar BenLubar
Ben Rosser TC01
brndd brndd
brndd brndd burneddi
Bumber Bumber64
Caldfir caldfir
Carter Bray Qartar
@ -58,6 +59,7 @@ jj jjyg jj``
John Beisley huin
John Shade gsvslto
Jonas Ask
Josh Cooper cppcooper coope
kane-t kane-t
Kelly Kinkade ab9rf
Kris Parker kaypy

@ -74,6 +74,11 @@ Note that the ``:foo`` syntax for whitespace in arguments is not compatible \
with '+ args'.
.. _dfhack-run:
dfhack-run
..........
If DF and DFHack are already running, calling ``dfhack-run my command``
in an external terminal is equivalent to calling ``my command`` in the
DFHack console. Direct use of the DFhack console is generally easier,
@ -90,10 +95,10 @@ but ``dfhack-run`` can be useful in a variety of circumstances:
Examples::
./dfhack-run cursecheck
dfhack-run multicmd kill-lua; die
dfhack-run kill-lua
The first (\*nix) example `checks for vampires <cursecheck>`; the
second (Windows) example uses `kill-lua` to cancel a script and exits.
second (Windows) example uses `kill-lua` to stop a Lua script.
Built-in Commands

@ -2404,6 +2404,10 @@ environment by the mandatory init file dfhack.lua:
If the argument is a lua table or DF object reference, prints all fields.
* ``printall_recurse(obj)``
If the argument is a lua table or DF object reference, prints all fields recursively.
* ``copyall(obj)``
Returns a shallow copy of the table or reference as a lua table.
@ -3517,6 +3521,8 @@ The widget implements:
Same as with an ordinary list.
.. _lua-plugins:
=======
Plugins
=======
@ -3901,6 +3907,127 @@ A class with all the tcp functionality.
Tries connecting to that address and port. Returns ``client`` object.
.. _cxxrandom:
cxxrandom
=========
Exposes some features of the C++11 random number library to Lua.
Native functions (exported to Lua)
----------------------------------
- ``GenerateEngine(seed)``
returns engine id
- ``DestroyEngine(rngID)``
destroys corresponding engine
- ``NewSeed(rngID, seed)``
re-seeds engine
- ``rollInt(rngID, min, max)``
generates random integer
- ``rollDouble(rngID, min, max)``
generates random double
- ``rollNormal(rngID, avg, stddev)``
generates random normal[gaus.]
- ``rollBool(rngID, chance)``
generates random boolean
- ``MakeNumSequence(start, end)``
returns sequence id
- ``AddToSequence(seqID, num)``
adds a number to the sequence
- ``ShuffleSequence(rngID, seqID)``
shuffles the number sequence
- ``NextInSequence(seqID)``
returns the next number in sequence
Lua plugin functions
--------------------
- ``MakeNewEngine(seed)``
returns engine id
Lua plugin classes
------------------
``crng``
~~~~~~~~
- ``init(id, df, dist)``: constructor
- ``id``: Reference ID of engine to use in RNGenerations
- ``df`` (optional): bool indicating whether to destroy the Engine when the crng object is garbage collected
- ``dist`` (optional): lua number distribution to use
- ``changeSeed(seed)``: alters engine's seed value
- ``setNumDistrib(distrib)``: sets the number distribution crng object should use
- ``distrib``: number distribution object to use in RNGenerations
- ``next()``: returns the next number in the distribution
- ``shuffle()``: effectively shuffles the number distribution
``normal_distribution``
~~~~~~~~~~~~~~~~~~~~~~~
- ``init(avg, stddev)``: constructor
- ``next(id)``: returns next number in the distribution
- ``id``: engine ID to pass to native function
``real_distribution``
~~~~~~~~~~~~~~~~~~~~~
- ``init(min, max)``: constructor
- ``next(id)``: returns next number in the distribution
- ``id``: engine ID to pass to native function
``int_distribution``
~~~~~~~~~~~~~~~~~~~~
- ``init(min, max)``: constructor
- ``next(id)``: returns next number in the distribution
- ``id``: engine ID to pass to native function
``bool_distribution``
~~~~~~~~~~~~~~~~~~~~~
- ``init(min, max)``: constructor
- ``next(id)``: returns next boolean in the distribution
- ``id``: engine ID to pass to native function
``num_sequence``
~~~~~~~~~~~~~~~~
- ``init(a, b)``: constructor
- ``add(num)``: adds num to the end of the number sequence
- ``shuffle()``: shuffles the sequence of numbers
- ``next()``: returns next number in the sequence
=======
Scripts

@ -2731,3 +2731,14 @@ can easily result in inconsistent state once this plugin is
available again. The effects may be as weird as negative power
being generated.
=======
Lua API
=======
Some plugins consist solely of native libraries exposed to Lua. They are listed
in the `lua-api` file under `lua-plugins`:
* `eventful`
* `building-hacks`
* `luasocket`
* `cxxrandom`

@ -37,32 +37,146 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
================================================================================
# Future
## Fixes
- `stonesense`: fixed ``PLANT:DESERT_LIME:LEAF`` typo
## API
- Added C++-style linked list interface for DF linked lists
================================================================================
# 0.44.11-beta2.1
## Internals
-@ `stonesense`: fixed build
================================================================================
# 0.44.11-beta2
## Fixes
-@ Windows: Fixed console failing to initialize
- `command-prompt`: added support for commands that require a specific screen to be visible, e.g. `spotclean`
- `gui/workflow`: fixed advanced constraint menu for crafts
## API
- Added ``Screen::Hide`` to temporarily hide screens, like `command-prompt`
================================================================================
# 0.44.11-beta1
## Fixes
- Fixed displayed names (from ``Units::getVisibleName``) for units with identities
- Fixed potential memory leak in ``Screen::show()``
- `fix/dead-units`: fixed script trying to use missing isDiplomat function
## Misc Improvements
- Console:
- added support for multibyte characters on Linux/macOS
- made the console exit properly when an interactive command is active (`liquids`, `mode`, `tiletypes`)
- Made the ``DFHACK_PORT`` environment variable take priority over ``remote-server.json``
- Linux: added automatic support for GCC sanitizers in ``dfhack`` script
- `digfort`: added better map bounds checking
- `dfhack-run`: added support for port specified in ``remote-server.json``, to match DFHack's behavior
- `remove-stress`:
- added support for ``-all`` as an alternative to the existing ``all`` argument for consistency
- sped up significantly
- improved output/error messages
- now removes tantrums, depression, and obliviousness
- `ruby`: sped up handling of onupdate events
## API
- New functions:
- ``Units::isDiplomat(unit)``
- Exposed ``Screen::zoom()`` to C++ (was Lua-only)
## Lua
- Added ``printall_recurse`` to print tables and DF references recursively. It can be also used with ``^`` from the `lua` interpreter.
- ``gui.widgets``: ``List:setChoices`` clones ``choices`` for internal table changes
## Internals
- jsoncpp: updated to version 1.8.4 and switched to using a git submodule
## Structures
- ``history_event_entity_expels_hfst``: added (new in 0.44.11)
- ``history_event_site_surrenderedst``: added (new in 0.44.11)
- ``history_event_type``: added ``SITE_SURRENDERED``, ``ENTITY_EXPELS_HF`` (new in 0.44.11)
- ``syndrome``: identified a few fields
- ``viewscreen_civlistst``: fixed layout and identified many fields
================================================================================
# 0.44.11-alpha1
## Structures
- Dropped 0.44.10 support
- Added support for automatically sizing arrays indexed with an enum
- Removed stale generated CSV files and DT layouts from pre-0.43.05
- ``announcement_type``: new in 0.44.11: ``NEW_HOLDING``, ``NEW_MARKET_LINK``
- ``breath_attack_type``: added ``OTHER``
- ``historical_figure_info.relationships.list``: added ``unk_3a``-``unk_3c`` fields at end
- ``interface_key``: added bindings new in 0.44.11
- ``occupation_type``: new in 0.44.11: ``MESSENGER``
- ``profession``: new in 0.44.11: ``MESSENGER``
- ``ui_sidebar_menus``:
- ``unit.in_squad``: renamed to ``unit.squad_list_opened``, fixed location
- ``unit``: added ``expel_error`` and other unknown fields new in 0.44.11
- ``hospital``: added, new in 0.44.11
- ``num_speech_tokens``, ``unk_17d8``: moved out of ``command_line`` to fix layout on x64
-@ ``viewscreen_civlistst``: added a few new fields (incomplete)
- ``viewscreen_locationsst``: identified ``edit_input``
================================================================================
# 0.44.10-r2
## New Plugins
- `cxxrandom`: exposes some features of the C++11 random number library to Lua
## New Scripts
- `gui/stamper`: allows manipulation of designations by transforms such as translations, reflections, rotations, and inversion
- `add-recipe`: adds unknown crafting recipes to the player's civ
## Fixes
- Fixed many tools incorrectly using the ``dead`` unit flag (they should generally check ``flags2.killed`` instead)
- Fixed many tools passing incorrect arguments to printf-style functions, including a few possible crashes (`changelayer`, `follow`, `forceequip`, `generated-creature-renamer`)
- Fixed ``-g`` flag (GDB) in Linux ``dfhack`` script (particularly on x64)
- Fixed several bugs in Lua scripts found by static analysis (df-luacheck)
- `autochop`, `autodump`, `autogems`, `automelt`, `autotrade`, `buildingplan`, `dwarfmonitor`, `fix-unit-occupancy`, `fortplan`, `stockflow`: fix issues with periodic tasks not working for some time after save/load cycles
- `buildingplan`, `fortplan`: stop running before a world has fully loaded
- `autogems`, `fix-unit-occupancy`: stopped running when a fort isn't loaded (e.g. while embarking)
- `buildingplan`, `fortplan`: stopped running before a world has fully loaded
- `autogems`:
- stop running repeatedly when paused
- fixed crash when furnaces are linked to same stockpiles as jeweler's workshops
- `ban-cooking`: fixed errors introduced by kitchen structure changes in 0.44.10-r1
- `remove-stress`: fixed an error when running on soul-less units (e.g. with ``-all``)
- `revflood`: stopped revealing tiles adjacent to tiles above open space inappropriately
- `dig`: fixed "Inappropriate dig square" announcements if digging job has been posted
- `stockpiles`: ``loadstock`` now sets usable and unusable weapon and armor settings
- `stocks`: stopped listing carried items under stockpiles where they were picked up from
- `deramp`: fixed deramp to find designations that already have jobs posted
- `fixnaked`: fixed errors due to emotion changes in 0.44
- `autounsuspend`: now skips planned buildings
## Misc Improvements
- Added script name to messages produced by ``qerror()`` in Lua scripts
- Fixed an issue in around 30 scripts that could prevent edits to the files (adding valid arguments) from taking effect
- Linux: Added several new options to ``dfhack`` script: ``--remotegdb``, ``--gdbserver``, ``--strace``
- `bodyswap`: improved error handling
- `buildingplan`: added max quality setting
- `caravan`: documented (new in 0.44.10-alpha1)
- `deathcause`: added "slaughtered" to descriptions
- `fix/retrieve-units`: now re-adds units to active list to counteract `fix/dead-units`
- `item-descriptions`: fixed several grammatical errors
- `modtools/create-unit`:
- added quantity argument
- now selects a caste at random if none is specified
- `mousequery`:
- migrated several features from TWBT's fork
- added ability to drag with left/right buttons
- added depth display for TWBT (when multilevel is enabled)
- made shift+click jump to lower levels visible with TWBT
- `title-version`: added version to options screen too
- `remotefortressreader`: added build menu information
- `embark-assistant`:
- changed region interaction matching to search for evil rain, syndrome rain, and reanimation rather than interaction presence (misleadingly called evil weather), reanimation, and thralling
- gave syndrome rain and reanimation wider ranges of criterion values
- `fix/dead-units`: added a delay of around 1 month before removing units
## API
- New functions (also exposed to Lua):
@ -75,10 +189,22 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- Added ``profiler`` module to measure lua performance
- Enabled shift+cursor movement in WorkshopOverlay-derived screens
## Structures
- ``unit_flags1``: renamed ``dead`` to ``inactive`` to better reflect its use
- ``item_body_component``: fixed location of ``corpse_flags``
- ``job_type``: added ``is_designation`` attribute
- ``unit_thought_type``: added ``SawDeadBody`` (new in 0.44.10)
- ``unit_personality``: fixed location of ``current_focus`` and ``undistracted_focus``
- ``incident_sub6_performance``: identified some fields
- ``job_handler``: fixed static array layout
## Internals
- Added fallback for YouCompleteMe database lookup failures (e.g. for newly-created files)
- jsoncpp: fixed constructor with ``long`` on Linux
- Improved efficiency and error handling in ``stl_vsprintf`` and related functions
- Added build option to generate symbols for large generated files containing df-structures metadata
================================================================================
# 0.44.10-r1
## New Scripts
@ -134,6 +260,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- ``viewscreen_kitchenprefst.forbidden``, ``possible``: now a bitfield, ``kitchen_pref_flag``
- ``world_data.feature_map``: added extensive documentation (in XML)
================================================================================
# 0.44.10-beta1
## New Scripts
@ -167,6 +294,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- Added function names to DFHack's NullPointer and InvalidArgument exceptions
- Linux: required plugins to have symbols resolved at link time, for consistency with other platforms
================================================================================
# 0.44.10-alpha1
## New Scripts
@ -174,6 +302,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `gui/autogems`: a configuration UI for the `autogems` plugin
## Fixes
- Fixed uninitialized pointer being returned from ``Gui::getAnyUnit()`` in rare cases
- `autohauler`, `autolabor`, `labormanager`: fixed fencepost error and potential crash
- `dwarfvet`: fixed infinite loop if an animal is not accepted at a hospital
- `liquids`: fixed "range" command to default to 1 for dimensions consistently
- `search`: fixed 4/6 keys in unit screen search
- `view-item-info`: fixed an error with some armor

@ -15,9 +15,9 @@ CHANGELOG_SECTIONS = [
'Removed',
'API',
'Internals',
'Structures',
'Lua',
'Ruby',
'Structures',
]
REPLACEMENTS = {

@ -267,14 +267,15 @@ ADD_CUSTOM_COMMAND(
)
ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
IF(UNIX)
# Don't produce debug info for generated stubs
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
IF(REMOVE_SYMBOLS_FROM_DF_STUBS)
IF(UNIX)
# Don't produce debug info for generated stubs
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
ENDIF()
ENDIF()
# Compilation
@ -365,10 +366,10 @@ IF(APPLE)
SET_TARGET_PROPERTIES(dfhack PROPERTIES SOVERSION 1.0.0)
ENDIF()
TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua jsoncpp dfhack-version ${PROJECT_LIBS})
TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua jsoncpp_lib_static dfhack-version ${PROJECT_LIBS})
SET_TARGET_PROPERTIES(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "")
TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket)
TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket jsoncpp_lib_static)
TARGET_LINK_LIBRARIES(dfhack-run dfhack-client)
if(APPLE)

@ -60,6 +60,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <termios.h>
#include <errno.h>
#include <deque>
#ifdef HAVE_CUCHAR
#include <cuchar>
#else
#include <cwchar>
#endif
// George Vulov for MacOSX
#ifndef __LINUX__
@ -133,6 +138,68 @@ const char * getANSIColor(const int c)
}
}
#ifdef HAVE_CUCHAR
// Use u32string for GCC 6 and later and msvc to allow potable implementation
using u32string = std::u32string;
using std::c32rtomb;
using std::mbrtoc32;
#else
// Fallback for gcc 4 and 5 that don't have cuchar header
// But wchar_t is 4 bytes that is a good fallback implementation
using u32string = std::wstring;
size_t mbrtoc32(u32string::value_type* c,
const char* s,
std::size_t n,
std::mbstate_t* ps)
{
return std::mbrtowc(c, s, n, ps);
}
size_t c32rtomb(char* mb,
u32string::value_type c,
std::mbstate_t* ps)
{
return std::wcrtomb(mb, c, ps);
}
#endif
//! Convert a locale defined multibyte coding to UTF-32 string for easier
//! character processing.
static u32string fromLocaleMB(const std::string& str)
{
u32string rv;
u32string::value_type ch;
size_t pos = 0;
ssize_t sz;
std::mbstate_t state{};
while ((sz = mbrtoc32(&ch,&str[pos], str.size() - pos, &state)) != 0) {
if (sz == -1 || sz == -2)
break;
rv.push_back(ch);
if (sz == -3) /* multi value character */
continue;
pos += sz;
}
return rv;
}
//! Convert a UTF-32 string back to locale defined multibyte coding.
static std::string toLocaleMB(const u32string& wstr)
{
std::stringstream ss{};
char mb[MB_CUR_MAX];
std::mbstate_t state{};
const size_t err = -1;
for (auto ch: wstr) {
size_t sz = c32rtomb(mb, ch, &state);
if (sz == err)
break;
ss.write(mb, sz);
}
return ss.str();
}
namespace DFHack
{
class Private
@ -157,7 +224,7 @@ namespace DFHack
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(exit_pipe[0], &descriptor_set);
int ret = TMP_FAILURE_RETRY(
select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL)
select (std::max(STDIN_FILENO,exit_pipe[0])+1,&descriptor_set, NULL, NULL, NULL)
);
if(ret == -1)
return false;
@ -356,15 +423,15 @@ namespace DFHack
int count;
if (enable_raw() == -1) return 0;
if(state == con_lineedit)
return -1;
return Console::FAILURE;
state = con_lineedit;
count = prompt_loop(lock,ch);
state = con_unclaimed;
disable_raw();
print("\n");
if(count != -1)
if(count > Console::FAILURE)
{
output = raw_buffer;
output = toLocaleMB(raw_buffer);
}
return count;
}
@ -374,9 +441,9 @@ namespace DFHack
struct termios raw;
if (!supported_terminal)
return -1;
return Console::FAILURE;
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
return -1;
return Console::FAILURE;
raw = orig_termios; //modify the original mode
// input modes: no break, no CR to NL, no parity check, no strip char,
@ -398,7 +465,7 @@ namespace DFHack
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
// put terminal in raw mode
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &raw) < 0)
return -1;
return Console::FAILURE;
rawmode = 1;
return 0;
}
@ -414,26 +481,24 @@ namespace DFHack
char seq[64];
int cols = get_columns();
int plen = prompt.size();
const char * buf = raw_buffer.c_str();
int len = raw_buffer.size();
int begin = 0;
int cooked_cursor = raw_cursor;
// Use math! This is silly.
while((plen+cooked_cursor) >= cols)
{
buf++;
len--;
cooked_cursor--;
}
while (plen+len > cols)
if ((plen+cooked_cursor) >= cols)
{
len--;
begin = plen+cooked_cursor-cols-1;
len -= plen+cooked_cursor-cols-1;
cooked_cursor -= plen+cooked_cursor-cols-1;
}
if (plen+len > cols)
len -= plen+len - cols;
std::string mbstr = toLocaleMB(raw_buffer.substr(begin,len));
/* Cursor to left edge */
snprintf(seq,64,"\x1b[1G");
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
/* Write the prompt and the current buffer content */
if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return;
if (::write(STDIN_FILENO,buf,len) == -1) return;
if (::write(STDIN_FILENO,mbstr.c_str(),mbstr.length()) == -1) return;
/* Erase to right */
snprintf(seq,64,"\x1b[0K");
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
@ -453,7 +518,8 @@ namespace DFHack
* initially is just an empty string. */
const std::string empty;
history.add(empty);
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1;
if (::write(fd,prompt.c_str(),prompt.size()) == -1)
return Console::FAILURE;
while(1)
{
unsigned char c;
@ -463,7 +529,7 @@ namespace DFHack
if(!read_char(c))
{
lock->lock();
return -2;
return Console::SHUTDOWN;
}
lock->lock();
/* Only autocomplete when the callback is set. It returns < 0 when
@ -496,8 +562,7 @@ namespace DFHack
history.remove();
return raw_buffer.size();
case 3: // ctrl-c
errno = EAGAIN;
return -1;
return Console::RETRY;
case 127: // backspace
case 8: // ctrl-h
if (raw_cursor > 0 && raw_buffer.size() > 0)
@ -512,7 +577,7 @@ namespace DFHack
if (!read_char(seq[0]))
{
lock->lock();
return -2;
return Console::SHUTDOWN;
}
lock->lock();
if (seq[0] == 'b')
@ -528,7 +593,7 @@ namespace DFHack
if (!read_char(seq[1]))
{
lock->lock();
return -2;
return Console::SHUTDOWN;
}
if (seq[1] == 'D')
{
@ -555,7 +620,7 @@ namespace DFHack
{
/* Update the current history entry before to
* overwrite it with tne next one. */
history[history_index] = raw_buffer;
history[history_index] = toLocaleMB(raw_buffer);
/* Show the new entry */
history_index += (seq[1] == 'A') ? 1 : -1;
if (history_index < 0)
@ -568,7 +633,7 @@ namespace DFHack
history_index = history.size()-1;
break;
}
raw_buffer = history[history_index];
raw_buffer = fromLocaleMB(history[history_index]);
raw_cursor = raw_buffer.size();
prompt_refresh();
}
@ -593,7 +658,7 @@ namespace DFHack
if(!read_char(seq2))
{
lock->lock();
return -2;
return Console::SHUTDOWN;
}
lock->lock();
if (seq[1] == '3' && seq2 == '~' )
@ -609,7 +674,7 @@ namespace DFHack
if (!read_char(seq3[0]) || !read_char(seq3[1]))
{
lock->lock();
return -2;
return Console::SHUTDOWN;
}
if (seq2 == ';')
{
@ -672,15 +737,31 @@ namespace DFHack
default:
if (c >= 32) // Space
{
u32string::value_type c32;
char mb[MB_CUR_MAX];
size_t count = 1;
mb[0] = c;
ssize_t sz;
std::mbstate_t state{};
// Read all bytes belonging to a multi byte
// character starting from the first bye already red
while ((sz = mbrtoc32(&c32,&mb[count-1],1, &state)) < 0) {
if (sz == -1 || sz == -3)
return Console::FAILURE; /* mbrtoc32 error (not valid utf-32 character */
if(!read_char(c))
return Console::SHUTDOWN;
mb[count++] = c;
}
if (raw_buffer.size() == size_t(raw_cursor))
{
raw_buffer.append(1,c);
raw_buffer.append(1,c32);
raw_cursor++;
if (plen+raw_buffer.size() < size_t(get_columns()))
{
/* Avoid a full update of the line in the
* trivial case. */
if (::write(fd,&c,1) == -1) return -1;
if (::write(fd,mb,count) == -1)
return Console::FAILURE;
}
else
{
@ -689,7 +770,7 @@ namespace DFHack
}
else
{
raw_buffer.insert(raw_cursor,1,c);
raw_buffer.insert(raw_cursor,1,c32);
raw_cursor++;
prompt_refresh();
}
@ -712,8 +793,8 @@ namespace DFHack
} state;
bool in_batch;
std::string prompt; // current prompt string
std::string raw_buffer; // current raw mode buffer
std::string yank_buffer; // last text deleted with Ctrl-K/Ctrl-U
u32string raw_buffer; // current raw mode buffer
u32string yank_buffer; // last text deleted with Ctrl-K/Ctrl-U
int raw_cursor; // cursor position in the buffer
// thread exit mechanism
int exit_pipe[2];
@ -730,8 +811,7 @@ Console::Console()
}
Console::~Console()
{
if(inited)
shutdown();
assert(!inited);
if(wlock)
delete wlock;
if(d)
@ -768,11 +848,6 @@ bool Console::shutdown(void)
if(!d)
return true;
lock_guard <recursive_mutex> g(*wlock);
if(d->rawmode)
d->disable_raw();
d->print("\n");
inited = false;
// kill the thing
close(d->exit_pipe[1]);
return true;
}
@ -814,7 +889,7 @@ void Console::add_text(color_value color, const std::string &text)
int Console::get_columns(void)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -1;
int ret = Console::FAILURE;
if(inited)
ret = d->get_columns();
return ret;
@ -823,7 +898,7 @@ int Console::get_columns(void)
int Console::get_rows(void)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -1;
int ret = Console::FAILURE;
if(inited)
ret = d->get_rows();
return ret;
@ -853,9 +928,17 @@ void Console::cursor(bool enable)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{
lock_guard <recursive_mutex> g(*wlock);
int ret = -2;
if(inited)
int ret = Console::SHUTDOWN;
if(inited) {
ret = d->lineedit(prompt,output,wlock,ch);
if (ret == Console::SHUTDOWN) {
// kill the thing
if(d->rawmode)
d->disable_raw();
d->print("\n");
inited = false;
}
}
return ret;
}

@ -285,7 +285,10 @@ namespace DFHack
INPUT_RECORD rec;
DWORD count;
lock->unlock();
ReadConsoleInputA(console_in, &rec, 1, &count);
if (ReadConsoleInputA(console_in, &rec, 1, &count) == 0) {
lock->lock();
return Console::SHUTDOWN;
}
lock->lock();
if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown)
continue;
@ -379,7 +382,7 @@ namespace DFHack
state = con_lineedit;
this->prompt = prompt;
count = prompt_loop(lock, ch);
if(count != -1)
if(count > Console::FAILURE)
output = raw_buffer;
state = con_unclaimed;
print("\n");
@ -588,7 +591,7 @@ void Console::cursor(bool enable)
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
{
wlock->lock();
int ret = -2;
int ret = Console::SHUTDOWN;
if(inited)
ret = d->lineedit(prompt,output,wlock,ch);
wlock->unlock();

@ -77,7 +77,9 @@ using namespace DFHack;
#include <iomanip>
#include <stdlib.h>
#include <fstream>
#include "tinythread.h"
#include <thread>
#include <mutex>
#include <condition_variable>
#include "md5wrapper.h"
#include "SDL_events.h"
@ -86,7 +88,6 @@ using namespace DFHack;
#include <dlfcn.h>
#endif
using namespace tthread;
using namespace df::enums;
using df::global::init;
using df::global::world;
@ -96,48 +97,41 @@ using df::global::world;
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL);
size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder);
struct Core::Cond
{
Cond()
{
predicate = false;
wakeup = new tthread::condition_variable();
}
~Cond()
{
delete wakeup;
}
bool Lock(tthread::mutex * m)
{
while(!predicate)
{
wakeup->wait(*m);
}
predicate = false;
return true;
}
bool Unlock()
{
predicate = true;
wakeup->notify_one();
return true;
namespace DFHack {
struct MainThread {
//! MainThread::suspend keeps the main DF thread suspended from Core::Init to
//! thread exit.
static CoreSuspenderBase& suspend() {
static thread_local CoreSuspenderBase lock(std::defer_lock);
return lock;
}
tthread::condition_variable * wakeup;
bool predicate;
};
}
struct Core::Private
CoreSuspendReleaseMain::CoreSuspendReleaseMain()
{
tthread::mutex AccessMutex;
tthread::mutex StackMutex;
std::stack<Core::Cond*> suspended_tools;
Core::Cond core_cond;
thread::id df_suspend_thread;
int df_suspend_depth;
MainThread::suspend().unlock();
}
Private() {
df_suspend_depth = 0;
}
CoreSuspendReleaseMain::~CoreSuspendReleaseMain()
{
MainThread::suspend().lock();
}
CoreSuspendClaimMain::CoreSuspendClaimMain()
{
MainThread::suspend().lock();
}
CoreSuspendClaimMain::~CoreSuspendClaimMain()
{
MainThread::suspend().unlock();
}
struct Core::Private
{
std::thread iothread;
std::thread hotkeythread;
};
struct CommandDepthCounter
@ -227,9 +221,10 @@ void fHKthread(void * iodata)
cerr << "Hotkey thread has croaked." << endl;
return;
}
while(1)
bool keep_going = true;
while(keep_going)
{
std::string stuff = core->getHotkeyCmd(); // waits on mutex!
std::string stuff = core->getHotkeyCmd(keep_going); // waits on mutex!
if(!stuff.empty())
{
color_ostream_proxy out(core->getConsole());
@ -510,7 +505,7 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
bool Core::addScriptPath(string path, bool search_before)
{
lock_guard<mutex> lock(*script_path_mutex);
lock_guard<mutex> lock(script_path_mutex);
vector<string> &vec = script_paths[search_before ? 0 : 1];
if (std::find(vec.begin(), vec.end(), path) != vec.end())
return false;
@ -522,7 +517,7 @@ bool Core::addScriptPath(string path, bool search_before)
bool Core::removeScriptPath(string path)
{
lock_guard<mutex> lock(*script_path_mutex);
lock_guard<mutex> lock(script_path_mutex);
bool found = false;
for (int i = 0; i < 2; i++)
{
@ -541,7 +536,7 @@ bool Core::removeScriptPath(string path)
void Core::getScriptPaths(std::vector<std::string> *dest)
{
lock_guard<mutex> lock(*script_path_mutex);
lock_guard<mutex> lock(script_path_mutex);
dest->clear();
string df_path = this->p->getPath();
for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it)
@ -1339,7 +1334,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
con.printerr("that is not loaded - try \"load %s\" or check stderr.log\n",
first.c_str());
else if (p->size())
con.printerr("that implements %i commands - see \"ls %s\" for details\n",
con.printerr("that implements %zi commands - see \"ls %s\" for details\n",
p->size(), first.c_str());
else
con.printerr("but does not implement any commands\n");
@ -1449,13 +1444,15 @@ void fIOthread(void * iodata)
while (true)
{
string command = "";
int ret = con.lineedit("[DFHack]# ",command, main_history);
if(ret == -2)
int ret;
while ((ret = con.lineedit("[DFHack]# ",command, main_history))
== Console::RETRY);
if(ret == Console::SHUTDOWN)
{
cerr << "Console is shutting down properly." << endl;
return;
}
else if(ret == -1)
else if(ret == Console::FAILURE)
{
cerr << "Console caught an unspecified error." << endl;
continue;
@ -1480,10 +1477,33 @@ void fIOthread(void * iodata)
}
}
Core::Core()
Core::~Core()
{
d = new Private();
if (MainThread::suspend().owns_lock())
MainThread::suspend().unlock();
if (d->hotkeythread.joinable()) {
std::lock_guard<std::mutex> lock(HotkeyMutex);
hotkey_set = SHUTDOWN;
HotkeyCond.notify_one();
}
if (d->iothread.joinable())
con.shutdown();
delete d;
}
Core::Core() :
d{new Private},
script_path_mutex{},
HotkeyMutex{},
HotkeyCond{},
alias_mutex{},
misc_data_mutex{},
CoreSuspendMutex{},
CoreWakeup{},
ownerThread{},
toolCount{0}
{
// init the console. This must be always the first step!
plug_mgr = 0;
vif = 0;
@ -1494,11 +1514,7 @@ Core::Core()
memset(&(s_mods), 0, sizeof(s_mods));
// set up hotkey capture
hotkey_set = false;
HotkeyMutex = 0;
HotkeyCond = 0;
alias_mutex = 0;
misc_data_mutex = 0;
hotkey_set = NO;
last_world_data_ptr = NULL;
last_local_map_ptr = NULL;
last_pause_state = false;
@ -1508,7 +1524,6 @@ Core::Core()
color_ostream::log_errors_to_stderr = true;
script_path_mutex = new mutex();
};
void Core::fatal (std::string output)
@ -1552,6 +1567,10 @@ bool Core::Init()
if(errorstate)
return false;
// Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown
// Core::Update will temporary unlock when there is any commands queued
MainThread::suspend().lock();
// Re-route stdout and stderr again - DF seems to set up stdout and
// stderr.txt on Windows as of 0.43.05. Also, log before switching files to
// make it obvious what's going on if someone checks the *.txt files.
@ -1642,7 +1661,6 @@ bool Core::Init()
// Init global object pointers
df::global::InitGlobals();
alias_mutex = new recursive_mutex();
cerr << "Initializing Console.\n";
// init the console.
@ -1732,7 +1750,6 @@ bool Core::Init()
}
// create mutex for syncing with interactive tasks
misc_data_mutex=new mutex();
cerr << "Initializing Plugins.\n";
// create plugin manager
plug_mgr = new PluginManager(this);
@ -1741,27 +1758,21 @@ bool Core::Init()
temp->core = this;
temp->plug_mgr = plug_mgr;
HotkeyMutex = new mutex();
HotkeyCond = new condition_variable();
if (!is_text_mode || is_headless)
{
cerr << "Starting IO thread.\n";
// create IO thread
thread * IO = new thread(fIOthread, (void *) temp);
(void)IO;
d->iothread = std::thread{fIOthread, (void*)temp};
}
else
{
cerr << "Starting dfhack.init thread.\n";
thread * init = new thread(fInitthread, (void *) temp);
(void)init;
std::cerr << "Starting dfhack.init thread.\n";
d->iothread = std::thread{fInitthread, (void*)temp};
}
cerr << "Starting DF input capture thread.\n";
// set up hotkey capture
thread * HK = new thread(fHKthread, (void *) temp);
(void)HK;
d->hotkeythread = std::thread(fHKthread, (void *) temp);
screen_window = new Windows::top_level_window();
screen_window->addChild(new Windows::dfhack_dummy(5,10));
started = true;
@ -1775,7 +1786,7 @@ bool Core::Init()
if (df::global::ui_sidebar_menus)
{
vector<string> args;
const string & raw = df::global::ui_sidebar_menus->command_line.raw;
const string & raw = df::global::ui_sidebar_menus->command_line.original;
size_t offset = 0;
while (offset < raw.size())
{
@ -1840,28 +1851,25 @@ bool Core::Init()
bool Core::setHotkeyCmd( std::string cmd )
{
// access command
HotkeyMutex->lock();
{
hotkey_set = true;
hotkey_cmd = cmd;
HotkeyCond->notify_all();
}
HotkeyMutex->unlock();
std::lock_guard<std::mutex> lock(HotkeyMutex);
hotkey_set = SET;
hotkey_cmd = cmd;
HotkeyCond.notify_all();
return true;
}
/// removes the hotkey command and gives it to the caller thread
std::string Core::getHotkeyCmd( void )
std::string Core::getHotkeyCmd( bool &keep_going )
{
string returner;
HotkeyMutex->lock();
while ( ! hotkey_set )
{
HotkeyCond->wait(*HotkeyMutex);
std::unique_lock<std::mutex> lock(HotkeyMutex);
HotkeyCond.wait(lock, [this]() -> bool {return this->hotkey_set;});
if (hotkey_set == SHUTDOWN) {
keep_going = false;
return returner;
}
hotkey_set = false;
hotkey_set = NO;
returner = hotkey_cmd;
hotkey_cmd.clear();
HotkeyMutex->unlock();
return returner;
}
@ -1887,82 +1895,29 @@ void Core::printerr(const char *format, ...)
void Core::RegisterData( void *p, std::string key )
{
misc_data_mutex->lock();
std::lock_guard<std::mutex> lock(misc_data_mutex);
misc_data_map[key] = p;
misc_data_mutex->unlock();
}
void *Core::GetData( std::string key )
{
misc_data_mutex->lock();
std::lock_guard<std::mutex> lock(misc_data_mutex);
std::map<std::string,void*>::iterator it=misc_data_map.find(key);
if ( it != misc_data_map.end() )
{
void *p=it->second;
misc_data_mutex->unlock();
return p;
}
else
{
misc_data_mutex->unlock();
return 0;// or throw an error.
}
}
bool Core::isSuspended(void)
{
lock_guard<mutex> lock(d->AccessMutex);
return (d->df_suspend_depth > 0 && d->df_suspend_thread == this_thread::get_id());
}
void Core::Suspend()
{
auto tid = this_thread::get_id();
// If recursive, just increment the count
{
lock_guard<mutex> lock(d->AccessMutex);
if (d->df_suspend_depth > 0 && d->df_suspend_thread == tid)
{
d->df_suspend_depth++;
return;
}
}
// put the condition on a stack
Core::Cond *nc = new Core::Cond();
{
lock_guard<mutex> lock2(d->StackMutex);
d->suspended_tools.push(nc);
}
// wait until Core::Update() wakes up the tool
{
lock_guard<mutex> lock(d->AccessMutex);
nc->Lock(&d->AccessMutex);
assert(d->df_suspend_depth == 0);
d->df_suspend_thread = tid;
d->df_suspend_depth = 1;
}
}
void Core::Resume()
{
auto tid = this_thread::get_id();
lock_guard<mutex> lock(d->AccessMutex);
assert(d->df_suspend_depth > 0 && d->df_suspend_thread == tid);
(void)tid;
if (--d->df_suspend_depth == 0)
d->core_cond.Unlock();
return ownerThread.load() == std::this_thread::get_id();
}
int Core::TileUpdate()
@ -1973,40 +1928,6 @@ int Core::TileUpdate()
return true;
}
int Core::ClaimSuspend(bool force_base)
{
auto tid = this_thread::get_id();
lock_guard<mutex> lock(d->AccessMutex);
if (force_base || d->df_suspend_depth <= 0)
{
assert(d->df_suspend_depth == 0);
d->df_suspend_thread = tid;
d->df_suspend_depth = 1000000;
return 1000000;
}
else
{
assert(d->df_suspend_thread == tid);
return ++d->df_suspend_depth;
}
}
void Core::DisclaimSuspend(int level)
{
auto tid = this_thread::get_id();
lock_guard<mutex> lock(d->AccessMutex);
assert(d->df_suspend_depth == level && d->df_suspend_thread == tid);
(void)tid;
if (level == 1000000)
d->df_suspend_depth = 0;
else
--d->df_suspend_depth;
}
void Core::doUpdate(color_ostream &out, bool first_update)
{
Lua::Core::Reset(out, "DF code execution");
@ -2109,8 +2030,6 @@ int Core::Update()
// Pretend this thread has suspended the core in the usual way,
// and run various processing hooks.
{
CoreSuspendClaimer suspend(true);
// Initialize the core
bool first_update = false;
@ -2126,27 +2045,9 @@ int Core::Update()
doUpdate(out, first_update);
}
// wake waiting tools
// do not allow more tools to join in while we process stuff here
lock_guard<mutex> lock_stack(d->StackMutex);
while (!d->suspended_tools.empty())
{
Core::Cond * nc = d->suspended_tools.top();
d->suspended_tools.pop();
lock_guard<mutex> lock(d->AccessMutex);
// wake tool
nc->Unlock();
// wait for tool to wake us
d->core_cond.Lock(&d->AccessMutex);
// verify
assert(d->df_suspend_depth == 0);
// destroy condition
delete nc;
// check lua stack depth
Lua::Core::Reset(out, "suspend");
}
// Let all commands run that require CoreSuspender
CoreWakeup.wait(MainThread::suspend(),
[this]() -> bool {return this->toolCount.load() == 0;});
return 0;
};
@ -2360,12 +2261,31 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
handleLoadAndUnloadScripts(out, event);
}
// FIXME: needs to terminate the IO threads and properly dismantle all the machinery involved.
int Core::Shutdown ( void )
{
if(errorstate)
return true;
errorstate = 1;
// Make sure we release main thread if this is called from main thread
if (MainThread::suspend().owns_lock())
MainThread::suspend().unlock();
// Make sure the console thread shutdowns before clean up to avoid any
// unlikely data races.
if (d->iothread.joinable()) {
con.shutdown();
}
if (d->hotkeythread.joinable()) {
std::unique_lock<std::mutex> hot_lock(HotkeyMutex);
hotkey_set = SHUTDOWN;
HotkeyCond.notify_one();
}
d->hotkeythread.join();
d->iothread.join();
CoreSuspendClaimer suspend;
if(plug_mgr)
{
@ -2379,7 +2299,6 @@ int Core::Shutdown ( void )
}
allModules.clear();
memset(&(s_mods), 0, sizeof(s_mods));
con.shutdown();
return -1;
}
@ -2530,7 +2449,7 @@ bool Core::SelectHotkey(int sym, int modifiers)
std::string cmd;
{
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::lock_guard<std::mutex> lock(HotkeyMutex);
// Check the internal keybindings
std::vector<KeyBinding> &bindings = key_bindings[sym];
@ -2629,7 +2548,7 @@ bool Core::ClearKeyBindings(std::string keyspec)
if (!parseKeySpec(keyspec, &sym, &mod, &focus))
return false;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::lock_guard<std::mutex> lock(HotkeyMutex);
std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) {
@ -2668,7 +2587,7 @@ bool Core::AddKeyBinding(std::string keyspec, std::string cmdline)
if (binding.command.empty())
return false;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::lock_guard<std::mutex> lock(HotkeyMutex);
// Don't add duplicates
std::vector<KeyBinding> &bindings = key_bindings[sym];
@ -2692,7 +2611,7 @@ std::vector<std::string> Core::ListKeyBindings(std::string keyspec)
if (!parseKeySpec(keyspec, &sym, &mod, &focus))
return rv;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::lock_guard<std::mutex> lock(HotkeyMutex);
std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) {
@ -2712,7 +2631,7 @@ std::vector<std::string> Core::ListKeyBindings(std::string keyspec)
bool Core::AddAlias(const std::string &name, const std::vector<std::string> &command, bool replace)
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
if (!IsAlias(name) || replace)
{
aliases[name] = command;
@ -2723,7 +2642,7 @@ bool Core::AddAlias(const std::string &name, const std::vector<std::string> &com
bool Core::RemoveAlias(const std::string &name)
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
if (IsAlias(name))
{
aliases.erase(name);
@ -2734,14 +2653,14 @@ bool Core::RemoveAlias(const std::string &name)
bool Core::IsAlias(const std::string &name)
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
return aliases.find(name) != aliases.end();
}
bool Core::RunAlias(color_ostream &out, const std::string &name,
const std::vector<std::string> &parameters, command_result &result)
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
if (!IsAlias(name))
{
return false;
@ -2756,13 +2675,13 @@ bool Core::RunAlias(color_ostream &out, const std::string &name,
std::map<std::string, std::vector<std::string>> Core::ListAliases()
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
return aliases;
}
std::string Core::GetAliasCommand(const std::string &name, const std::string &default_)
{
tthread::lock_guard<tthread::recursive_mutex> lock(*alias_mutex);
std::lock_guard<std::recursive_mutex> lock(alias_mutex);
if (IsAlias(name))
return join_strings(" ", aliases[name]);
else

@ -463,7 +463,7 @@ DFhackCExport char * SDL_GetError(void)
}
static void (*_SDL_SetError)(const char *fmt, ...) = 0;
DFhackCExport void SDL_SetError(const char *fmt, ...)
DFhackCExport void SDL_SetError(const char *fmt, ...) Wformat(printf,1,2)
{
char buf[1024];
va_list args;

@ -1626,6 +1626,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isMale),
WRAPM(Units, isFemale),
WRAPM(Units, isMerchant),
WRAPM(Units, isDiplomat),
WRAPM(Units, isForest),
WRAPM(Units, isMarkedForSlaughter),
WRAPM(Units, isTame),
@ -2242,11 +2243,7 @@ int screen_show(lua_State *L)
df::viewscreen *screen = dfhack_lua_viewscreen::get_pointer(L, 1, true);
bool ok = Screen::show(screen, before);
// If it is a table, get_pointer created a new object. Don't leak it.
if (!ok && lua_istable(L, 1))
delete screen;
bool ok = Screen::show(std::unique_ptr<df::viewscreen>{screen}, before);
lua_pushboolean(L, ok);
return 1;

@ -283,9 +283,6 @@ static int lua_dfhack_is_interactive(lua_State *S)
static int dfhack_lineedit_sync(lua_State *S, Console *pstream)
{
if (!pstream)
return 2;
const char *prompt = luaL_optstring(S, 1, ">> ");
const char *hfile = luaL_optstring(S, 2, NULL);
@ -296,10 +293,16 @@ static int dfhack_lineedit_sync(lua_State *S, Console *pstream)
std::string ret;
int rv = pstream->lineedit(prompt, ret, hist);
if (rv == Console::RETRY)
rv = 0; /* return empty string to lua */
if (rv < 0)
{
lua_pushnil(S);
lua_pushstring(S, "input error");
if (rv == Console::SHUTDOWN)
lua_pushstring(S, "shutdown requested");
else
lua_pushstring(S, "input error");
return 2;
}
else
@ -333,8 +336,11 @@ static int dfhack_lineedit(lua_State *S)
lua_settop(S, 2);
Console *pstream = get_console(S);
if (!pstream)
if (!pstream) {
lua_pushnil(S);
lua_pushstring(S, "no console");
return 2;
}
lua_rawgetp(S, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN);
lua_rawgetp(S, -1, S);
@ -1058,7 +1064,11 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state,
prompt = ">> ";
std::string curline;
con.lineedit(prompt,curline,hist);
while((rv = con.lineedit(prompt,curline,hist)) == Console::RETRY);
if (rv <= Console::FAILURE) {
rv = rv == Console::SHUTDOWN ? LUA_OK : LUA_ERRRUN;
break;
}
hist.add(curline);
{

@ -27,6 +27,7 @@ distribution.
#include <string>
#include <vector>
#include <map>
#include <cinttypes>
#include "MemAccess.h"
#include "Core.h"
@ -1008,7 +1009,7 @@ static int meta_ptr_tostring(lua_State *state)
const char *cname = lua_tostring(state, -1);
if (has_length)
lua_pushstring(state, stl_sprintf("<%s[%llu]: %p>", cname, length, (void*)ptr).c_str());
lua_pushstring(state, stl_sprintf("<%s[%" PRIu64 "]: %p>", cname, length, (void*)ptr).c_str());
else
lua_pushstring(state, stl_sprintf("<%s: %p>", cname, (void*)ptr).c_str());
return 1;

@ -27,6 +27,8 @@ distribution.
#include "MiscUtils.h"
#ifndef LINUX_BUILD
// We don't want min and max macros
#define NOMINMAX
#include <Windows.h>
#else
#include <sys/time.h>
@ -40,6 +42,7 @@ distribution.
#include <sstream>
#include <map>
#include <array>
std::string stl_sprintf(const char *fmt, ...) {
va_list lst;
@ -50,21 +53,25 @@ std::string stl_sprintf(const char *fmt, ...) {
}
std::string stl_vsprintf(const char *fmt, va_list args) {
std::vector<char> buf;
buf.resize(4096);
for (;;) {
va_list args2;
va_copy(args2, args);
int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2);
va_end(args2);
if (rsz < 0)
buf.resize(buf.size()*2);
else if (unsigned(rsz) >= buf.size())
buf.resize(rsz+1);
else
return std::string(&buf[0], rsz);
}
/* Allow small (about single line) strings to be printed into stack memory
* with a call to vsnprintf.
*/
std::array<char,128> buf;
va_list args2;
va_copy(args2, args);
int rsz = vsnprintf(&buf[0], buf.size(), fmt, args2);
va_end(args2);
if (rsz < 0)
return std::string(); /* Error occurred */
if (static_cast<unsigned>(rsz) < buf.size())
return std::string(&buf[0], rsz); /* Whole string fits to a single line buffer */
std::string rv;
// Allocate enough memory for the output and null termination
rv.resize(rsz);
rsz = vsnprintf(&rv[0], rv.size()+1, fmt, args);
if (rsz < static_cast<int>(rv.size()))
rv.resize(std::max(rsz,0));
return rv;
}
bool split_string(std::vector<std::string> *out,

@ -49,7 +49,6 @@ using namespace DFHack;
using namespace std;
#include "tinythread.h"
using namespace tthread;
#include <assert.h>
@ -83,8 +82,8 @@ struct Plugin::RefLock
RefLock()
{
refcount = 0;
wakeup = new condition_variable();
mut = new mutex();
wakeup = new tthread::condition_variable();
mut = new tthread::mutex();
}
~RefLock()
{
@ -119,8 +118,8 @@ struct Plugin::RefLock
wakeup->wait(*mut);
}
}
condition_variable * wakeup;
mutex * mut;
tthread::condition_variable * wakeup;
tthread::mutex * mut;
int refcount;
};
@ -786,8 +785,8 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn)
PluginManager::PluginManager(Core * core) : core(core)
{
plugin_mutex = new recursive_mutex();
cmdlist_mutex = new mutex();
plugin_mutex = new tthread::recursive_mutex();
cmdlist_mutex = new tthread::mutex();
ruby = NULL;
}

@ -55,9 +55,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <memory>
using namespace DFHack;
#include "json/json.h"
#include "tinythread.h"
using namespace DFHack;
using namespace tthread;
using dfproto::CoreTextNotification;
@ -132,14 +133,34 @@ bool readFullBuffer(CSimpleSocket *socket, void *buf, int size)
int RemoteClient::GetDefaultPort()
{
const char *port = getenv("DFHACK_PORT");
if (!port) port = "0";
int port = DEFAULT_PORT;
int portval = atoi(port);
if (portval <= 0)
return 5000;
const char *port_env = getenv("DFHACK_PORT");
if (port_env)
{
int port_val = atoi(port_env);
if (port_val > 0)
port = port_val;
}
else
return portval;
{
for (const char *filename : {"dfhack-config/remote-server.json", "../dfhack-config/remote-server.json"})
{
std::ifstream in_file(filename, std::ios_base::in);
if (in_file)
{
Json::Value config;
in_file >> config;
in_file.close();
if (config.isMember("port")) {
port = config["port"].asInt();
break;
}
}
}
}
return port;
}
bool RemoteClient::connect(int port)
@ -157,7 +178,7 @@ bool RemoteClient::connect(int port)
if (!socket->Open("localhost", port))
{
default_output().printerr("Could not connect to localhost: %d\n", port);
default_output().printerr("Could not connect to localhost:%d\n", port);
return false;
}

@ -58,12 +58,11 @@ POSSIBILITY OF SUCH DAMAGE.
#include <memory>
using namespace DFHack;
#include "json/json.h"
#include "tinythread.h"
using namespace tthread;
#include "jsoncpp.h"
using namespace DFHack;
using namespace tthread;
using dfproto::CoreTextNotification;
using dfproto::CoreTextFragment;
@ -395,11 +394,11 @@ bool ServerMain::listen(int port)
inFile.close();
allow_remote = configJson.get("allow_remote", "false").asBool();
port = configJson.get("port", port).asInt();
}
// rewrite/normalize config file
configJson["allow_remote"] = allow_remote;
configJson["port"] = port;
configJson["port"] = configJson.get("port", RemoteClient::DEFAULT_PORT);
std::ofstream outFile(filename, std::ios_base::trunc);
@ -409,6 +408,7 @@ bool ServerMain::listen(int port)
outFile.close();
}
std::cerr << "Listening on port " << port << (allow_remote ? " (remote enabled)" : "") << std::endl;
if (allow_remote)
{
if (!socket->Listen(NULL, port))

@ -652,8 +652,10 @@ static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn
return CR_OK;
}
CoreService::CoreService() {
suspend_depth = 0;
CoreService::CoreService() :
suspend_depth{0},
coreSuspender{nullptr}
{
// These 2 methods must be first, so that they get id 0 and 1
addMethod("BindMethod", &CoreService::BindMethod, SF_DONT_SUSPEND | SF_ALLOW_REMOTE);
@ -683,8 +685,7 @@ CoreService::CoreService() {
CoreService::~CoreService()
{
while (suspend_depth-- > 0)
Core::getInstance().Resume();
delete coreSuspender;
}
command_result CoreService::BindMethod(color_ostream &stream,
@ -725,7 +726,8 @@ command_result CoreService::RunCommand(color_ostream &stream,
command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt)
{
Core::getInstance().Suspend();
if (suspend_depth == 0)
coreSuspender = new CoreSuspender();
cnt->set_value(++suspend_depth);
return CR_OK;
}
@ -735,8 +737,11 @@ command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage
if (suspend_depth <= 0)
return CR_WRONG_USAGE;
Core::getInstance().Resume();
cnt->set_value(--suspend_depth);
if (suspend_depth == 0) {
delete coreSuspender;
coreSuspender = nullptr;
}
return CR_OK;
}

@ -25,12 +25,14 @@ distribution.
#pragma once
#include "Pragma.h"
#include "Export.h"
#include "Error.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include <exception>
//#include <ostream>
#include <type_traits>
#include <iterator>
namespace DFHack
{
template <typename T = int>
@ -232,4 +234,338 @@ namespace DFHack
m_data[idx] = item;
}
};
template <typename L, typename I>
struct DfLinkedList
{
class iterator;
class const_iterator;
class proxy
{
L *cur;
friend struct DfLinkedList<L, I>;
friend class iterator;
proxy(L *cur) : cur(cur)
{
CHECK_NULL_POINTER(cur);
}
public:
operator I *const &() const
{
return cur->item;
}
I *operator->() const
{
return cur->item;
}
proxy & operator=(I *const & item)
{
if (item)
{
CHECK_INVALID_ARGUMENT(item->dfhack_get_list_link() == nullptr);
item->dfhack_set_list_link(cur);
}
if (cur->item)
{
cur->item->dfhack_set_list_link(nullptr);
}
cur->item = item;
return *this;
}
};
class iterator
{
L *root;
L *cur;
friend struct DfLinkedList<L, I>;
friend class const_iterator;
iterator(L *root, L *cur) : root(root), cur(cur) {}
public:
using difference_type = void;
using value_type = I *;
using pointer = I **;
using reference = proxy;
using iterator_category = std::bidirectional_iterator_tag;
iterator() : root(nullptr), cur(nullptr) {}
iterator(const iterator & other) : root(other.root), cur(other.cur) {}
iterator & operator++()
{
CHECK_NULL_POINTER(root);
CHECK_NULL_POINTER(cur);
cur = cur->next;
return *this;
}
iterator & operator--()
{
CHECK_NULL_POINTER(root);
if (!cur)
{
// find end() - 1
for (cur = root->next; cur && cur->next; cur = cur->next)
{
}
return *this;
}
CHECK_NULL_POINTER(cur);
CHECK_NULL_POINTER(cur->prev);
cur = cur->prev;
return *this;
}
iterator operator++(int)
{
iterator copy(*this);
++*this;
return copy;
}
iterator operator--(int)
{
iterator copy(*this);
--*this;
return copy;
}
iterator & operator=(const iterator & other)
{
root = other.root;
cur = other.cur;
return *this;
}
proxy operator*()
{
CHECK_NULL_POINTER(root);
CHECK_NULL_POINTER(cur);
return proxy(cur);
}
I *const & operator*() const
{
CHECK_NULL_POINTER(root);
CHECK_NULL_POINTER(cur);
return cur->item;
}
operator const_iterator() const
{
return const_iterator(*this);
}
bool operator==(const iterator & other) const
{
return root == other.root && cur == other.cur;
}
bool operator!=(const iterator & other) const
{
return !(*this == other);
}
};
class const_iterator
{
iterator iter;
friend struct DfLinkedList<L, I>;
public:
using difference_type = void;
using value_type = I *;
using pointer = I *const *;
using reference = I *const &;
using iterator_category = std::bidirectional_iterator_tag;
const_iterator(const iterator & iter) : iter(iter) {}
const_iterator(const const_iterator & other) : iter(other.iter) {}
const_iterator & operator++()
{
++iter;
return *this;
}
const_iterator & operator--()
{
--iter;
return *this;
}
const_iterator operator++(int)
{
const_iterator copy(*this);
++iter;
return copy;
}
const_iterator operator--(int)
{
const_iterator copy(*this);
--iter;
return copy;
}
const_iterator & operator=(const const_iterator & other)
{
iter = other.iter;
return *this;
}
I *const & operator*() const
{
return *iter;
}
bool operator==(const const_iterator & other) const
{
return iter == other.iter;
}
bool operator!=(const const_iterator & other) const
{
return iter != other.iter;
}
};
using value_type = I *;
using reference_type = proxy;
using difference_type = void;
using size_type = size_t;
bool empty() const
{
return static_cast<const L *>(this)->next == nullptr;
}
size_t size() const
{
size_t n = 0;
for (value_type const & i : *this)
n++;
return n;
}
iterator begin()
{
return iterator(static_cast<L *>(this), static_cast<L *>(this)->next);
}
const_iterator begin() const
{
return const_iterator(const_cast<DfLinkedList<L, I> *>(this)->begin());
}
const_iterator cbegin() const
{
return begin();
}
iterator end()
{
return iterator(static_cast<L *>(this), nullptr);
}
const_iterator end() const
{
return const_iterator(const_cast<DfLinkedList<L, I> *>(this)->end());
}
const_iterator cend() const
{
return end();
}
iterator erase(const_iterator pos)
{
auto root = static_cast<L *>(this);
CHECK_INVALID_ARGUMENT(pos.iter.root == root);
CHECK_NULL_POINTER(pos.iter.cur);
auto link = pos.iter.cur;
auto next = link->next;
if (link->prev)
{
link->prev->next = link->next;
}
else
{
root->next = link->next;
}
if (link->next)
{
link->next->prev = link->prev;
}
proxy p(link);
p = nullptr;
delete link;
return iterator(root, next);
}
iterator insert(const_iterator pos, I *const & item)
{
auto root = static_cast<L *>(this);
CHECK_INVALID_ARGUMENT(pos.iter.root == root);
auto link = pos.iter.cur;
if (!link || !link->prev)
{
if (!link && root->next)
{
pos--;
return insert_after(pos, item);
}
CHECK_INVALID_ARGUMENT(root->next == link);
push_front(item);
return begin();
}
auto newlink = new L();
newlink->prev = link->prev;
newlink->next = link;
link->prev = newlink;
if (newlink->prev)
{
newlink->prev->next = newlink;
}
else if (link == root->next)
{
root->next = newlink;
}
newlink->item = nullptr;
proxy p(newlink);
p = item;
return iterator(root, newlink);
}
iterator insert_after(const_iterator pos, I *const & item)
{
auto root = static_cast<L *>(this);
CHECK_INVALID_ARGUMENT(pos.iter.root == root);
CHECK_NULL_POINTER(pos.iter.cur);
auto link = pos.iter.cur;
auto next = link->next;
auto newlink = new L();
newlink->prev = link;
newlink->next = next;
link->next = newlink;
if (next)
{
next->prev = newlink;
}
newlink->item = nullptr;
proxy p(newlink);
p= item;
return iterator(root, newlink);
}
void push_front(I *const & item)
{
auto root = static_cast<L *>(this);
auto link = new L();
link->prev = nullptr;
if (root->next)
{
root->next->prev = link;
link->next = root->next;
}
link->item = nullptr;
proxy p(link);
p = item;
root->next = link;
}
};
}

@ -106,12 +106,12 @@ namespace DFHack
virtual ~color_ostream();
/// Print a formatted string, like printf
void print(const char *format, ...);
void vprint(const char *format, va_list args);
void print(const char *format, ...) Wformat(printf,2,3);
void vprint(const char *format, va_list args) Wformat(printf,2,0);
/// Print a formatted string, like printf, in red
void printerr(const char *format, ...);
void vprinterr(const char *format, va_list args);
void printerr(const char *format, ...) Wformat(printf,2,3);
void vprinterr(const char *format, va_list args) Wformat(printf,2,0);
/// Get color
color_value color() { return cur_color; }

@ -26,6 +26,7 @@ distribution.
#include "Pragma.h"
#include "Export.h"
#include "ColorText.h"
#include <atomic>
#include <deque>
#include <fstream>
#include <assert.h>
@ -152,6 +153,12 @@ namespace DFHack
int get_rows(void);
/// beep. maybe?
//void beep (void);
//! \defgroup lineedit_return_values Possible errors from lineedit
//! \{
static constexpr int FAILURE = -1;
static constexpr int SHUTDOWN = -2;
static constexpr int RETRY = -3;
//! \}
/// A simple line edit (raw mode)
int lineedit(const std::string& prompt, std::string& output, CommandHistory & history );
bool isInited (void) { return inited; };
@ -163,6 +170,6 @@ namespace DFHack
private:
Private * d;
tthread::recursive_mutex * wlock;
bool inited;
std::atomic<bool> inited;
};
}

@ -34,6 +34,11 @@ distribution.
#include "Console.h"
#include "modules/Graphic.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include "RemoteClient.h"
#define DFH_MOD_SHIFT 1
@ -42,13 +47,6 @@ distribution.
struct WINDOW;
namespace tthread
{
class mutex;
class condition_variable;
class thread;
}
namespace df
{
struct viewscreen;
@ -65,11 +63,21 @@ namespace DFHack
class PluginManager;
class Core;
class ServerMain;
class CoreSuspender;
namespace Lua { namespace Core {
DFHACK_EXPORT void Reset(color_ostream &out, const char *where);
} }
namespace Windows
{
class df_window;
}
namespace Screen
{
struct Hide;
}
enum state_change_event
{
SC_UNKNOWN = -1,
@ -133,10 +141,6 @@ namespace DFHack
}
/// check if the activity lock is owned by this thread
bool isSuspended(void);
/// try to acquire the activity lock
void Suspend(void);
/// return activity lock
void Resume(void);
/// Is everything OK?
bool isValid(void) { return !errorstate; }
@ -149,7 +153,7 @@ namespace DFHack
/// sets the current hotkey command
bool setHotkeyCmd( std::string cmd );
/// removes the hotkey command and gives it to the caller thread
std::string getHotkeyCmd( void );
std::string getHotkeyCmd( bool &keep_going );
/// adds a named pointer (for later or between plugins)
void RegisterData(void *p,std::string key);
@ -191,8 +195,8 @@ namespace DFHack
DFHack::VersionInfo * vinfo;
DFHack::Windows::df_window * screen_window;
static void print(const char *format, ...);
static void printerr(const char *format, ...);
static void print(const char *format, ...) Wformat(printf,1,2);
static void printerr(const char *format, ...) Wformat(printf,1,2);
PluginManager *getPluginManager() { return plug_mgr; }
@ -202,14 +206,11 @@ namespace DFHack
DFHack::Console con;
Core();
~Core();
struct Private;
Private *d;
friend class CoreSuspendClaimer;
int ClaimSuspend(bool force_base);
void DisclaimSuspend(int level);
bool Init();
int Update (void);
int TileUpdate (void);
@ -246,7 +247,7 @@ namespace DFHack
DFHack::PluginManager * plug_mgr;
std::vector<std::string> script_paths[2];
tthread::mutex *script_path_mutex;
std::mutex script_path_mutex;
// hotkey-related stuff
struct KeyBinding {
@ -260,12 +261,17 @@ namespace DFHack
std::map<int, std::vector<KeyBinding> > key_bindings;
std::map<int, bool> hotkey_states;
std::string hotkey_cmd;
bool hotkey_set;
tthread::mutex * HotkeyMutex;
tthread::condition_variable * HotkeyCond;
enum hotkey_set_t {
NO,
SET,
SHUTDOWN,
};
hotkey_set_t hotkey_set;
std::mutex HotkeyMutex;
std::condition_variable HotkeyCond;
std::map<std::string, std::vector<std::string>> aliases;
tthread::recursive_mutex * alias_mutex;
std::recursive_mutex alias_mutex;
bool SelectHotkey(int key, int modifiers);
@ -273,6 +279,7 @@ namespace DFHack
void *last_world_data_ptr;
// for state change tracking
void *last_local_map_ptr;
friend struct Screen::Hide;
df::viewscreen *top_viewscreen;
bool last_pause_state;
// Very important!
@ -280,35 +287,158 @@ namespace DFHack
// Additional state change scripts
std::vector<StateChangeScript> state_change_scripts;
tthread::mutex * misc_data_mutex;
std::mutex misc_data_mutex;
std::map<std::string,void*> misc_data_map;
/*!
* \defgroup core_suspend CoreSuspender state handling serialization to
* DF memory.
* \sa DFHack::CoreSuspender
* \{
*/
std::recursive_mutex CoreSuspendMutex;
std::condition_variable_any CoreWakeup;
std::atomic<std::thread::id> ownerThread;
std::atomic<size_t> toolCount;
//! \}
friend class CoreService;
friend class ServerConnection;
friend class CoreSuspender;
friend class CoreSuspenderBase;
friend struct CoreSuspendClaimMain;
friend struct CoreSuspendReleaseMain;
ServerMain *server;
};
class CoreSuspender {
Core *core;
class CoreSuspenderBase : protected std::unique_lock<std::recursive_mutex> {
protected:
using parent_t = std::unique_lock<std::recursive_mutex>;
std::thread::id tid;
CoreSuspenderBase(std::defer_lock_t d) : CoreSuspenderBase{&Core::getInstance(), d} {}
CoreSuspenderBase(Core* core, std::defer_lock_t) :
/* Lock the core */
parent_t{core->CoreSuspendMutex,std::defer_lock},
/* Mark this thread to be the core owner */
tid{}
{}
public:
CoreSuspender() : core(&Core::getInstance()) { core->Suspend(); }
CoreSuspender(Core *core) : core(core) { core->Suspend(); }
~CoreSuspender() { core->Resume(); }
void lock()
{
auto& core = Core::getInstance();
parent_t::lock();
tid = core.ownerThread.exchange(std::this_thread::get_id(),
std::memory_order_acquire);
}
void unlock()
{
auto& core = Core::getInstance();
/* Restore core owner to previous value */
core.ownerThread.store(tid, std::memory_order_release);
if (tid == std::thread::id{})
Lua::Core::Reset(core.getConsole(), "suspend");
parent_t::unlock();
}
bool owns_lock() const noexcept
{
return parent_t::owns_lock();
}
~CoreSuspenderBase() {
if (owns_lock())
unlock();
}
friend class MainThread;
};
/** Claims the current thread already has the suspend lock.
* Strictly for use in callbacks from DF.
/*!
* CoreSuspender allows serialization to DF data with std::unique_lock like
* interface. It includes handling for recursive CoreSuspender calls and
* notification to main thread after all queue tools have been handled.
*
* State transitions are:
* - Startup setups Core::SuspendMutex to unlocked states
* - Core::Init locks Core::SuspendMutex until the thread exits or that thread
* calls Core::Shutdown or Core::~Core.
* - Other thread request core suspend by atomic incrementation of Core::toolCount
* and then locking Core::CoreSuspendMutex. After locking CoreSuspendMutex
* success callers exchange their std::thread::id to Core::ownerThread.
* - Core::Update() makes sure that queued tools are run when it calls
* Core::CoreWakup::wait. The wait keeps Core::CoreSuspendMutex unlocked
* and waits until Core::toolCount is reduced back to zero.
* - CoreSuspender::~CoreSuspender() first stores the previous Core::ownerThread
* back. In case of recursive call Core::ownerThread equals tid. If tis is
* zero then we are releasing the recursive_mutex which means suspend
* context is over. It is time to reset lua.
* The last step is to decrement Core::toolCount and wakeup main thread if
* no more tools are queued trying to acquire the
* Core::CoreSuspenderMutex.
*/
class CoreSuspendClaimer {
Core *core;
int level;
class CoreSuspender : public CoreSuspenderBase {
using parent_t = CoreSuspenderBase;
public:
CoreSuspendClaimer(bool base = false) : core(&Core::getInstance()) {
level = core->ClaimSuspend(base);
CoreSuspender() : CoreSuspender{&Core::getInstance()} { }
CoreSuspender(std::defer_lock_t d) : CoreSuspender{&Core::getInstance(),d} { }
CoreSuspender(bool) : CoreSuspender{&Core::getInstance()} { }
CoreSuspender(Core* core, bool) : CoreSuspender{core} { }
CoreSuspender(Core* core) :
CoreSuspenderBase{core, std::defer_lock}
{
lock();
}
CoreSuspendClaimer(Core *core, bool base = false) : core(core) {
level = core->ClaimSuspend(base);
CoreSuspender(Core* core, std::defer_lock_t) :
CoreSuspenderBase{core, std::defer_lock}
{}
void lock()
{
auto& core = Core::getInstance();
core.toolCount.fetch_add(1, std::memory_order_relaxed);
parent_t::lock();
}
void unlock()
{
auto& core = Core::getInstance();
parent_t::unlock();
/* Notify core to continue when all queued tools have completed
* 0 = None wants to own the core
* 1+ = There are tools waiting core access
* fetch_add returns old value before subtraction
*/
if (core.toolCount.fetch_add(-1, std::memory_order_relaxed) == 1)
core.CoreWakeup.notify_one();
}
~CoreSuspendClaimer() { core->DisclaimSuspend(level); }
~CoreSuspender() {
if (owns_lock())
unlock();
}
};
/*!
* Temporary release main thread ownership to allow alternative thread
* implement DF logic thread loop
*/
struct DFHACK_EXPORT CoreSuspendReleaseMain {
CoreSuspendReleaseMain();
~CoreSuspendReleaseMain();
};
/*!
* Temporary claim main thread ownership. This allows caller to call
* Core::Update from a different thread than original DF logic thread if
* logic thread has released main thread ownership with
* CoreSuspendReleaseMain
*/
struct DFHACK_EXPORT CoreSuspendClaimMain {
CoreSuspendClaimMain();
~CoreSuspendClaimMain();
};
using CoreSuspendClaimer = CoreSuspender;
}

@ -439,6 +439,7 @@ namespace df
using DFHack::enum_list_attr;
using DFHack::BitArray;
using DFHack::DfArray;
using DFHack::DfLinkedList;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"

@ -58,3 +58,14 @@ distribution.
#define DFhackCExport extern "C" __declspec(dllexport)
#define DFhackDataExport extern "C" __declspec(dllexport)
#endif
// Make gcc warn if types and format string don't match for printf
#ifdef __GNUC__
//! Tell GCC about format functions to allow parameter strict type checks
//! \param type The type of function can be printf, scanf, strftime or strfmon
//! \param fmtstr One based position index for format parameter
//! \param vararg One based position index for the first checked parameter
#define Wformat(type, fmtstr, vararg) __attribute__ ((format (type, fmtstr, vararg)))
#else
#define Wformat(type, fmtstr, vararg)
#endif

@ -395,7 +395,7 @@ namespace DFHack {namespace Lua {
// Not exported; for use by the Core class
bool Init(color_ostream &out);
void Reset(color_ostream &out, const char *where);
DFHACK_EXPORT void Reset(color_ostream &out, const char *where);
// Events signalled by the core
void onStateChange(color_ostream &out, int code);

@ -31,6 +31,7 @@ distribution.
#include <vector>
#include <sstream>
#include <cstdio>
#include <memory>
using std::ostream;
using std::stringstream;
@ -44,6 +45,28 @@ using std::endl;
#define DFHACK_FUNCTION_SIG __func__
#endif
/*! \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>
void print_bits ( T val, ostream& out )
{
@ -354,8 +377,8 @@ DFHACK_EXPORT int random_int(int max);
*/
DFHACK_EXPORT uint64_t GetTimeMs64();
DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...);
DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args);
DFHACK_EXPORT std::string stl_sprintf(const char *fmt, ...) Wformat(printf,1,2);
DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args) Wformat(printf,1,0);
// Conversion between CP437 and UTF-8
DFHACK_EXPORT std::string UTF2DF(const std::string &in);

@ -233,6 +233,7 @@ namespace DFHack
RemoteClient(color_ostream *default_output = NULL);
~RemoteClient();
static constexpr int DEFAULT_PORT = 5000;
static int GetDefaultPort();
color_ostream &default_output() { return *p_default_output; };

@ -133,6 +133,7 @@ namespace DFHack
class CoreService : public RPCService {
int suspend_depth;
CoreSuspender* coreSuspender;
static int doRunLuaFunction(lua_State *L);
public:

@ -1,4 +1,4 @@
#include "jsoncpp.h"
#include "json/json.h"
#pragma once
namespace Json {
@ -24,8 +24,10 @@ namespace Json {
inline std::string toSimpleString (const Json::Value &val)
{
Json::FastWriter w;
return w.write(val);
StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = "\t";
return writeString(builder, val);
}
}

@ -38,6 +38,8 @@ distribution.
#include "df/item.h"
#include "df/inclusion_type.h"
#include <bitset>
namespace df {
struct world_region_details;
}
@ -127,22 +129,22 @@ public:
/// Arbitrary tag field for flood fills etc.
int16_t &tag(df::coord2d p) {
if (!tags) init_tags();
return index_tile<int16_t&>(tags, p);
return index_tile(tags, p);
}
/// Base layer tile type (i.e. layer stone, veins, feature stone)
df::tiletype baseTiletypeAt(df::coord2d p)
{
if (!tiles) init_tiles();
return index_tile<df::tiletype>(tiles->base_tiles,p);
return index_tile(tiles->base_tiles,p);
}
/// Base layer material (i.e. layer stone, veins, feature stone)
t_matpair baseMaterialAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
return t_matpair(
index_tile<int16_t>(basemats->mat_type,p),
index_tile<int16_t>(basemats->mat_index,p)
index_tile(basemats->mat_type,p),
index_tile(basemats->mat_index,p)
);
}
/// Check if the base layer tile is a vein
@ -164,13 +166,13 @@ public:
int16_t veinMaterialAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
return index_tile<int16_t>(basemats->veinmat,p);
return index_tile(basemats->veinmat,p);
}
/// Vein type at pos (even if there is no vein tile)
df::inclusion_type veinTypeAt(df::coord2d p)
{
if (!basemats) init_tiles(true);
return (df::inclusion_type)index_tile<uint8_t>(basemats->veintype,p);
return (df::inclusion_type)index_tile(basemats->veintype,p);
}
/** Sets the vein material at the specified tile position.
@ -207,7 +209,7 @@ public:
{
if (!tiles) init_tiles();
if (tiles->con_info)
return index_tile<df::tiletype>(tiles->con_info->tiles,p);
return index_tile(tiles->con_info->tiles,p);
return baseTiletypeAt(p);
}
/// Static layer material (i.e. base + constructions)
@ -216,8 +218,8 @@ public:
if (!basemats) init_tiles(true);
if (tiles->con_info)
return t_matpair(
index_tile<int16_t>(tiles->con_info->mat_type,p),
index_tile<int16_t>(tiles->con_info->mat_index,p)
index_tile(tiles->con_info->mat_type,p),
index_tile(tiles->con_info->mat_index,p)
);
return baseMaterialAt(p);
}
@ -232,47 +234,50 @@ public:
{
if (!block) return tiletype::Void;
if (tiles)
return index_tile<df::tiletype>(tiles->raw_tiles,p);
return index_tile<df::tiletype>(block->tiletype,p);
return index_tile(tiles->raw_tiles,p);
return index_tile(block->tiletype,p);
}
bool setTiletypeAt(df::coord2d, df::tiletype tt, bool force = false);
uint16_t temperature1At(df::coord2d p)
{
return index_tile<uint16_t>(temp1,p);
return index_tile(temp1,p);
}
bool setTemp1At(df::coord2d p, uint16_t temp)
{
if(!valid) return false;
dirty_temperatures = true;
index_tile<uint16_t&>(temp1,p) = temp;
index_tile(temp1,p) = temp;
return true;
}
uint16_t temperature2At(df::coord2d p)
{
return index_tile<uint16_t>(temp2,p);
return index_tile(temp2,p);
}
bool setTemp2At(df::coord2d p, uint16_t temp)
{
if(!valid) return false;
dirty_temperatures = true;
index_tile<uint16_t&>(temp2,p) = temp;
index_tile(temp2,p) = temp;
return true;
}
df::tile_designation DesignationAt(df::coord2d p)
{
return index_tile<df::tile_designation>(designation,p);
return index_tile(designation,p);
}
bool setDesignationAt(df::coord2d p, df::tile_designation des)
bool setDesignationAt(df::coord2d p, df::tile_designation des, int32_t priority = 4000)
{
if(!valid) return false;
dirty_designations = true;
designated_tiles[(p.x&15) + (p.y&15)*16] = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
index_tile<df::tile_designation&>(designation,p) = des;
if(des.bits.dig && block)
index_tile(designation,p) = des;
if((des.bits.dig || des.bits.smooth) && block) {
block->flags.bits.designated = true;
setPriorityAt(p, priority);
}
return true;
}
@ -281,21 +286,21 @@ public:
df::tile_occupancy OccupancyAt(df::coord2d p)
{
return index_tile<df::tile_occupancy>(occupancy,p);
return index_tile(occupancy,p);
}
bool setOccupancyAt(df::coord2d p, df::tile_occupancy des)
{
if(!valid) return false;
dirty_occupancies = true;
index_tile<df::tile_occupancy&>(occupancy,p) = des;
index_tile(occupancy,p) = des;
return true;
}
bool getFlagAt(df::coord2d p, df::tile_designation::Mask mask) {
return (index_tile<df::tile_designation&>(designation,p).whole & mask) != 0;
return (index_tile(designation,p).whole & mask) != 0;
}
bool getFlagAt(df::coord2d p, df::tile_occupancy::Mask mask) {
return (index_tile<df::tile_occupancy&>(occupancy,p).whole & mask) != 0;
return (index_tile(occupancy,p).whole & mask) != 0;
}
bool setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool set);
bool setFlagAt(df::coord2d p, df::tile_occupancy::Mask mask, bool set);
@ -303,7 +308,7 @@ public:
int itemCountAt(df::coord2d p)
{
if (!item_counts) init_item_counts();
return index_tile<int>(item_counts,p);
return index_tile(item_counts,p);
}
t_blockflags BlockFlags()
@ -316,7 +321,7 @@ public:
int biomeIndexAt(df::coord2d p);
int layerIndexAt(df::coord2d p) {
return index_tile<df::tile_designation&>(designation,p).bits.geolayer_index;
return index_tile(designation,p).bits.geolayer_index;
}
df::coord2d biomeRegionAt(df::coord2d p);
@ -342,13 +347,15 @@ private:
void init();
bool valid;
bool valid:1;
bool dirty_designations:1;
bool dirty_tiles:1;
bool dirty_veins:1;
bool dirty_temperatures:1;
bool dirty_occupancies:1;
std::bitset<16*16> designated_tiles;
DFCoord bcoord;
// Custom tags for floodfill
@ -548,13 +555,11 @@ class DFHACK_EXPORT MapCache
return b ? b->DesignationAt(tilecoord) : df::tile_designation();
}
// priority is optional, only set if >= 0
bool setDesignationAt (DFCoord tilecoord, df::tile_designation des, int32_t priority = -1)
bool setDesignationAt (DFCoord tilecoord, df::tile_designation des, int32_t priority = 4000)
{
if (Block *b = BlockAtTile(tilecoord))
{
if (!b->setDesignationAt(tilecoord, des))
return false;
if (priority >= 0 && b->setPriorityAt(tilecoord, priority))
if (!b->setDesignationAt(tilecoord, des, priority))
return false;
return true;
}
@ -599,15 +604,8 @@ class DFHACK_EXPORT MapCache
return b ? b->removeItemOnGround(item) : false;
}
bool WriteAll()
{
std::map<DFCoord, Block *>::iterator p;
for(p = blocks.begin(); p != blocks.end(); p++)
{
p->second->Write();
}
return true;
}
bool WriteAll();
void trash()
{
std::map<DFCoord, Block *>::iterator p;

@ -167,7 +167,9 @@ typedef uint16_t t_temperatures [16][16];
/**
* Index a tile array by a 2D coordinate, clipping it to mod 16
*/
template<class R, class T> inline R index_tile(T &v, df::coord2d p) {
template<class T> inline auto index_tile(T &v, df::coord2d p)
-> typename std::add_rvalue_reference<decltype(v[0][0])>::type
{
return v[p.x&15][p.y&15];
}

@ -31,10 +31,12 @@ distribution.
#include <string>
#include <set>
#include <memory>
#include "DataDefs.h"
#include "df/graphic.h"
#include "df/viewscreen.h"
#include "df/zoom_commands.h"
#include "modules/GuiHooks.h"
@ -182,6 +184,9 @@ namespace DFHack
return rect2d(df::coord2d(0,0), getWindowSize()-df::coord2d(1,1));
}
/// Wrapper to call enabler->zoom_display from plugins
DFHACK_EXPORT void zoom(df::zoom_commands cmd);
/// Returns the state of [GRAPHICS:YES/NO]
DFHACK_EXPORT bool inGraphicsMode();
@ -210,9 +215,9 @@ namespace DFHack
DFHACK_EXPORT bool findGraphicsTile(const std::string &page, int x, int y, int *ptile, int *pgs = NULL);
// Push and remove viewscreens
DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL, Plugin *p = NULL);
inline bool show(df::viewscreen *screen, Plugin *p)
{ return show(screen, NULL, p); }
DFHACK_EXPORT bool show(std::unique_ptr<df::viewscreen> screen, df::viewscreen *before = NULL, Plugin *p = NULL);
inline bool show(std::unique_ptr<df::viewscreen> screen, Plugin *p)
{ return show(std::move(screen), NULL, p); }
DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false);
DFHACK_EXPORT bool isDismissed(df::viewscreen *screen);
DFHACK_EXPORT bool hasActiveScreens(Plugin *p);
@ -301,6 +306,15 @@ namespace DFHack
GUI_HOOK_DECLARE(set_tile, bool, (const Pen &pen, int x, int y, bool map));
}
//! Temporary hide a screen until destructor is called
struct DFHACK_EXPORT Hide {
Hide(df::viewscreen* screen);
~Hide();
private:
void extract(df::viewscreen*);
void merge(df::viewscreen*);
df::viewscreen* screen_;
};
}
class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen {
@ -374,4 +388,5 @@ namespace DFHack
virtual df::building *getSelectedBuilding();
virtual df::plant *getSelectedPlant();
};
}

@ -139,6 +139,7 @@ DFHACK_EXPORT bool isTamable(df::unit* unit);
DFHACK_EXPORT bool isMale(df::unit* unit);
DFHACK_EXPORT bool isFemale(df::unit* unit);
DFHACK_EXPORT bool isMerchant(df::unit* unit);
DFHACK_EXPORT bool isDiplomat(df::unit* unit);
DFHACK_EXPORT bool isForest(df::unit* unit);
DFHACK_EXPORT bool isMarkedForSlaughter(df::unit* unit);
DFHACK_EXPORT bool isTame(df::unit* unit);

@ -180,6 +180,116 @@ function printall_ipairs(table)
end
end
local do_print_recurse
local function print_string(printfn, v, seen, indent)
local str = tostring(v)
printfn(str)
return #str;
end
local fill_chars = {
__index = function(table, key, value)
local rv = string.rep(' ', 23 - key) .. ' = '
rawset(table, key, rv)
return rv
end,
}
setmetatable(fill_chars, fill_chars)
local function print_fields(value, seen, indent, prefix)
local ok,f,t,k = pcall(pairs,value)
if not ok then
dfhack.print(prefix)
dfhack.println('<Type doesn\'t support iteration with pairs>')
return 0
end
local prev_value = "not a value"
local repeated = 0
for k, v in f,t,k do
-- Only show set values of bitfields
if value._kind ~= "bitfield" or v then
local continue = false
if type(k) == "number" then
if prev_value == v then
repeated = repeated + 1
continue = true
else
prev_value = v
end
else
prev_value = "not a value"
end
if not continue then
if repeated > 0 then
dfhack.println(prefix .. "<Repeated " .. repeated .. " times>")
repeated = 0
end
dfhack.print(prefix)
local len = do_print_recurse(dfhack.print, k, seen, indent + 1)
dfhack.print(fill_chars[len <= 23 and len or 23])
do_print_recurse(dfhack.println, v, seen, indent + 1)
end
end
end
if repeated > 0 then
dfhack.println(prefix .. "<Repeated " .. repeated .. " times>")
end
return 0
end
-- This should be same as print_array but userdata doesn't compare equal even if
-- they hold same pointer.
local function print_userdata(printfn, value, seen, indent)
local prefix = string.rep(' ', indent)
local strvalue = tostring(value)
dfhack.println(strvalue)
if seen[strvalue] then
dfhack.print(prefix)
dfhack.println('<Cyclic reference! Skipping fields>\n')
return 0
end
seen[strvalue] = true
return print_fields(value, seen, indent, prefix)
end
local function print_array(printfn, value, seen, indent)
local prefix = string.rep(' ', indent)
dfhack.println(tostring(value))
if seen[value] then
dfhack.print(prefix)
dfhack.println('<Cyclic reference! skipping fields>\n')
return 0
end
seen[value] = true
return print_fields(value, seen, indent, prefix)
end
local recurse_type_map = {
number = print_string,
string = print_string,
boolean = print_string,
['function'] = print_string,
['nil'] = print_string,
userdata = print_userdata,
table = print_array,
}
do_print_recurse = function(printfn, value, seen, indent)
local t = type(value)
if not recurse_type_map[t] then
printfn("Unknown type " .. t .. " " .. tostring(value))
return
end
return recurse_type_map[t](printfn, value, seen, indent)
end
function printall_recurse(value)
local seen = {}
do_print_recurse(dfhack.println, value, seen, 0)
end
function copyall(table)
local rv = {}
for k,v in pairs(table) do rv[k] = v end
@ -334,6 +444,7 @@ function dfhack.interpreter(prompt,hfile,env)
" '= foo' => '_1,_2,... = foo'\n"..
" '! foo' => 'print(foo)'\n"..
" '~ foo' => 'printall(foo)'\n"..
" '^ foo' => 'printall_recurse(foo)'\n"..
" '@ foo' => 'printall_ipairs(foo)'\n"..
"All of these save the first result as '_'.")
print_banner = false
@ -358,6 +469,9 @@ function dfhack.interpreter(prompt,hfile,env)
print(table.unpack(data,2,data.n))
printall_ipairs(data[2])
end,
['^'] = function(data)
printall_recurse(data[2])
end,
['='] = function(data)
for i=2,data.n do
local varname = '_'..vcnt

@ -456,15 +456,17 @@ function List:init(info)
end
function List:setChoices(choices, selected)
self.choices = choices or {}
self.choices = {}
for i,v in ipairs(self.choices) do
for i,v in ipairs(choices or {}) do
local l = utils.clone(v);
if type(v) ~= 'table' then
v = { text = v }
self.choices[i] = v
l = { text = v }
else
l.text = v.text or v.caption or v[1]
end
v.text = v.text or v.caption or v[1]
parse_label_text(v)
parse_label_text(l)
self.choices[i] = l
end
self:setSelected(selected)

@ -505,17 +505,17 @@ function prompt_yes_no(msg,default)
prompt = prompt..' (y/n)[n]: '
end
while true do
local rv = dfhack.lineedit(prompt)
if rv then
if string.match(rv,'^[Yy]') then
return true
elseif string.match(rv,'^[Nn]') then
return false
elseif rv == 'abort' then
qerror('User abort')
elseif rv == '' and default ~= nil then
return default
end
local rv,err = dfhack.lineedit(prompt)
if not rv then
qerror(err);
elseif string.match(rv,'^[Yy]') then
return true
elseif string.match(rv,'^[Nn]') then
return false
elseif rv == 'abort' then
qerror('User abort')
elseif rv == '' and default ~= nil then
return default
end
end
end
@ -524,7 +524,10 @@ end
function prompt_input(prompt,check,quit_str)
quit_str = quit_str or '~~~'
while true do
local rv = dfhack.lineedit(prompt)
local rv,err = dfhack.lineedit(prompt)
if not rv then
qerror(err);
end
if rv == quit_str then
qerror('User abort')
end

@ -676,7 +676,7 @@ static void manageSyndromeEvent(color_ostream& out) {
for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) {
df::unit* unit = *a;
/*
if ( unit->flags1.bits.dead )
if ( unit->flags1.bits.inactive )
continue;
*/
for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) {
@ -723,7 +723,7 @@ static void manageEquipmentEvent(color_ostream& out) {
itemIdToInventoryItem.clear();
currentlyEquipped.clear();
df::unit* unit = *a;
/*if ( unit->flags1.bits.dead )
/*if ( unit->flags1.bits.inactive )
continue;
*/
@ -952,7 +952,7 @@ static void manageUnitAttackEvent(color_ostream& out) {
}
if ( !wound1 && !wound2 ) {
//if ( unit1->flags1.bits.dead || unit2->flags1.bits.dead )
//if ( unit1->flags1.bits.inactive || unit2->flags1.bits.inactive )
// continue;
if ( reportStr.find("severed part") )
continue;

@ -33,7 +33,7 @@ void Kitchen::debug_print(color_ostream &out)
out.print("Kitchen Exclusions\n");
for(std::size_t i = 0; i < size(); ++i)
{
out.print("%2u: IT:%2i IS:%i MT:%3i MI:%2i ET:%i %s\n",
out.print("%2zu: IT:%2i IS:%i MT:%3i MI:%2i ET:%i %s\n",
i,
ui->kitchen.item_types[i],
ui->kitchen.item_subtypes[i],

@ -45,6 +45,7 @@ using namespace std;
#include "modules/Buildings.h"
#include "modules/MapCache.h"
#include "modules/Maps.h"
#include "modules/Job.h"
#include "modules/Materials.h"
#include "df/block_burrow.h"
@ -57,6 +58,7 @@ using namespace std;
#include "df/burrow.h"
#include "df/feature_init.h"
#include "df/flow_info.h"
#include "df/job.h"
#include "df/plant.h"
#include "df/plant_tree_info.h"
#include "df/plant_tree_tile.h"
@ -91,7 +93,9 @@ const BiomeInfo MapCache::biome_stub = {
#define COPY(a,b) memcpy(&a,&b,sizeof(a))
MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) :
parent(parent),
designated_tiles{}
{
dirty_designations = false;
dirty_tiles = false;
@ -232,7 +236,7 @@ MapExtras::Block::BasematInfo::BasematInfo()
bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_designation::Mask mask, bool set)
{
if(!valid) return false;
auto &val = index_tile<df::tile_designation&>(designation,p);
auto &val = index_tile(designation,p);
bool cur = (val.whole & mask) != 0;
if (cur != set)
{
@ -245,7 +249,7 @@ bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_designation::Mask mask,
bool MapExtras::Block::setFlagAt(df::coord2d p, df::tile_occupancy::Mask mask, bool set)
{
if(!valid) return false;
auto &val = index_tile<df::tile_occupancy&>(occupancy,p);
auto &val = index_tile(occupancy,p);
bool cur = (val.whole & mask) != 0;
if (cur != set)
{
@ -1063,7 +1067,7 @@ int MapExtras::Block::biomeIndexAt(df::coord2d p)
if (!block)
return -1;
auto des = index_tile<df::tile_designation>(designation,p);
auto des = index_tile(designation,p);
uint8_t idx = des.bits.biome;
if (idx >= 9)
return -1;
@ -1141,12 +1145,12 @@ bool MapExtras::Block::addItemOnGround(df::item *item)
if (inserted)
{
int &count = index_tile<int&>(item_counts,item->pos);
int &count = index_tile(item_counts,item->pos);
if (count++ == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = true;
index_tile<df::tile_occupancy&>(block->occupancy,item->pos).bits.item = true;
index_tile(occupancy,item->pos).bits.item = true;
index_tile(block->occupancy,item->pos).bits.item = true;
}
}
@ -1166,13 +1170,13 @@ bool MapExtras::Block::removeItemOnGround(df::item *item)
vector_erase_at(block->items, idx);
int &count = index_tile<int&>(item_counts,item->pos);
int &count = index_tile(item_counts,item->pos);
if (--count == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = false;
index_tile(occupancy,item->pos).bits.item = false;
auto &occ = index_tile<df::tile_occupancy&>(block->occupancy,item->pos);
auto &occ = index_tile(block->occupancy,item->pos);
occ.bits.item = false;
@ -1250,6 +1254,38 @@ MapExtras::MapCache::MapCache()
}
}
bool MapExtras::MapCache::WriteAll()
{
auto world = df::global::world;
df::job_list_link* job_link = world->jobs.list.next;
df::job_list_link* next = nullptr;
for (;job_link;job_link = next) {
next = job_link->next;
df::job* job = job_link->item;
df::coord pos = job->pos;
df::coord blockpos(pos.x>>4,pos.y>>4,pos.z);
auto iter = blocks.find(blockpos);
if (iter == blocks.end())
continue;
df::coord2d bpos(pos.x - (blockpos.x<<4),pos.y - (blockpos.y<<4));
auto block = iter->second;
if (!block->designated_tiles.test(bpos.x+bpos.y*16))
continue;
bool is_designed = ENUM_ATTR(job_type,is_designation,job->job_type);
if (!is_designed)
continue;
// Remove designation job. DF will create a new one in the next tick
// processing.
Job::removeJob(job);
}
std::map<DFCoord, Block *>::iterator p;
for(p = blocks.begin(); p != blocks.end(); p++)
{
p->second->Write();
}
return true;
}
MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
{
if(!valid)

@ -504,7 +504,7 @@ df::coord2d Maps::getBlockTileBiomeRgn(df::map_block *block, df::coord2d pos)
if (!block || !world->world_data)
return df::coord2d();
auto des = index_tile<df::tile_designation>(block->designation,pos);
auto des = index_tile(block->designation,pos);
unsigned idx = des.bits.biome;
if (idx < 9)
{
@ -579,8 +579,8 @@ bool Maps::canWalkBetween(df::coord pos1, df::coord pos2)
if (!block1 || !block2)
return false;
auto tile1 = index_tile<uint16_t>(block1->walkable, pos1);
auto tile2 = index_tile<uint16_t>(block2->walkable, pos2);
auto tile1 = index_tile(block1->walkable, pos1);
auto tile2 = index_tile(block2->walkable, pos2);
return tile1 && tile1 == tile2;
}
@ -607,7 +607,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
if ( !block1 || !block2 )
return false;
if ( !index_tile<uint16_t>(block1->walkable,pos1) || !index_tile<uint16_t>(block2->walkable,pos2) ) {
if ( !index_tile(block1->walkable,pos1) || !index_tile(block2->walkable,pos2) ) {
return false;
}
@ -626,7 +626,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
if ( dx == 0 && dy == 0 ) {
//check for forbidden hatches and floors and such
df::tile_building_occ upOcc = index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building;
df::tile_building_occ upOcc = index_tile(block2->occupancy,pos2).bits.building;
if ( upOcc == tile_building_occ::Impassable || upOcc == tile_building_occ::Obstacle || upOcc == tile_building_occ::Floored )
return false;
@ -659,7 +659,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
return false; //unusable ramp
//there has to be an unforbidden hatch above the ramp
if ( index_tile<df::tile_occupancy>(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic )
if ( index_tile(block2->occupancy,pos2).bits.building != tile_building_occ::Dynamic )
return false;
//note that forbidden hatches have Floored occupancy. unforbidden ones have dynamic occupancy
df::building* building = Buildings::findAtTile(pos2);
@ -703,7 +703,7 @@ bool Maps::canStepBetween(df::coord pos1, df::coord pos2)
if ( !blockUp )
return false;
df::tile_building_occ occupancy = index_tile<df::tile_occupancy>(blockUp->occupancy,up).bits.building;
df::tile_building_occ occupancy = index_tile(blockUp->occupancy,up).bits.building;
if ( occupancy == tile_building_occ::Obstacle || occupancy == tile_building_occ::Floored || occupancy == tile_building_occ::Impassable )
return false;
return true;

@ -90,6 +90,10 @@ df::coord2d Screen::getWindowSize()
return df::coord2d(gps->dimx, gps->dimy);
}
void Screen::zoom(df::zoom_commands cmd) {
enabler->zoom_display(cmd);
}
bool Screen::inGraphicsMode()
{
return init && init->display.flag.is_set(init_display_flags::USE_GRAPHICS);
@ -299,7 +303,7 @@ bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *pt
static std::map<df::viewscreen*, Plugin*> plugin_screens;
bool Screen::show(df::viewscreen *screen, df::viewscreen *before, Plugin *plugin)
bool Screen::show(std::unique_ptr<df::viewscreen> screen, df::viewscreen *before, Plugin *plugin)
{
CHECK_NULL_POINTER(screen);
CHECK_INVALID_ARGUMENT(!screen->parent && !screen->child);
@ -316,15 +320,16 @@ bool Screen::show(df::viewscreen *screen, df::viewscreen *before, Plugin *plugin
screen->child = parent->child;
screen->parent = parent;
parent->child = screen;
if (screen->child)
screen->child->parent = screen;
df::viewscreen* s = screen.release();
parent->child = s;
if (s->child)
s->child->parent = s;
if (dfhack_viewscreen::is_instance(screen))
static_cast<dfhack_viewscreen*>(screen)->onShow();
if (dfhack_viewscreen::is_instance(s))
static_cast<dfhack_viewscreen*>(s)->onShow();
if (plugin)
plugin_screens[screen] = plugin;
plugin_screens[s] = plugin;
return true;
}
@ -371,6 +376,42 @@ bool Screen::hasActiveScreens(Plugin *plugin)
return false;
}
namespace DFHack { namespace Screen {
Hide::Hide(df::viewscreen* screen) :
screen_{screen}
{
extract(screen_);
}
Hide::~Hide()
{
if (screen_)
merge(screen_);
}
void Hide::extract(df::viewscreen* a)
{
df::viewscreen* ap = a->parent;
df::viewscreen* ac = a->child;
ap->child = ac;
if (ac) ac->parent = ap;
else Core::getInstance().top_viewscreen = ap;
}
void Hide::merge(df::viewscreen* a)
{
df::viewscreen* ap = a->parent;
df::viewscreen* ac = a->parent->child;
ap->child = a;
a->child = ac;
if (ac) ac->parent = a;
else Core::getInstance().top_viewscreen = a;
}
} }
#ifdef _LINUX
class DFHACK_EXPORT renderer {
unsigned char *screen;

@ -219,15 +219,9 @@ df::language_name *Units::getVisibleName(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
// as of 0.44.11, identity names take precedence over associated histfig names
if (auto identity = getIdentity(unit))
{
auto id_hfig = df::historical_figure::find(identity->histfig_id);
if (id_hfig)
return &id_hfig->name;
return &identity->name;
}
return &unit->name;
}
@ -1476,6 +1470,13 @@ bool Units::isMerchant(df::unit* unit)
return unit->flags1.bits.merchant == 1;
}
bool Units::isDiplomat(df::unit* unit)
{
CHECK_NULL_POINTER(unit);
return unit->flags1.bits.diplomat == 1;
}
bool Units::isForest(df::unit* unit)
{
CHECK_NULL_POINTER(unit);
@ -1579,7 +1580,7 @@ bool Units::isActive(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
return !unit->flags1.bits.dead;
return !unit->flags1.bits.inactive;
}
bool Units::isKilled(df::unit *unit)

@ -1 +1 @@
Subproject commit c3025feb80c6f8e7441ea5dcf4f463a9cf89cbbd
Subproject commit 372993c58851153320f3390119cdd2d97caef9db

@ -61,7 +61,12 @@ fi
export LD_LIBRARY_PATH="./hack/libs:./hack:$LD_LIBRARY_PATH"
PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}./hack/libdfhack.so"
LIB="./hack/libdfhack.so"
LIBSAN=""
if which objdump > /dev/null; then
LIBSAN="$(objdump -p $LIB | sed -n 's/^.*NEEDED.*\(lib[a-z]san[a-z.0-9]*\).*$/\1/p' | head -n1):"
fi
PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}${LIBSAN}${LIB}"
setarch_arch=$(cat hack/dfhack_setarch.txt || printf i386)

@ -589,7 +589,7 @@ bool VeinGenerator::init_biomes()
if (info.geo_index < 0 || !info.geobiome)
{
out.printerr("Biome %d is not defined.\n", i);
out.printerr("Biome %zd is not defined.\n", i);
return false;
}
@ -1567,7 +1567,7 @@ bool VeinGenerator::place_veins(bool verbose)
sort(queue.begin(), queue.end(), vein_cmp);
// Place tiles
out.print("Processing... ", queue.size());
out.print("Processing... (%zu)", queue.size());
for (size_t j = 0; j < queue.size(); j++)
{
@ -1588,7 +1588,7 @@ bool VeinGenerator::place_veins(bool verbose)
out.print("done.");
out.print(
"\nVein layer %d of %d: %s %s (%.2f%%)... ",
"\nVein layer %zu of %zu: %s %s (%.2f%%)... ",
j+1, queue.size(),
MaterialInfo(0,queue[j]->vein.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str(),
@ -1597,7 +1597,7 @@ bool VeinGenerator::place_veins(bool verbose)
}
else
{
out.print("\rVein layer %d of %d... ", j+1, queue.size());
out.print("\rVein layer %zu of %zu... ", j+1, queue.size());
out.flush();
}

@ -214,7 +214,7 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
bool hasConsole = true)
{
using namespace DFHack;
int newWidth = 0, newHeight = 0, newZLevels = 0;
int newWidth = 0, newHeight = 0, newZLevels = 0, rv = 0;
if (end > start + 1)
{
@ -237,7 +237,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str("");
str << "Set range width <" << width << "> ";
con.lineedit(str.str(), command, hist);
while ((rv = con.lineedit(str.str(), command, hist))
== Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_FAILURE;
hist.add(command);
newWidth = command.empty() ? width : atoi(command.c_str());
} else {
@ -251,7 +254,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str("");
str << "Set range height <" << height << "> ";
con.lineedit(str.str(), command, hist);
while ((rv = con.lineedit(str.str(), command, hist))
== Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
hist.add(command);
newHeight = command.empty() ? height : atoi(command.c_str());
} else {
@ -265,7 +271,10 @@ DFHack::command_result parseRectangle(DFHack::color_ostream & out,
str.str("");
str << "Set range z-levels <" << zLevels << "> ";
con.lineedit(str.str(), command, hist);
while ((rv = con.lineedit(str.str(), command, hist))
== Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
hist.add(command);
newZLevels = command.empty() ? zLevels : atoi(command.c_str());
} else {

@ -85,7 +85,7 @@ if (BUILD_SUPPORTED)
#DFHACK_PLUGIN(advtools advtools.cpp)
DFHACK_PLUGIN(autochop autochop.cpp)
DFHACK_PLUGIN(autodump autodump.cpp)
DFHACK_PLUGIN(autogems autogems.cpp LINK_LIBRARIES jsoncpp)
DFHACK_PLUGIN(autogems autogems.cpp LINK_LIBRARIES jsoncpp_lib_static)
DFHACK_PLUGIN(autohauler autohauler.cpp)
DFHACK_PLUGIN(autolabor autolabor.cpp)
DFHACK_PLUGIN(automaterial automaterial.cpp)
@ -105,6 +105,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(confirm confirm.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(createitem createitem.cpp)
DFHACK_PLUGIN(cursecheck cursecheck.cpp)
DFHACK_PLUGIN(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(deramp deramp.cpp)
DFHACK_PLUGIN(dig dig.cpp)
DFHACK_PLUGIN(digFlood digFlood.cpp)
@ -137,7 +138,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(misery misery.cpp)
DFHACK_PLUGIN(mode mode.cpp)
DFHACK_PLUGIN(mousequery mousequery.cpp)
DFHACK_PLUGIN(orders orders.cpp LINK_LIBRARIES jsoncpp)
DFHACK_PLUGIN(orders orders.cpp LINK_LIBRARIES jsoncpp_lib_static)
DFHACK_PLUGIN(pathable pathable.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
DFHACK_PLUGIN(plants plants.cpp)

@ -482,9 +482,9 @@ static void printCompanionHeader(color_ostream &out, size_t i, df::unit *unit)
out << i;
out << ": " << getUnitNameProfession(unit);
if (unit->flags1.bits.dead)
if (Units::isDead(unit))
out << " (DEAD)";
if (unit->flags3.bits.ghostly)
if (Units::isGhost(unit))
out << " (GHOST)";
out << endl;

@ -809,7 +809,7 @@ struct autochop_hook : public df::viewscreen_dwarfmodest
if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C))
{
sendKey(interface_key::LEAVESCREEN);
Screen::show(new ViewscreenAutochop(), plugin_self);
Screen::show(dts::make_unique<ViewscreenAutochop>(), plugin_self);
}
else
{
@ -852,7 +852,7 @@ command_result df_autochop (color_ostream &out, vector <string> & parameters)
return CR_WRONG_USAGE;
}
if (Maps::IsValid())
Screen::show(new ViewscreenAutochop(), plugin_self);
Screen::show(dts::make_unique<ViewscreenAutochop>(), plugin_self);
return CR_OK;
}

@ -222,7 +222,8 @@ void create_jobs() {
}
DFhackCExport command_result plugin_onupdate(color_ostream &out) {
if (running && !World::ReadPauseState() && (world->frame_counter % DELTA_TICKS == 0)) {
if (running && !World::ReadPauseState() && Maps::IsValid() &&
(world->frame_counter % DELTA_TICKS == 0)) {
create_jobs();
}

@ -1497,7 +1497,7 @@ command_result autolabor (color_ostream &out, std::vector <std::string> & parame
if (maximum < minimum || maximum < 0 || minimum < 0)
{
out.printerr("Syntax: autolabor <labor> <minimum> [<maximum>] [<talent pool>]\n", maximum, minimum);
out.printerr("Syntax: autolabor <labor> <minimum> [<maximum>] [<talent pool>], %d > %d\n", maximum, minimum);
return CR_WRONG_USAGE;
}

@ -156,7 +156,7 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
}
else if (input->count(interface_key::CUSTOM_SHIFT_M))
{
Screen::show(new ViewscreenChooseMaterial(planner.getDefaultItemFilterForType(type)), plugin_self);
Screen::show(dts::make_unique<ViewscreenChooseMaterial>(planner.getDefaultItemFilterForType(type)), plugin_self);
}
else if (input->count(interface_key::CUSTOM_Q))
{

@ -295,7 +295,7 @@ static void init_map(color_ostream &out)
active = true;
if (auto_grow && !grow_burrows.empty())
out.print("Auto-growing %d burrows.\n", grow_burrows.size());
out.print("Auto-growing %zu burrows.\n", grow_burrows.size());
}
static void deinit_map(color_ostream &out)

@ -120,12 +120,12 @@ command_result changelayer (color_ostream &out, std::vector <std::string> & para
{
if(parameters[i] == "help" || parameters[i] == "?")
{
out.print(changelayer_help.c_str());
out.print("%s",changelayer_help.c_str());
return CR_OK;
}
if(parameters[i] == "trouble")
{
out.print(changelayer_trouble.c_str());
out.print("%s",changelayer_trouble.c_str());
return CR_OK;
}
if(parameters[i] == "force")

@ -87,7 +87,7 @@ command_result df_cleanowned (color_ostream &out, vector <string> & parameters)
return CR_FAILURE;
}
out.print("Found total %d items.\n", world->items.all.size());
out.print("Found total %zd items.\n", world->items.all.size());
for (std::size_t i=0; i < world->items.all.size(); i++)
{
@ -160,7 +160,7 @@ command_result df_cleanowned (color_ostream &out, vector <string> & parameters)
std::string description;
item->getItemDescription(&description, 0);
out.print(
"0x%x %s (wear %d)",
"0x%p %s (wear %d)",
item,
description.c_str(),
item->getWear()

@ -200,7 +200,10 @@ void viewscreen_commandpromptst::submit()
return;
submitted = true;
prompt_ostream out(this);
Core::getInstance().runCommand(out, get_entry());
{
Screen::Hide hide_guard(this);
Core::getInstance().runCommand(out, get_entry());
}
if(out.empty() && responses.empty())
Screen::dismiss(this);
else
@ -312,7 +315,7 @@ void viewscreen_commandpromptst::feed(std::set<df::interface_key> *events)
command_result show_prompt(color_ostream &out, std::vector <std::string> & parameters)
{
if (Gui::getCurFocus() == "dfhack/commandprompt")
if (Gui::getCurFocus(true) == "dfhack/commandprompt")
{
Screen::dismiss(Gui::getCurViewscreen(true));
return CR_OK;
@ -320,7 +323,7 @@ command_result show_prompt(color_ostream &out, std::vector <std::string> & param
std::string params;
for(size_t i=0;i<parameters.size();i++)
params+=parameters[i]+" ";
Screen::show(new viewscreen_commandpromptst(params), plugin_self);
Screen::show(dts::make_unique<viewscreen_commandpromptst>(params), plugin_self);
return CR_OK;
}
bool hotkey_allow_all(df::viewscreen *top)

@ -160,8 +160,9 @@ command_result cursecheck (color_ostream &out, vector <string> & parameters)
{
df::unit * unit = world->units.all[i];
// filter out all "living" units that are currently removed from play
// don't spam all completely dead creatures if not explicitly wanted
if(unit->flags1.bits.dead && ignoreDead)
if((!Units::isActive(unit) && !Units::isKilled(unit)) || (Units::isKilled(unit) && ignoreDead))
{
continue;
}
@ -217,7 +218,7 @@ command_result cursecheck (color_ostream &out, vector <string> & parameters)
cursetype.c_str(),
// technically most cursed creatures are undead,
// therefore output 'active' if they are not completely dead
unit->flags1.bits.dead ? "deceased" : "active",
unit->flags2.bits.killed ? "deceased" : "active",
unit->flags3.bits.ghostly ? "-ghostly" : "",
missing ? "-missing" : ""
);
@ -239,9 +240,9 @@ command_result cursecheck (color_ostream &out, vector <string> & parameters)
}
if (checkWholeMap)
out.print("Number of cursed creatures on map: %d \n", cursecount);
out.print("Number of cursed creatures on map: %zd \n", cursecount);
else
out.print("Number of cursed creatures on tile: %d \n", cursecount);
out.print("Number of cursed creatures on tile: %zd \n", cursecount);
return CR_OK;
}

@ -0,0 +1,274 @@
/* Plugin for exporting C++11 random number functionality
*Exports functions for random number generation
*Functions:
- seedRNG(seed)
- rollInt(min, max)
- rollDouble(min, max)
- rollNormal(mean, std_deviation)
- rollBool(chance_for_true)
- resetIndexRolls(string, array_length) --String identifies the instance of SimpleNumDistribution to reset
- rollIndex(string, array_length) --String identifies the instance of SimpleNumDistribution to use
--(Shuffles a vector of indices, Next() increments through then reshuffles when end() is reached)
Author: Josh Cooper
Created: Dec. 13 2017
Updated: Dec. 21 2017
*/
#include <random>
#include <chrono>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <cstdint>
#include <cinttypes>
#include <stdio.h>
#include <stdlib.h>
#include "Error.h"
#include "Core.h"
#include "DataFuncs.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
using namespace DFHack;
DFHACK_PLUGIN("cxxrandom");
#define PLUGIN_VERSION 2.0
color_ostream *cout = nullptr;
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
cout = &out;
return CR_OK;
}
DFhackCExport command_result plugin_shutdown (color_ostream &out)
{
return CR_OK;
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
return CR_OK;
}
class EnginesKeeper
{
private:
EnginesKeeper() {}
std::unordered_map<uint16_t, std::mt19937_64> m_engines;
uint16_t counter = 0;
public:
static EnginesKeeper& Instance()
{
static EnginesKeeper instance;
return instance;
}
uint16_t NewEngine( uint64_t seed )
{
std::mt19937_64 engine( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() );
m_engines[++counter] = engine;
return counter;
}
void DestroyEngine( uint16_t id )
{
m_engines.erase( id );
}
void NewSeed( uint16_t id, uint64_t seed )
{
CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() );
m_engines[id].seed( seed != 0 ? seed : std::chrono::system_clock::now().time_since_epoch().count() );
}
std::mt19937_64& RNG( uint16_t id )
{
CHECK_INVALID_ARGUMENT( m_engines.find( id ) != m_engines.end() );
return m_engines[id];
}
};
uint16_t GenerateEngine( uint64_t seed )
{
return EnginesKeeper::Instance().NewEngine( seed );
}
void DestroyEngine( uint16_t id )
{
EnginesKeeper::Instance().DestroyEngine( id );
}
void NewSeed( uint16_t id, uint64_t seed )
{
EnginesKeeper::Instance().NewSeed( id, seed );
}
int rollInt(uint16_t id, int min, int max)
{
std::uniform_int_distribution<int> ND(min, max);
return ND(EnginesKeeper::Instance().RNG(id));
}
double rollDouble(uint16_t id, double min, double max)
{
std::uniform_real_distribution<double> ND(min, max);
return ND(EnginesKeeper::Instance().RNG(id));
}
double rollNormal(uint16_t id, double mean, double stddev)
{
std::normal_distribution<double> ND(mean, stddev);
return ND(EnginesKeeper::Instance().RNG(id));
}
bool rollBool(uint16_t id, float p)
{
std::bernoulli_distribution ND(p);
return ND(EnginesKeeper::Instance().RNG(id));
}
class NumberSequence
{
private:
unsigned short m_position = 0;
std::vector<int64_t> m_numbers;
public:
NumberSequence(){}
NumberSequence( int64_t start, int64_t end )
{
for( int64_t i = start; i <= end; ++i )
{
m_numbers.push_back( i );
}
}
void Add( int64_t num ) { m_numbers.push_back( num ); }
void Reset() { m_numbers.clear(); }
int64_t Next()
{
if(m_position >= m_numbers.size())
{
m_position = 0;
}
return m_numbers[m_position++];
}
void Shuffle( uint16_t id )
{
std::shuffle( std::begin( m_numbers ), std::end( m_numbers ), EnginesKeeper::Instance().RNG( id ) );
}
void Print()
{
char buffer1[256] = {0};
char buffer2[256] = {0};
for( auto v : m_numbers )
{
sprintf( buffer2, "%s%" PRId64, buffer1, v );
sprintf( buffer1, "%s ", buffer2 );
}
cout->print( "%s", buffer1 );
}
};
class SequenceKeeper
{
private:
SequenceKeeper() {}
std::unordered_map<uint16_t, NumberSequence> m_sequences;
uint16_t counter = 0;
public:
static SequenceKeeper& Instance()
{
static SequenceKeeper instance;
return instance;
}
uint16_t MakeNumSequence( int64_t start, int64_t end )
{
m_sequences[++counter] = NumberSequence( start, end );
return counter;
}
uint16_t MakeNumSequence()
{
m_sequences[++counter] = NumberSequence();
return counter;
}
void DestroySequence( uint16_t id )
{
m_sequences.erase( id );
}
void AddToSequence( uint16_t id, int64_t num )
{
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
m_sequences[id].Add( num );
}
void Shuffle( uint16_t id, uint16_t rng_id )
{
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
m_sequences[id].Shuffle( rng_id );
}
int64_t NextInSequence( uint16_t id )
{
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
return m_sequences[id].Next();
}
void PrintSequence( uint16_t id )
{
CHECK_INVALID_ARGUMENT( m_sequences.find( id ) != m_sequences.end() );
auto seq = m_sequences[id];
seq.Print();
}
};
uint16_t MakeNumSequence( int64_t start, int64_t end )
{
if( start == end )
{
return SequenceKeeper::Instance().MakeNumSequence();
}
return SequenceKeeper::Instance().MakeNumSequence( start, end );
}
void DestroyNumSequence( uint16_t id )
{
SequenceKeeper::Instance().DestroySequence( id );
}
void AddToSequence( uint16_t id, int64_t num )
{
SequenceKeeper::Instance().AddToSequence( id, num );
}
void ShuffleSequence( uint16_t rngID, uint16_t id )
{
SequenceKeeper::Instance().Shuffle( id, rngID );
}
int64_t NextInSequence( uint16_t id )
{
return SequenceKeeper::Instance().NextInSequence( id );
}
void DebugSequence( uint16_t id )
{
SequenceKeeper::Instance().PrintSequence( id );
}
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(GenerateEngine),
DFHACK_LUA_FUNCTION(DestroyEngine),
DFHACK_LUA_FUNCTION(NewSeed),
DFHACK_LUA_FUNCTION(rollInt),
DFHACK_LUA_FUNCTION(rollDouble),
DFHACK_LUA_FUNCTION(rollNormal),
DFHACK_LUA_FUNCTION(rollBool),
DFHACK_LUA_FUNCTION(MakeNumSequence),
DFHACK_LUA_FUNCTION(DestroyNumSequence),
DFHACK_LUA_FUNCTION(AddToSequence),
DFHACK_LUA_FUNCTION(ShuffleSequence),
DFHACK_LUA_FUNCTION(NextInSequence),
DFHACK_LUA_FUNCTION(DebugSequence),
DFHACK_LUA_END
};

@ -7,10 +7,12 @@
#include "DataDefs.h"
#include "modules/Maps.h"
#include "modules/Job.h"
#include "TileTypes.h"
#include "df/map_block.h"
#include "df/world.h"
#include "df/job.h"
using std::vector;
using std::string;
@ -20,6 +22,24 @@ using namespace df::enums;
DFHACK_PLUGIN("deramp");
REQUIRE_GLOBAL(world);
static void doDeramp(df::map_block* block, df::map_block* above, int x, int y, df::tiletype oldT)
{
// Current tile is a ramp.
// Set current tile, as accurately as can be expected
df::tiletype newT = findSimilarTileType(oldT, tiletype_shape::FLOOR);
// If no change, skip it (couldn't find a good tile type)
if (oldT == newT)
return;
// Set new tile type, clear designation
block->tiletype[x][y] = newT;
block->designation[x][y].bits.dig = tile_dig_designation::No;
// Check the tile above this one, in case a downward slope needs to be removed.
if ((above) && (tileShape(above->tiletype[x][y]) == tiletype_shape::RAMP_TOP))
above->tiletype[x][y] = tiletype::OpenSpace; // open space
}
command_result df_deramp (color_ostream &out, vector <string> & parameters)
{
if (!parameters.empty())
@ -36,6 +56,23 @@ command_result df_deramp (color_ostream &out, vector <string> & parameters)
int count = 0;
int countbad = 0;
df::job_list_link* next;
for (df::job_list_link* jl = world->jobs.list.next;jl; jl = next) {
next = jl->next;
df::job* job = jl->item;
if (job->job_type != df::job_type::RemoveStairs)
continue;
df::map_block *block = Maps::getTileBlock(job->pos);
df::coord2d bpos = job->pos - block->map_pos;
df::tiletype oldT = block->tiletype[bpos.x][bpos.y];
if (tileShape(oldT) != tiletype_shape::RAMP)
continue;
df::map_block *above = Maps::getTileBlock(job->pos + df::coord(0,0,1));
doDeramp(block, above, bpos.x, bpos.y, oldT);
count++;
Job::removeJob(job);
}
int num_blocks = 0, blocks_total = world->map.map_blocks.size();
for (int i = 0; i < blocks_total; i++)
{
@ -50,20 +87,7 @@ command_result df_deramp (color_ostream &out, vector <string> & parameters)
if ((tileShape(oldT) == tiletype_shape::RAMP) &&
(block->designation[x][y].bits.dig == tile_dig_designation::Default))
{
// Current tile is a ramp.
// Set current tile, as accurately as can be expected
df::tiletype newT = findSimilarTileType(oldT, tiletype_shape::FLOOR);
// If no change, skip it (couldn't find a good tile type)
if (oldT == newT)
continue;
// Set new tile type, clear designation
block->tiletype[x][y] = newT;
block->designation[x][y].bits.dig = tile_dig_designation::No;
// Check the tile above this one, in case a downward slope needs to be removed.
if ((above) && (tileShape(above->tiletype[x][y]) == tiletype_shape::RAMP_TOP))
above->tiletype[x][y] = tiletype::OpenSpace; // open space
doDeramp(block, above, x, y, oldT);
count++;
}
// ramp fixer

@ -113,7 +113,7 @@ command_result eventExample(color_ostream& out, vector<string>& parameters) {
//static int timerCount=0;
//static int timerDenom=0;
void jobInitiated(color_ostream& out, void* job_) {
out.print("Job initiated! 0x%X\n", job_);
out.print("Job initiated! %p\n", job_);
/*
df::job* job = (df::job*)job_;
out.print(" completion_timer = %d\n", job->completion_timer);
@ -124,15 +124,15 @@ void jobInitiated(color_ostream& out, void* job_) {
}
void jobCompleted(color_ostream& out, void* job) {
out.print("Job completed! 0x%X\n", job);
out.print("Job completed! %p\n", job);
}
void timePassed(color_ostream& out, void* ptr) {
out.print("Time: %d\n", (intptr_t)(ptr));
out.print("Time: %zi\n", (intptr_t)(ptr));
}
void unitDeath(color_ostream& out, void* ptr) {
out.print("Death: %d\n", (intptr_t)(ptr));
out.print("Death: %zi\n", (intptr_t)(ptr));
}
void itemCreate(color_ostream& out, void* ptr) {
@ -143,15 +143,15 @@ void itemCreate(color_ostream& out, void* ptr) {
df::item* item = df::global::world->items.all[item_index];
df::item_type type = item->getType();
df::coord pos = item->pos;
out.print("Item created: %d, %s, at (%d,%d,%d)\n", (intptr_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z);
out.print("Item created: %zi, %s, at (%d,%d,%d)\n", (intptr_t)(ptr), ENUM_KEY_STR(item_type, type).c_str(), pos.x, pos.y, pos.z);
}
void building(color_ostream& out, void* ptr) {
out.print("Building created/destroyed: %d\n", (intptr_t)ptr);
out.print("Building created/destroyed: %zi\n", (intptr_t)ptr);
}
void construction(color_ostream& out, void* ptr) {
out.print("Construction created/destroyed: 0x%X\n", ptr);
out.print("Construction created/destroyed: %p\n", ptr);
df::construction* constr = (df::construction*)ptr;
df::coord pos = constr->pos;
out.print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z);
@ -168,7 +168,7 @@ void syndrome(color_ostream& out, void* ptr) {
}
void invasion(color_ostream& out, void* ptr) {
out.print("New invasion! %d\n", (intptr_t)ptr);
out.print("New invasion! %zi\n", (intptr_t)ptr);
}
void unitAttack(color_ostream& out, void* ptr) {

@ -1,3 +1,4 @@
#include <atomic>
#include <vector>
#include <string>
@ -24,13 +25,12 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(ui);
REQUIRE_GLOBAL(world);
//FIXME: possible race conditions with calling kittens from the IO thread and shutdown from Core.
volatile bool shutdown_flag = false;
volatile bool final_flag = true;
bool timering = false;
bool trackmenu_flg = false;
bool trackpos_flg = false;
bool statetrack = false;
std::atomic<bool> shutdown_flag{false};
std::atomic<bool> final_flag{true};
std::atomic<bool> timering{false};
std::atomic<bool> trackmenu_flg{false};
std::atomic<uint8_t> trackpos_flg{0};
std::atomic<uint8_t> statetrack{0};
int32_t last_designation[3] = {-30000, -30000, -30000};
int32_t last_mouse[2] = {-1, -1};
df::ui_sidebar_mode last_menu = df::ui_sidebar_mode::Default;
@ -93,14 +93,12 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
if(timering == true)
if(timering)
{
uint64_t time2 = GetTimeMs64();
// harmless potential data race here...
uint64_t delta = time2-timeLast;
// harmless potential data race here...
timeLast = time2;
out.print("Time delta = %d ms\n", delta);
out.print("Time delta = %d ms\n", int(delta));
}
if(trackmenu_flg)
{
@ -135,30 +133,30 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
command_result trackmenu (color_ostream &out, vector <string> & parameters)
{
if(trackmenu_flg)
bool is_running = trackmenu_flg.exchange(false);
if(is_running)
{
trackmenu_flg = false;
return CR_OK;
}
else
{
trackmenu_flg = true;
is_enabled = true;
last_menu = ui->main.mode;
out.print("Menu: %d\n",last_menu);
trackmenu_flg = true;
return CR_OK;
}
}
command_result trackpos (color_ostream &out, vector <string> & parameters)
{
trackpos_flg = !trackpos_flg;
trackpos_flg.fetch_xor(1);
is_enabled = true;
return CR_OK;
}
command_result trackstate ( color_ostream& out, vector< string >& parameters )
{
statetrack = !statetrack;
statetrack.fetch_xor(1);
return CR_OK;
}
@ -169,10 +167,10 @@ command_result colormods (color_ostream &out, vector <string> & parameters)
for(df::creature_raw* rawlion : vec)
{
df::caste_raw * caste = rawlion->caste[0];
out.print("%s\nCaste addr 0x%x\n",rawlion->creature_id.c_str(), &caste->color_modifiers);
out.print("%s\nCaste addr %p\n",rawlion->creature_id.c_str(), &caste->color_modifiers);
for(size_t j = 0; j < caste->color_modifiers.size();j++)
{
out.print("mod %d: 0x%x\n", j, caste->color_modifiers[j]);
out.print("mod %zd: %p\n", j, caste->color_modifiers[j]);
}
}
return CR_OK;
@ -180,20 +178,19 @@ command_result colormods (color_ostream &out, vector <string> & parameters)
command_result ktimer (color_ostream &out, vector <string> & parameters)
{
if(timering)
bool is_running = timering.exchange(false);
if(is_running)
{
timering = false;
return CR_OK;
}
uint64_t timestart = GetTimeMs64();
{
CoreSuspender suspend;
uint64_t timeend = GetTimeMs64();
timeLast = timeend;
timering = true;
out.print("Time to suspend = %d ms\n", int(timeend - timestart));
}
uint64_t timeend = GetTimeMs64();
out.print("Time to suspend = %d ms\n",timeend - timestart);
// harmless potential data race here...
timeLast = timeend;
timering = true;
is_enabled = true;
return CR_OK;
}
@ -255,7 +252,7 @@ command_result kittens (color_ostream &out, vector <string> & parameters)
Console::color_value color = COLOR_BLUE;
while(1)
{
if(shutdown_flag)
if(shutdown_flag || !con.isInited())
{
final_flag = true;
con.reset_color();

@ -188,7 +188,7 @@ command_result memview (color_ostream &out, vector <string> & parameters)
isValid=true;
if(!isValid)
{
out.printerr("Invalid address:%x\n",memdata.addr);
out.printerr("Invalid address: %p\n",memdata.addr);
mymutex->unlock();
return CR_OK;
}

@ -51,7 +51,7 @@ command_result df_notes (color_ostream &con, vector <string> & parameters)
{
t_note* note = (*note_list)[i];
con.print("Note %x at: %d/%d/%d\n",note, note->x, note->y, note->z);
con.print("Note %p at: %d/%d/%d\n",note, note->x, note->y, note->z);
con.print("Note id: %d\n", note->id);
con.print("Note symbol: '%c'\n", note->symbol);

@ -135,8 +135,8 @@ static void printVec(color_ostream &con, const char* msg, t_vecTriplet *vec,
uintptr_t length = (intptr_t)vec->end - (intptr_t)vec->start;
uintptr_t offset = pos - start;
con.print("%8s offset %06p, addr %010p, start %010p, length %u",
msg, offset, pos, vec->start, length);
con.print("%8s offset 0x%06zx, addr 0x%01zx, start 0x%01zx, length %zi",
msg, offset, pos, intptr_t(vec->start), length);
if (length >= 4 && length % 4 == 0)
{
void *ptr = vec->start;
@ -200,10 +200,10 @@ command_result df_vectors (color_ostream &con, vector <string> & parameters)
// Found the range containing the start
if (!range.isInRange((void *)end))
{
con.print("Scanning %u bytes would read past end of memory "
con.print("Scanning %zi bytes would read past end of memory "
"range.\n", bytes);
size_t diff = end - (intptr_t)range.end;
con.print("Cutting bytes down by %u.\n", diff);
con.print("Cutting bytes down by %zi.\n", diff);
end = (uintptr_t) range.end;
}

@ -7,6 +7,7 @@
#include <map>
#include <vector>
#include "modules/Gui.h"
#include "modules/Screen.h"
#include "modules/World.h"
#include "df/enabler.h"
@ -52,7 +53,7 @@ command_result df_zoom (color_ostream &out, std::vector <std::string> & paramete
return CR_FAILURE;
}
df::zoom_commands cmd = zcmap[parameters[0]];
enabler->zoom_display(cmd);
Screen::zoom(cmd);
if (cmd == df::zoom_commands::zoom_fullscreen)
enabler->fullscreen = !enabler->fullscreen;
return CR_OK;

@ -260,7 +260,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df:
df::world_site::find(df::global::ui->site_id), 0);
if ( out_items.size() != 1 ) {
out.print("%s, %d: wrong size: %d.\n", __FILE__, __LINE__, out_items.size());
out.print("%s, %d: wrong size: %zu.\n", __FILE__, __LINE__, out_items.size());
return -1;
}
out_items[0]->moveToGround(firstInvader->pos.x, firstInvader->pos.y, firstInvader->pos.z);

@ -62,6 +62,7 @@
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <cinttypes>
using namespace std;
@ -301,7 +302,7 @@ command_result diggingInvadersCommand(color_ostream& out, std::vector<std::strin
DigAbilities& abilities = digAbilities[raceString];
df::coord bob = Gui::getCursorPos();
out.print("(%d,%d,%d), (%d,%d,%d): cost = %lld\n", lastDebugEdgeCostPoint.x, lastDebugEdgeCostPoint.y, lastDebugEdgeCostPoint.z, bob.x, bob.y, bob.z, getEdgeCost(out, lastDebugEdgeCostPoint, bob, abilities));
out.print("(%d,%d,%d), (%d,%d,%d): cost = %" PRId64 "\n", lastDebugEdgeCostPoint.x, lastDebugEdgeCostPoint.y, lastDebugEdgeCostPoint.z, bob.x, bob.y, bob.z, getEdgeCost(out, lastDebugEdgeCostPoint, bob, abilities));
lastDebugEdgeCostPoint = bob;
a++;
} else if ( parameters[a] == "now" ) {
@ -389,7 +390,7 @@ void findAndAssignInvasionJob(color_ostream& out, void* tickTime) {
//find all locals and invaders
for ( size_t a = 0; a < world->units.all.size(); a++ ) {
df::unit* unit = world->units.all[a];
if ( unit->flags1.bits.dead )
if ( !Units::isActive(unit) )
continue;
if ( Units::isCitizen(unit) ) {
if ( localPts.find(unit->pos) != localPts.end() )

@ -1062,7 +1062,7 @@ public:
{
df::unit *selected_unit = (selected_column == 1) ? dwarf_activity_column.getFirstSelectedElem() : nullptr;
Screen::dismiss(this);
Screen::show(new ViewscreenDwarfStats(selected_unit), plugin_self);
Screen::show(dts::make_unique<ViewscreenDwarfStats>(selected_unit), plugin_self);
}
else if (input->count(interface_key::CUSTOM_SHIFT_Z))
{
@ -1643,7 +1643,7 @@ public:
{
auto unitscr = df::allocate<df::viewscreen_unitst>();
unitscr->unit = unit;
Screen::show(unitscr);
Screen::show(std::unique_ptr<df::viewscreen>(unitscr));
}
}
else if (input->count(interface_key::CUSTOM_SHIFT_Z))
@ -1737,7 +1737,7 @@ private:
static void open_stats_screen()
{
Screen::show(new ViewscreenFortStats(), plugin_self);
Screen::show(dts::make_unique<ViewscreenFortStats>(), plugin_self);
}
static void add_work_history(df::unit *unit, activity_type type)
@ -1977,12 +1977,12 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector <string> & par
else if (cmd == 's' || cmd == 'S')
{
if(Maps::IsValid())
Screen::show(new ViewscreenFortStats(), plugin_self);
Screen::show(dts::make_unique<ViewscreenFortStats>(), plugin_self);
}
else if (cmd == 'p' || cmd == 'P')
{
if(Maps::IsValid())
Screen::show(new ViewscreenPreferences(), plugin_self);
Screen::show(dts::make_unique<ViewscreenPreferences>(), plugin_self);
}
else if (cmd == 'r' || cmd == 'R')
{

@ -383,7 +383,7 @@ void AnimalHospital::processPatients(color_ostream &out) {
}
// Check to make sure the unit hasn't expired before assigning a job, or if they've been healed
if (!real_unit || real_unit->flags1.bits.dead || !real_unit->health->flags.bits.needs_healthcare) {
if (!real_unit || !Units::isActive(real_unit) || !real_unit->health->flags.bits.needs_healthcare) {
// discharge the patient from the hospital
this->dischargePatient(*patient, out);
return;
@ -582,7 +582,7 @@ void tickHandler(color_ostream& out, void* data) {
/* Now add it to the scratch AHZ */
for (vector<df::building*>::iterator current_hospital = to_be_added.begin(); current_hospital != to_be_added.end(); current_hospital++) {
// Add it to the vector
out.print("Adding new hospital #id at x1 %d y1: %d z: %d\n",
out.print("Adding new hospital #id: %d at x1 %d y1: %d z: %d\n",
(*current_hospital)->id,
(*current_hospital)->x1,
(*current_hospital)->y1,
@ -624,7 +624,7 @@ processUnits:
df::unit* unit = units[a];
/* As hilarious as it would be, lets not treat FB :) */
if ( unit->flags1.bits.dead || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) {
if ( !Units::isActive(unit) || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) {
continue;
}

@ -58,9 +58,15 @@ namespace embark_assist {
int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used
int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used
uint8_t biome_count;
bool evil_weather[10];
bool evil_weather_possible;
bool evil_weather_full;
bool blood_rain[10];
bool blood_rain_possible;
bool blood_rain_full;
bool permanent_syndrome_rain[10];
bool permanent_syndrome_rain_possible;
bool permanent_syndrome_rain_full;
bool temporary_syndrome_rain[10];
bool temporary_syndrome_rain_possible;
bool temporary_syndrome_rain_full;
bool reanimating[10];
bool reanimating_possible;
bool reanimating_full;
@ -204,6 +210,25 @@ namespace embark_assist {
Very_Deep
};
enum class syndrome_rain_ranges : int8_t {
NA = -1,
Any,
Permanent,
Temporary,
Not_Permanent,
None
};
enum class reanimation_ranges : int8_t {
NA = -1,
Both,
Any,
Thralling,
Reanimation,
Not_Thralling,
None
};
/* // Future possible enhancement
enum class freezing_ranges : int8_t {
NA = -1,
@ -232,9 +257,9 @@ namespace embark_assist {
all_present_ranges soil_min_everywhere;
soil_ranges soil_max;
/*freezing_ranges freezing;*/
yes_no_ranges evil_weather; // Will probably blow up with the magic release arcs...
yes_no_ranges reanimation;
yes_no_ranges thralling;
yes_no_ranges blood_rain; // Will probably blow up with the magic release arcs...
syndrome_rain_ranges syndrome_rain;
reanimation_ranges reanimation;
int8_t spire_count_min; // N/A(-1), 0-9
int8_t spire_count_max; // N/A(-1), 0-9
magma_ranges magma_min;

@ -259,7 +259,9 @@ command_result embark_assistant(color_ostream &out, std::vector <std::string> &
for (uint8_t l = 1; l < 10; l++) {
embark_assist::main::state->survey_results[i][k].biome_index[l] = -1;
embark_assist::main::state->survey_results[i][k].biome[l] = -1;
embark_assist::main::state->survey_results[i][k].evil_weather[l] = false;
embark_assist::main::state->survey_results[i][k].blood_rain[l] = false;
embark_assist::main::state->survey_results[i][k].permanent_syndrome_rain[l] = false;
embark_assist::main::state->survey_results[i][k].temporary_syndrome_rain[l] = false;
embark_assist::main::state->survey_results[i][k].reanimating[l] = false;
embark_assist::main::state->survey_results[i][k].thralling[l] = false;
}

@ -46,9 +46,9 @@ namespace embark_assist {
soil_min,
soil_min_everywhere,
soil_max,
evil_weather,
blood_rain,
syndrome_rain,
reanimation,
thralling,
spire_count_min,
spire_count_max,
magma_min,
@ -451,9 +451,7 @@ namespace embark_assist {
case fields::waterfall:
case fields::flat:
case fields::evil_weather:
case fields::reanimation:
case fields::thralling:
case fields::blood_rain:
{
embark_assist::defs::yes_no_ranges k = embark_assist::defs::yes_no_ranges::NA;
while (true) {
@ -576,6 +574,91 @@ namespace embark_assist {
break;
case fields::syndrome_rain:
{
embark_assist::defs::syndrome_rain_ranges k = embark_assist::defs::syndrome_rain_ranges::NA;
while (true) {
switch (k) {
case embark_assist::defs::syndrome_rain_ranges::NA:
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::syndrome_rain_ranges::Any:
element->list.push_back({ "Any Syndrome", static_cast<int8_t>(k) });
break;
case embark_assist::defs::syndrome_rain_ranges::Permanent:
element->list.push_back({ "Permanent Syndrome", static_cast<int8_t>(k) });
break;
case embark_assist::defs::syndrome_rain_ranges::Temporary:
element->list.push_back({ "Temporary Syndrome", static_cast<int8_t>(k) });
break;
case embark_assist::defs::syndrome_rain_ranges::Not_Permanent:
element->list.push_back({ "Not Permanent Syndrome", static_cast<int8_t>(k) });
break;
case embark_assist::defs::syndrome_rain_ranges::None:
element->list.push_back({ "No Syndrome", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::syndrome_rain_ranges::None) {
break;
}
k = static_cast <embark_assist::defs::syndrome_rain_ranges>(static_cast<int8_t>(k) + 1);
}
}
break;
case fields::reanimation:
{
embark_assist::defs::reanimation_ranges k = embark_assist::defs::reanimation_ranges::NA;
while (true) {
switch (k) {
case embark_assist::defs::reanimation_ranges::NA:
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::Both:
element->list.push_back({ "Reanimation & Thralling", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::Any:
element->list.push_back({ "Reanimation or Thralling", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::Thralling:
element->list.push_back({ "Thralling", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::Reanimation:
element->list.push_back({ "Reanimation", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::Not_Thralling:
element->list.push_back({ "Not Thralling", static_cast<int8_t>(k) });
break;
case embark_assist::defs::reanimation_ranges::None:
element->list.push_back({ "None", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::reanimation_ranges::None) {
break;
}
k = static_cast <embark_assist::defs::reanimation_ranges>(static_cast<int8_t>(k) + 1);
}
}
break;
case fields::spire_count_min:
case fields::spire_count_max:
for (int16_t k = -1; k <= 9; k++) {
@ -834,16 +917,16 @@ namespace embark_assist {
state->finder_list.push_back({ "Min Soil Everywhere", static_cast<int8_t>(i) });
break;
case fields::evil_weather:
state->finder_list.push_back({ "Evil Weather", static_cast<int8_t>(i) });
case fields::blood_rain:
state->finder_list.push_back({ "Blood Rain", static_cast<int8_t>(i) });
break;
case fields::reanimation:
state->finder_list.push_back({ "Reanimation", static_cast<int8_t>(i) });
case fields::syndrome_rain:
state->finder_list.push_back({ "Syndrome Rain", static_cast<int8_t>(i) });
break;
case fields::thralling:
state->finder_list.push_back({ "Thralling", static_cast<int8_t>(i) });
case fields::reanimation:
state->finder_list.push_back({ "Reanimation", static_cast<int8_t>(i) });
break;
case fields::clay:
@ -1058,19 +1141,19 @@ namespace embark_assist {
static_cast<embark_assist::defs::all_present_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::evil_weather:
finder.evil_weather =
case fields::blood_rain:
finder.blood_rain =
static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::reanimation:
finder.reanimation =
static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
case fields::syndrome_rain:
finder.syndrome_rain =
static_cast<embark_assist::defs::syndrome_rain_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::thralling:
finder.thralling =
static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
case fields::reanimation:
finder.reanimation =
static_cast<embark_assist::defs::reanimation_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::clay:
@ -1380,7 +1463,7 @@ void embark_assist::finder_ui::init(DFHack::Plugin *plugin_self, embark_assist::
if (!embark_assist::finder_ui::state) { // First call. Have to do the setup
embark_assist::finder_ui::ui_setup(find_callback, max_inorganic);
}
Screen::show(new ViewscreenFindUi(), plugin_self);
Screen::show(dts::make_unique<ViewscreenFindUi>(), plugin_self);
}
//===============================================================================

@ -209,6 +209,11 @@ namespace embark_assist{
help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil");
help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and");
help_text.push_back("and Present.");
help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,");
help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as");
help_text.push_back("Not Permanent matches everything except Permanent syndromes.");
help_text.push_back("Reanimation packages thralling and reanimation into a single search");
help_text.push_back("criterion. Not Tralling means nothing and just reanimation is matched.");
help_text.push_back("The parameters for biomes, regions, etc. all require that the required");
help_text.push_back("feature is Present in the embark, and entering the same value multiple");
help_text.push_back("times does nothing (the first match ticks all requirements off). It can be");
@ -254,9 +259,9 @@ namespace embark_assist{
help_text.push_back(" emulate the sizing logic exactly.");
help_text.push_back("- There's currently a DF bug (#0010267) that causes adamantine spires");
help_text.push_back(" reaching caverns that have been removed at world gen to fail to be");
help_text.push_back(" generated. It's likely this bug also affects magma pools.");
help_text.push_back(" generated at all. It's likely this bug also affects magma pools.");
help_text.push_back(" This plugin does not address this but scripts can correct it.");
help_text.push_back("Version 0.3 2018-02-26");
help_text.push_back("Version 0.4 2018-06-21");
break;
}
@ -322,5 +327,5 @@ namespace embark_assist{
//===============================================================================
void embark_assist::help_ui::init(DFHack::Plugin *plugin_self) {
Screen::show(new embark_assist::help_ui::ViewscreenHelpUi(), plugin_self);
Screen::show(dts::make_unique<embark_assist::help_ui::ViewscreenHelpUi>(), plugin_self);
}

@ -48,7 +48,9 @@ namespace embark_assist {
bool flux_found = false;
uint8_t max_soil = 0;
bool uneven = false;
bool evil_weather_found = false;
bool blood_rain_found = false;
bool permanent_syndrome_rain_found = false;
bool temporary_syndrome_rain_found = false;
bool reanimation_found = false;
bool thralling_found = false;
uint8_t spire_count = 0;
@ -183,21 +185,39 @@ namespace embark_assist {
if (finder->soil_max != embark_assist::defs::soil_ranges::NA &&
mlt->at(i).at(k).soil_depth > static_cast<uint16_t>(finder->soil_max)) return false;
// Evil Weather
if (survey_results->at(x).at(y).evil_weather[mlt->at(i).at(k).biome_offset]) {
if (finder->evil_weather == embark_assist::defs::yes_no_ranges::No) return false;
evil_weather_found = true;
// Blood Rain
if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) {
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false;
blood_rain_found = true;
}
// Syndrome Rain, Permanent
if (survey_results->at(x).at(y).permanent_syndrome_rain[mlt->at(i).at(k).biome_offset]) {
if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary ||
finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Not_Permanent ||
finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false;
permanent_syndrome_rain_found = true;
}
// Syndrome Rain, Temporary
if (survey_results->at(x).at(y).temporary_syndrome_rain[mlt->at(i).at(k).biome_offset]) {
if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent ||
finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::None) return false;
temporary_syndrome_rain_found = true;
}
// Reanmation
if (survey_results->at(x).at(y).reanimating[mlt->at(i).at(k).biome_offset]) {
if (finder->reanimation == embark_assist::defs::yes_no_ranges::No) return false;
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling ||
finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false;
reanimation_found = true;
}
// Thralling
if (survey_results->at(x).at(y).thralling[mlt->at(i).at(k).biome_offset]) {
if (finder->thralling == embark_assist::defs::yes_no_ranges::No) return false;
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation ||
finder->reanimation == embark_assist::defs::reanimation_ranges::Not_Thralling ||
finder->reanimation == embark_assist::defs::reanimation_ranges::None) return false;
thralling_found = true;
}
@ -303,14 +323,19 @@ namespace embark_assist {
finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present &&
max_soil < static_cast<uint8_t>(finder->soil_min)) return false;
// Evil Weather
if (finder->evil_weather == embark_assist::defs::yes_no_ranges::Yes && !evil_weather_found) return false;
// Blood Rain
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false;
// Reanimation
if (finder->reanimation == embark_assist::defs::yes_no_ranges::Yes && !reanimation_found) return false;
// Syndrome Rain
if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Any && !permanent_syndrome_rain_found && !temporary_syndrome_rain_found) return false;
if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Permanent && !permanent_syndrome_rain_found) return false;
if (finder->syndrome_rain == embark_assist::defs::syndrome_rain_ranges::Temporary && !temporary_syndrome_rain_found) return false;
// Thralling
if (finder->thralling == embark_assist::defs::yes_no_ranges::Yes && !thralling_found) return false;
// Reanimation
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Both && !(reanimation_found && thralling_found)) return false;
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Any && !reanimation_found && !thralling_found) return false;
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Thralling && !thralling_found) return false;
if (finder->reanimation == embark_assist::defs::reanimation_ranges::Reanimation && !reanimation_found) return false;
// Spires
if (finder->spire_count_min != -1 && finder->spire_count_min > spire_count) return false;
@ -597,46 +622,74 @@ namespace embark_assist {
break;
}
// Evil Weather
switch (finder->evil_weather) {
// Blood Rain
switch (finder->blood_rain) {
case embark_assist::defs::yes_no_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
if (!tile->evil_weather_possible) return false;
if (!tile->blood_rain_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
if (tile->evil_weather_full) return false;
if (tile->blood_rain_full) return false;
break;
}
// Reanimating
switch (finder->reanimation) {
case embark_assist::defs::yes_no_ranges::NA:
// Syndrome Rain
switch (finder->syndrome_rain) {
case embark_assist::defs::syndrome_rain_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
if (!tile->reanimating_possible) return false;
case embark_assist::defs::syndrome_rain_ranges::Any:
if (!tile->permanent_syndrome_rain_possible && !tile->temporary_syndrome_rain_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
if (tile->reanimating_full) return false;
case embark_assist::defs::syndrome_rain_ranges::Permanent:
if (!tile->permanent_syndrome_rain_possible) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::Temporary:
if (!tile->temporary_syndrome_rain_possible) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::Not_Permanent:
if (tile->permanent_syndrome_rain_full) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::None:
if (tile->permanent_syndrome_rain_full || tile->temporary_syndrome_rain_full) return false;
break;
}
// Thralling
switch (finder->thralling) {
case embark_assist::defs::yes_no_ranges::NA:
// Reanimating
switch (finder->reanimation) {
case embark_assist::defs::reanimation_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
case embark_assist::defs::reanimation_ranges::Both:
if (!tile->reanimating_possible || !tile->thralling_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Any:
if (!tile->reanimating_possible && !tile->thralling_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Thralling:
if (!tile->thralling_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
case embark_assist::defs::reanimation_ranges::Reanimation:
if (!tile->reanimating_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Not_Thralling:
if (tile->thralling_full) return false;
break;
case embark_assist::defs::reanimation_ranges::None:
if (tile->reanimating_full || tile->thralling_full) return false;
break;
}
// Spire Count Min/Max
@ -950,46 +1003,74 @@ namespace embark_assist {
// Soil Max
// Can't say anything as the preliminary data isn't reliable
// Evil Weather
switch (finder->evil_weather) {
// Blood Rain
switch (finder->blood_rain) {
case embark_assist::defs::yes_no_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
if (!tile->evil_weather_possible) return false;
if (!tile->blood_rain_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
if (tile->evil_weather_full) return false;
if (tile->blood_rain_full) return false;
break;
}
// Reanimating
switch (finder->reanimation) {
case embark_assist::defs::yes_no_ranges::NA:
// Syndrome Rain
switch (finder->syndrome_rain) {
case embark_assist::defs::syndrome_rain_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
if (!tile->reanimating_possible) return false;
case embark_assist::defs::syndrome_rain_ranges::Any:
if (!tile->permanent_syndrome_rain_possible && !tile->temporary_syndrome_rain_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
if (tile->reanimating_full) return false;
case embark_assist::defs::syndrome_rain_ranges::Permanent:
if (!tile->permanent_syndrome_rain_possible) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::Temporary:
if (!tile->temporary_syndrome_rain_possible) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::Not_Permanent:
if (tile->permanent_syndrome_rain_full) return false;
break;
case embark_assist::defs::syndrome_rain_ranges::None:
if (tile->permanent_syndrome_rain_full || tile->temporary_syndrome_rain_full) return false;
break;
}
// Thralling
switch (finder->thralling) {
case embark_assist::defs::yes_no_ranges::NA:
// Reanimating
switch (finder->reanimation) {
case embark_assist::defs::reanimation_ranges::NA:
break; // No restriction
case embark_assist::defs::yes_no_ranges::Yes:
case embark_assist::defs::reanimation_ranges::Both:
if (!tile->reanimating_possible || !tile->thralling_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Any:
if (!tile->reanimating_possible && !tile->thralling_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Thralling:
if (!tile->thralling_possible) return false;
break;
case embark_assist::defs::yes_no_ranges::No:
case embark_assist::defs::reanimation_ranges::Reanimation:
if (!tile->reanimating_possible) return false;
break;
case embark_assist::defs::reanimation_ranges::Not_Thralling:
if (tile->thralling_full) return false;
break;
case embark_assist::defs::reanimation_ranges::None:
if (tile->reanimating_full || tile->thralling_full) return false;
break;
}
// Spire Count Min/Max

@ -259,12 +259,14 @@ namespace embark_assist {
//=================================================================================
void survey_evil_weather(embark_assist::defs::world_tile_data *survey_results) {
// color_ostream_proxy out(Core::getInstance().getConsole());
df::world_data *world_data = world->world_data;
for (uint16_t i = 0; i < world->interaction_instances.all.size(); i++) {
auto interaction = world->raws.interactions[world->interaction_instances.all[i]->interaction_id];
uint16_t region_index = world->interaction_instances.all[i]->region_index;
bool blood_rain = false;
bool permanent_syndrome_rain = false;
bool temporary_syndrome_rain = false;
bool thralling = false;
bool reanimating = false;
@ -282,62 +284,115 @@ namespace embark_assist {
else if (interaction->targets[k]->getType() == df::interaction_target_type::MATERIAL) {
df::interaction_target_materialst* material = virtual_cast<df::interaction_target_materialst>(interaction->targets[k]);
if (material && DFHack::MaterialInfo(material->mat_type, material->mat_index).isInorganic()) {
for (uint16_t l = 0; l < world->raws.inorganics[material->mat_index]->material.syndrome.size(); l++) {
for (uint16_t m = 0; m < world->raws.inorganics[material->mat_index]->material.syndrome[l]->ce.size(); m++) {
if (world->raws.inorganics[material->mat_index]->material.syndrome[l]->ce[m]->getType() == df::creature_interaction_effect_type::FLASH_TILE) {
for (const auto &syndrome : world->raws.inorganics[material->mat_index]->material.syndrome) {
for (const auto &ce : syndrome->ce) {
df::creature_interaction_effect_type ce_type = ce->getType();
if (ce_type == df::creature_interaction_effect_type::FLASH_TILE) {
// Using this as a proxy. There seems to be a group of 4 effects for thralls:
// display symbol, flash symbol, phys att change and one more.
thralling = true;
}
else if (ce_type == df::creature_interaction_effect_type::PAIN ||
ce_type == df::creature_interaction_effect_type::SWELLING ||
ce_type == df::creature_interaction_effect_type::OOZING ||
ce_type == df::creature_interaction_effect_type::BRUISING ||
ce_type == df::creature_interaction_effect_type::BLISTERS ||
ce_type == df::creature_interaction_effect_type::NUMBNESS ||
ce_type == df::creature_interaction_effect_type::PARALYSIS ||
ce_type == df::creature_interaction_effect_type::FEVER ||
ce_type == df::creature_interaction_effect_type::BLEEDING ||
ce_type == df::creature_interaction_effect_type::COUGH_BLOOD ||
ce_type == df::creature_interaction_effect_type::VOMIT_BLOOD ||
ce_type == df::creature_interaction_effect_type::NAUSEA ||
ce_type == df::creature_interaction_effect_type::UNCONSCIOUSNESS ||
ce_type == df::creature_interaction_effect_type::NECROSIS ||
ce_type == df::creature_interaction_effect_type::IMPAIR_FUNCTION ||
ce_type == df::creature_interaction_effect_type::DROWSINESS ||
ce_type == df::creature_interaction_effect_type::DIZZINESS ||
ce_type == df::creature_interaction_effect_type::ERRATIC_BEHAVIOR) { // Doubtful if possible for region.
if (ce->end == -1) {
permanent_syndrome_rain = true;
}
else {
temporary_syndrome_rain = true;
}
}
}
}
}
else { // If not inorganic it's always blood, as far as known.
blood_rain = true;
}
}
}
}
for (uint16_t k = 0; k < world_data->regions[region_index]->region_coords.size(); k++) {
survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).evil_weather[5] = true;
survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).reanimating[5] = reanimating;
survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y).thralling[5] = thralling;
auto &results = survey_results->at(world_data->regions[region_index]->region_coords[k].x).at(world_data->regions[region_index]->region_coords[k].y);
results.blood_rain[5] = blood_rain;
results.permanent_syndrome_rain[5] = permanent_syndrome_rain;
results.temporary_syndrome_rain[5] = temporary_syndrome_rain;
results.reanimating[5] = reanimating;
results.thralling[5] = thralling;
}
}
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
for (uint16_t k = 0; k < world->worldgen.worldgen_parms.dim_y; k++) {
survey_results->at(i).at(k).evil_weather_possible = false;
survey_results->at(i).at(k).reanimating_possible = false;
survey_results->at(i).at(k).thralling_possible = false;
survey_results->at(i).at(k).evil_weather_full = true;
survey_results->at(i).at(k).reanimating_full = true;
survey_results->at(i).at(k).thralling_full = true;
auto &results = survey_results->at(i).at(k);
results.blood_rain_possible = false;
results.permanent_syndrome_rain_possible = false;
results.temporary_syndrome_rain_possible = false;
results.reanimating_possible = false;
results.thralling_possible = false;
results.blood_rain_full = true;
results.permanent_syndrome_rain_full = true;
results.temporary_syndrome_rain_full = true;
results.reanimating_full = true;
results.thralling_full = true;
for (uint8_t l = 1; l < 10; l++) {
if (survey_results->at(i).at(k).biome_index[l] != -1) {
if (results.biome_index[l] != -1) {
df::coord2d adjusted = apply_offset(i, k, l);
survey_results->at(i).at(k).evil_weather[l] = survey_results->at(adjusted.x).at(adjusted.y).evil_weather[5];
survey_results->at(i).at(k).reanimating[l] = survey_results->at(adjusted.x).at(adjusted.y).reanimating[5];
survey_results->at(i).at(k).thralling[l] = survey_results->at(adjusted.x).at(adjusted.y).thralling[5];
results.blood_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).blood_rain[5];
results.permanent_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).permanent_syndrome_rain[5];
results.temporary_syndrome_rain[l] = survey_results->at(adjusted.x).at(adjusted.y).temporary_syndrome_rain[5];
results.reanimating[l] = survey_results->at(adjusted.x).at(adjusted.y).reanimating[5];
results.thralling[l] = survey_results->at(adjusted.x).at(adjusted.y).thralling[5];
if (results.blood_rain[l]) {
results.blood_rain_possible = true;
}
else {
results.blood_rain_full = false;
}
if (survey_results->at(i).at(k).evil_weather[l]) {
survey_results->at(i).at(k).evil_weather_possible = true;
if (results.permanent_syndrome_rain[l]) {
results.permanent_syndrome_rain_possible = true;
}
else {
survey_results->at(i).at(k).evil_weather_full = false;
results.permanent_syndrome_rain_full = false;
}
if (survey_results->at(i).at(k).reanimating[l]) {
survey_results->at(i).at(k).reanimating_possible = true;
if (results.temporary_syndrome_rain[l]) {
results.temporary_syndrome_rain_possible = true;
}
else {
survey_results->at(i).at(k).reanimating_full = false;
results.temporary_syndrome_rain_full = false;
}
if (survey_results->at(i).at(k).thralling[l]) {
survey_results->at(i).at(k).thralling_possible = true;
if (results.reanimating[l]) {
results.reanimating_possible = true;
}
else {
survey_results->at(i).at(k).thralling_full = false;
results.reanimating_full = false;
}
if (results.thralling[l]) {
results.thralling_possible = true;
}
else {
results.thralling_full = false;
}
}
}
@ -405,23 +460,24 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
uint16_t geo_index;
uint16_t sav_ev;
uint8_t offset_count = 0;
survey_results->at(i).at(k).surveyed = false;
survey_results->at(i).at(k).aquifer_count = 0;
survey_results->at(i).at(k).clay_count = 0;
survey_results->at(i).at(k).sand_count = 0;
survey_results->at(i).at(k).flux_count = 0;
survey_results->at(i).at(k).min_region_soil = 10;
survey_results->at(i).at(k).max_region_soil = 0;
survey_results->at(i).at(k).waterfall = false;
survey_results->at(i).at(k).savagery_count[0] = 0;
survey_results->at(i).at(k).savagery_count[1] = 0;
survey_results->at(i).at(k).savagery_count[2] = 0;
survey_results->at(i).at(k).evilness_count[0] = 0;
survey_results->at(i).at(k).evilness_count[1] = 0;
survey_results->at(i).at(k).evilness_count[2] = 0;
survey_results->at(i).at(k).metals.resize(state->max_inorganic);
survey_results->at(i).at(k).economics.resize(state->max_inorganic);
survey_results->at(i).at(k).minerals.resize(state->max_inorganic);
auto &results = survey_results->at(i).at(k);
results.surveyed = false;
results.aquifer_count = 0;
results.clay_count = 0;
results.sand_count = 0;
results.flux_count = 0;
results.min_region_soil = 10;
results.max_region_soil = 0;
results.waterfall = false;
results.savagery_count[0] = 0;
results.savagery_count[1] = 0;
results.savagery_count[2] = 0;
results.evilness_count[0] = 0;
results.evilness_count[1] = 0;
results.evilness_count[2] = 0;
results.metals.resize(state->max_inorganic);
results.economics.resize(state->max_inorganic);
results.minerals.resize(state->max_inorganic);
// Evil weather and rivers are handled in later operations. Should probably be merged into one.
for (uint8_t l = 1; l < 10; l++)
@ -430,53 +486,53 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
if (adjusted.x != i || adjusted.y != k || l == 5) {
offset_count++;
survey_results->at(i).at(k).biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id;
survey_results->at(i).at(k).biome[l] = get_biome_type(adjusted.x, adjusted.y, k);
results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id;
results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k);
geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index;
if (!geo_summary->at(geo_index).aquifer_absent) survey_results->at(i).at(k).aquifer_count++;
if (!geo_summary->at(geo_index).clay_absent) survey_results->at(i).at(k).clay_count++;
if (!geo_summary->at(geo_index).sand_absent) survey_results->at(i).at(k).sand_count++;
if (!geo_summary->at(geo_index).flux_absent) survey_results->at(i).at(k).flux_count++;
if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++;
if (!geo_summary->at(geo_index).clay_absent) results.clay_count++;
if (!geo_summary->at(geo_index).sand_absent) results.sand_count++;
if (!geo_summary->at(geo_index).flux_absent) results.flux_count++;
if (geo_summary->at(geo_index).soil_size < survey_results->at(i).at(k).min_region_soil)
survey_results->at(i).at(k).min_region_soil = geo_summary->at(geo_index).soil_size;
if (geo_summary->at(geo_index).soil_size < results.min_region_soil)
results.min_region_soil = geo_summary->at(geo_index).soil_size;
if (geo_summary->at(geo_index).soil_size > survey_results->at(i).at(k).max_region_soil)
survey_results->at(i).at(k).max_region_soil = geo_summary->at(geo_index).soil_size;
if (geo_summary->at(geo_index).soil_size > results.max_region_soil)
results.max_region_soil = geo_summary->at(geo_index).soil_size;
sav_ev = world_data->region_map[adjusted.x][adjusted.y].savagery / 33;
if (sav_ev == 3) sav_ev = 2;
survey_results->at(i).at(k).savagery_count[sav_ev]++;
results.savagery_count[sav_ev]++;
sav_ev = world_data->region_map[adjusted.x][adjusted.y].evilness / 33;
if (sav_ev == 3) sav_ev = 2;
survey_results->at(i).at(k).evilness_count[sav_ev]++;
results.evilness_count[sav_ev]++;
for (uint16_t m = 0; m < state->max_inorganic; m++) {
if (geo_summary->at(geo_index).possible_metals[m]) survey_results->at(i).at(k).metals[m] = true;
if (geo_summary->at(geo_index).possible_economics[m]) survey_results->at(i).at(k).economics[m] = true;
if (geo_summary->at(geo_index).possible_minerals[m]) survey_results->at(i).at(k).minerals[m] = true;
if (geo_summary->at(geo_index).possible_metals[m]) results.metals[m] = true;
if (geo_summary->at(geo_index).possible_economics[m]) results.economics[m] = true;
if (geo_summary->at(geo_index).possible_minerals[m]) results.minerals[m] = true;
}
}
else {
survey_results->at(i).at(k).biome_index[l] = -1;
survey_results->at(i).at(k).biome[l] = -1;
results.biome_index[l] = -1;
results.biome[l] = -1;
}
}
survey_results->at(i).at(k).biome_count = 0;
results.biome_count = 0;
for (uint8_t l = 1; l < 10; l++) {
if (survey_results->at(i).at(k).biome[l] != -1) survey_results->at(i).at(k).biome_count++;
if (results.biome[l] != -1) results.biome_count++;
}
if (survey_results->at(i).at(k).aquifer_count == offset_count) survey_results->at(i).at(k).aquifer_count = 256;
if (survey_results->at(i).at(k).clay_count == offset_count) survey_results->at(i).at(k).clay_count = 256;
if (survey_results->at(i).at(k).sand_count == offset_count) survey_results->at(i).at(k).sand_count = 256;
if (survey_results->at(i).at(k).flux_count == offset_count) survey_results->at(i).at(k).flux_count = 256;
if (results.aquifer_count == offset_count) results.aquifer_count = 256;
if (results.clay_count == offset_count) results.clay_count = 256;
if (results.sand_count == offset_count) results.sand_count = 256;
if (results.flux_count == offset_count) results.flux_count = 256;
for (uint8_t l = 0; l < 3; l++) {
if (survey_results->at(i).at(k).savagery_count[l] == offset_count) survey_results->at(i).at(k).savagery_count[l] = 256;
if (survey_results->at(i).at(k).evilness_count[l] == offset_count) survey_results->at(i).at(k).evilness_count[l] = 256;
if (results.savagery_count[l] == offset_count) results.savagery_count[l] = 256;
if (results.evilness_count[l] == offset_count) results.evilness_count[l] = 256;
}
}
}
@ -543,7 +599,7 @@ void embark_assist::survey::survey_mid_level_tile(embark_assist::defs::geo_data
else
{
mlt->at(i).at(k).biome_offset = 5;
};
}
survey_results->at(x).at(y).biome_index[mlt->at(i).at(k).biome_offset] =
world_data->region_map[adjusted.x][adjusted.y].region_id;

@ -696,7 +696,7 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
void display_settings()
{
Screen::show(new embark_tools_settings, plugin_self);
Screen::show(dts::make_unique<embark_tools_settings>(), plugin_self);
}
inline bool is_valid_page()

@ -211,7 +211,7 @@ DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
DFhackCExport command_result plugin_onupdate (color_ostream &out)
{
if (is_enabled && World::isFortressMode())
if (is_enabled && World::isFortressMode() && Maps::IsValid())
{
if (world->frame_counter % run_interval == 0 && !World::ReadPauseState())
{

@ -156,7 +156,7 @@ command_result follow (color_ostream &out, std::vector <std::string> & parameter
ss << "Unpause to begin following " << world->raws.creatures.all[followedUnit->race]->name[0];
if (followedUnit->name.has_name) ss << " " << followedUnit->name.first_name;
ss << ". Simply manually move the view to break the following.\n";
out.print(ss.str().c_str());
out.print("%s", ss.str().c_str());
}
else followedUnit = 0;
is_enabled = (followedUnit != NULL);

@ -298,7 +298,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
else
{
// The specified body part has not been found, and we've reached the end of the list. Report failure.
if (verbose) { Core::printerr("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n"); }
if (verbose) { Core::printerr("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n",targetBodyPart->token.c_str()); }
return false;
}

@ -270,7 +270,7 @@ command_result fortplan(color_ostream &out, vector<string> & params) {
checkCode = layout[checkY][checkX];
}
con.print(" - Code at (%d,%d) is '%s': ",checkX,checkY,checkCode.c_str());
con.print(" - Code at (%zu,%zu) is '%s': ",checkX,checkY,checkCode.c_str());
auto checkIndex = std::find_if(buildings.begin(), buildings.end(), MatchesCode(checkCode.c_str()));
//if (checkIndex == buildings.end()) {
// con.print("this is not a valid code, so we keep going.\n");
@ -331,16 +331,16 @@ command_result fortplan(color_ostream &out, vector<string> & params) {
offsetCursor.y -= yOffset;
DFHack::Gui::setCursorCoords(offsetCursor.x, offsetCursor.y, offsetCursor.z);
if (!buildingInfo.allocate()) {
con.print("*** There was an error placing building with code '%s' centered at (%d,%d).\n",curCode.c_str(),x,y);
con.print("*** There was an error placing building with code '%s' centered at (%zu,%zu).\n",curCode.c_str(),x,y);
}
DFHack::Gui::setCursorCoords(cursor.x, cursor.y, cursor.z);
} else if (block) {
//con.print("Placing a building with code '%s' with corner at (%d,%d) and default size %dx%d.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
if (!buildingInfo.allocate()) {
con.print("*** There was an error placing building with code '%s' with corner at (%d,%d).\n",curCode.c_str(),x,y);
con.print("*** There was an error placing building with code '%s' with corner at (%zu,%zu).\n",curCode.c_str(),x,y);
}
} else {
con.print("*** Found a code '%s' at (%d,%d) for a building with default size %dx%d with an invalid size designation.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
con.print("*** Found a code '%s' at (%zu,%zu) for a building with default size %dx%d with an invalid size designation.\n",curCode.c_str(),x,y,buildingInfo.defaultWidth,buildingInfo.defaultHeight);
}
} else {
//buildingSize = findBuildingExtent(layout, x, y, -1, -1, out);
@ -350,7 +350,7 @@ command_result fortplan(color_ostream &out, vector<string> & params) {
} else {
//con.print("Building a(n) %s.\n",buildingInfo.name.c_str());
if (!buildingInfo.allocate()) {
con.print("*** There was an error placing the %s at (%d,%d).\n",buildingInfo.name.c_str(),x,y);
con.print("*** There was an error placing the %s at (%zu,%zu).\n",buildingInfo.name.c_str(),x,y);
}
}
}

@ -199,11 +199,10 @@ command_result list_creatures(color_ostream &out, std::vector <std::string> & pa
auto creatureRaw = world->raws.creatures.all[i];
if (!creatureRaw->flags.is_set(df::enums::creature_raw_flags::GENERATED))
continue;
out.print(creatureRaw->creature_id.c_str());
out.print("%s",creatureRaw->creature_id.c_str());
if (detailed)
{
out.print("\t");
out.print(creatureRaw->caste[0]->description.c_str());
out.print("\t%s",creatureRaw->caste[0]->description.c_str());
}
out.print("\n");
}

@ -312,7 +312,7 @@ static command_result hotkeys_cmd(color_ostream &out, vector <string> & paramete
if (Gui::getFocusString(top_screen) != "dfhack/viewscreen_hotkeys")
{
find_active_keybindings(top_screen);
Screen::show(new ViewscreenHotkeys(top_screen), plugin_self);
Screen::show(dts::make_unique<ViewscreenHotkeys>(top_screen), plugin_self);
}
}
}

@ -156,14 +156,14 @@ static command_result job_material_in_job(color_ostream &out, MaterialInfo &new_
if (item_mat != cur_mat)
{
out.printerr("Job item %d has different material: %s\n",
out.printerr("Job item %zu has different material: %s\n",
i, item_mat.toString().c_str());
return CR_FAILURE;
}
if (!new_mat.matches(*item))
{
out.printerr("Job item %d requirements not satisfied by %s.\n",
out.printerr("Job item %zu requirements not satisfied by %s.\n",
i, new_mat.toString().c_str());
return CR_FAILURE;
}

@ -1224,7 +1224,7 @@ private:
if (p1 || p2)
{
df::unit* other = p1 ? act->unit_noble : act->unit_actor;
if (other && !(other->flags1.bits.dead ||
if (other && !(!Units::isActive(other) ||
(other->job.current_job &&
(other->job.current_job->job_type == df::job_type::Sleep ||
other->job.current_job->job_type == df::job_type::Rest)) ||
@ -1247,7 +1247,7 @@ private:
for (auto u2 = world->units.active.begin(); u2 != world->units.active.end(); ++u2)
{
if ((*u2)->relationship_ids[df::unit_relationship_type::Mother] == dwarf->dwarf->id &&
!(*u2)->flags1.bits.dead &&
Units::isActive(*u2) &&
((*u2)->profession == df::profession::CHILD || (*u2)->profession == df::profession::BABY))
{
dwarf->has_children = true;
@ -1757,7 +1757,7 @@ public:
}
if (print_debug)
out.print("available count = %d, distinct labors needed = %d\n", available_dwarfs.size(), pq.size());
out.print("available count = %zu, distinct labors needed = %zu\n", available_dwarfs.size(), pq.size());
std::map<df::unit_labor, int> to_assign;
@ -1958,7 +1958,7 @@ public:
/* Assign any leftover dwarfs to "standard" labors */
if (print_debug)
out.print("After assignment, %d dwarfs left over\n", available_dwarfs.size());
out.print("After assignment, %zu dwarfs left over\n", available_dwarfs.size());
for (auto d = available_dwarfs.begin(); d != available_dwarfs.end(); d++)
{

@ -188,8 +188,11 @@ command_result df_liquids (color_ostream &out_, vector <string> & parameters)
std::stringstream str;
print_prompt(str, cur_mode);
str << "# ";
if(out.lineedit(str.str(),input,liquids_hist) == -1)
return CR_FAILURE;
int rv;
while ((rv = out.lineedit(str.str(),input,liquids_hist))
== Console::RETRY);
if (rv <= Console::FAILURE)
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
liquids_hist.add(input);
commands.clear();

@ -0,0 +1,214 @@
local _ENV = mkmodule('plugins.cxxrandom')
function MakeNewEngine(seed)
if type(seed) == 'number' then
if seed == 0 then
print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).")
end
return GenerateEngine(seed)
elseif type(seed) == 'nil' then
return GenerateEngine(0)
else
error("Argument `seed` must be a number, or nil.")
end
end
--Class: crng
-------------
crng = {}
function crng:new(engineID, destroyEngineOnDestruction, distrib)
local o = {}
self.__index = self
local idtype = type(engineID)
local flagtype = type(destroyEngineOnDestruction)
if idtype == 'number' then
o.rngID = engineID
elseif idtype == 'nil' then
o.rngID = GenerateEngine(0)
else
error("Invalid argument type (engineID): " .. tostring(engineID))
end
if flagtype ~= 'nil' and flagtype == 'boolean' then
o.destroyid = destroyEngineOnDestruction
elseif flagtype == 'nil' then
o.destroyid = true
else
error("Invalid arugment type (destroyEngineOnDestruction): " .. tostring(destroyEngineOnDestruction))
end
if type(distrib) ~= 'nil' then
if type(distrib) == 'table' and type(distrib.next) == 'function' then
o.distrib = distrib
o.distrib.rngID = o.rngID
else
error("Invalid distribution used as an argument. Cannot set this as the number distribution.")
end
end
setmetatable(o,self)
return o
end
--crng destructor - we may need to destroy the engine, the user may be doing it manually though
function crng:__gc()
if self.destroyid then
DestroyEngine(self.rngID)
end
end
function crng:changeSeed(seed)
if type(seed) == 'number' then
if seed == 0 then
print(":WARNING: Seeds equal to 0 are used if no seed is provided. This indicates to cxxrandom.plug.dll that the engine needs to be seeded with the current time.\nRecommendation: use a non-zero value for your seed, or don't provide a seed to use the time since epoch(1969~).")
end
return NewSeed(self.rngID, seed)
elseif type(seed) == 'nil' then
return NewSeed(self.rngID, 0)
else
error("Argument `seed` must be a number, or nil.")
end
end
function crng:setNumDistrib(distrib)
if type(distrib) == 'table' and type(distrib.next) == 'function' then
self.distrib = distrib
self.distrib.rngID = self.rngID
else
error("Invalid distribution used as an argument. Cannot set this as the number distribution.")
end
end
function crng:next()
if type(self.distrib) == 'table' and type(self.distrib.next) == 'function' then
return self.distrib:next(self.rngID)
else
error("crng object does not have a valid number distribution set")
end
end
function crng:shuffle()
if type(self.distrib) == 'table' and type(self.distrib.shuffle) == 'function' then
self.distrib:shuffle(self.rngID)
else
print(":WARNING: No self.distrib.shuffle not found.")
changeSeed(0)
end
end
--Class: normal_distribution
----------------------------
normal_distribution = {}
function normal_distribution:new(avg, stddev)
local o = {}
self.__index = self
if type(avg) ~= 'number' or type(stddev) ~= 'number' then
error("Invalid arguments in normal_distribution construction. Average and standard deviation must be numbers.")
end
o.average = avg
o.std_deviation = stddev
setmetatable(o,self)
return o
end
function normal_distribution:next(id)
return rollNormal(id, self.average, self.std_deviation)
end
--Class: real_distribution
----------------------------
real_distribution = {}
function real_distribution:new(min, max)
local o = {}
self.__index = self
if type(min) ~= 'number' or type(max) ~= 'number' then
error("Invalid arguments in real_distribution construction. min and max must be numbers.")
end
o.min = min
o.max = max
setmetatable(o,self)
return o
end
function real_distribution:next(id)
return rollDouble(id, self.min, self.max)
end
--Class: int_distribution
----------------------------
int_distribution = {}
function int_distribution:new(min, max)
local o = {}
self.__index = self
if type(min) ~= 'number' or type(max) ~= 'number' then
error("Invalid arguments in int_distribution construction. min and max must be numbers.")
end
o.min = min
o.max = max
setmetatable(o,self)
return o
end
function int_distribution:next(id)
return rollInt(id, self.min, self.max)
end
--Class: bool_distribution
----------------------------
bool_distribution = {}
function bool_distribution:new(chance)
local o = {}
self.__index = self
if type(min) ~= 'number' or type(max) ~= 'number' then
error("Invalid arguments in bool_distribution construction. min and max must be numbers.")
end
o.p = chance
setmetatable(o,self)
return o
end
function bool_distribution:next(id)
return rollBool(id, self.p)
end
--Class: num_sequence
----------------------------
num_sequence = {}
function num_sequence:new(a,b)
local o = {}
self.__index = self
local btype = type(b)
local atype = type(a)
if atype == 'number' and btype == 'number' then
if a == b then
print(":WARNING: You've provided two equal arguments to initialize your sequence with. This is the mechanism used to indicate to cxxrandom.plug.dll that an empty sequence is desired.\nRecommendation: provide no arguments if you wish for an empty sequence.")
end
o.seqID = MakeNumSequence(a, b)
elseif atype == 'table' then
o.seqID = MakeNumSequence(0,0)
for _,v in pairs(a) do
if type(v) ~= 'number' then
error("num_sequence can only be initialized using numbers. " .. tostring(v) .. " is not a number.")
end
AddToSequence(o.seqID, v)
end
elseif atype == "nil" and btype == "nil" then
o.seqID = MakeNumSequence(0,0)
else
error("Invalid arguments - a: " .. tostring(a) .. " and b: " .. tostring(b))
end
print("seqID:"..o.seqID)
setmetatable(o,self)
return o
end
function num_sequence:__gc()
DestroyNumSequence(self.seqID)
end
function num_sequence:add(x)
if type(x) ~= 'number' then
error("Cannot add non-number to num_sequence.")
end
AddToSequence(self.seqID, x)
end
function num_sequence:next()
return NextInSequence(self.seqID)
end
function num_sequence:shuffle()
if self.rngID == 'nil' then
error("Add num_sequence object to crng as distribution, before attempting to shuffle.")
end
ShuffleSequence(self.rngID, self.seqID)
end
return _ENV

@ -185,7 +185,14 @@ end
function job_outputs.MakeCrafts(callback, job)
local mat_type, mat_index, mat_mask = guess_job_material(job)
callback{ is_craft = true, mat_type = mat_type, mat_index = mat_index, mat_mask = mat_mask }
callback{
is_craft = true,
item_type = -1,
item_subtype = -1,
mat_type = mat_type,
mat_index = mat_index,
mat_mask = mat_mask
}
end
local plant_products = {

@ -1157,7 +1157,7 @@ viewscreen_unitlaborsst::viewscreen_unitlaborsst(vector<df::unit*> &src, int cur
if (!Units::isOwnGroup(unit))
cur->allowEdit = false;
if (unit->flags1.bits.dead)
if (!Units::isActive(unit))
cur->allowEdit = false;
if (unit->flags2.bits.visitor)
@ -1814,14 +1814,14 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
if (events->count(interface_key::CUSTOM_B))
{
Screen::show(new viewscreen_unitbatchopst(units, true, &do_refresh_names), plugin_self);
Screen::show(dts::make_unique<viewscreen_unitbatchopst>(units, true, &do_refresh_names), plugin_self);
}
if (events->count(interface_key::CUSTOM_E))
{
vector<UnitInfo*> tmp;
tmp.push_back(cur);
Screen::show(new viewscreen_unitbatchopst(tmp, false, &do_refresh_names), plugin_self);
Screen::show(dts::make_unique<viewscreen_unitbatchopst>(tmp, false, &do_refresh_names), plugin_self);
}
if (events->count(interface_key::CUSTOM_P))
@ -1832,11 +1832,11 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
has_selected = true;
if (has_selected) {
Screen::show(new viewscreen_unitprofessionset(units, true), plugin_self);
Screen::show(dts::make_unique<viewscreen_unitprofessionset>(units, true), plugin_self);
} else {
vector<UnitInfo*> tmp;
tmp.push_back(cur);
Screen::show(new viewscreen_unitprofessionset(tmp, false), plugin_self);
Screen::show(dts::make_unique<viewscreen_unitprofessionset>(tmp, false), plugin_self);
}
}
@ -2187,7 +2187,7 @@ struct unitlist_hook : df::viewscreen_unitlistst
{
if (units[page].size())
{
Screen::show(new viewscreen_unitlaborsst(units[page], cursor_pos[page]), plugin_self);
Screen::show(dts::make_unique<viewscreen_unitlaborsst>(units[page], cursor_pos[page]), plugin_self);
return;
}
}

@ -96,6 +96,7 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
string command = "";
bool set = false;
bool abuse = false;
int rv = 0;
t_gamemodes gm;
for(auto iter = parameters.begin(); iter != parameters.end(); iter++)
{
@ -139,9 +140,10 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
string selected;
input_again:
CommandHistory hist;
out.lineedit("Enter new mode: ",selected, hist);
if(selected == "c")
return CR_OK;
while((rv = out.lineedit("Enter new mode: ",selected, hist))
== Console::RETRY);
if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
const char * start = selected.c_str();
char * end = 0;
select = strtol(start, &end, 10);
@ -178,14 +180,16 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
{
CommandHistory hist;
string selected;
out.lineedit("Enter new game mode number (c for exit): ",selected, hist);
if(selected == "c")
return CR_OK;
while ((rv = out.lineedit("Enter new game mode number (c for exit): ",selected, hist))
== Console::RETRY);
if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
const char * start = selected.c_str();
gm.g_mode = (GameMode) strtol(start, 0, 10);
out.lineedit("Enter new game type number (c for exit): ",selected, hist);
if(selected == "c")
return CR_OK;
while((rv = out.lineedit("Enter new game type number (c for exit): ",selected, hist))
== Console::RETRY);
if(rv <= Console::FAILURE || selected == "c")
return rv == Console::FAILURE ? CR_FAILURE : CR_OK;
start = selected.c_str();
gm.g_type = (GameType) strtol(start, 0, 10);
}

@ -95,7 +95,7 @@ static vector<df::unit *> get_units_at(const df::coord pos, bool only_one)
df::unit_flags1 bad_flags;
bad_flags.whole = 0;
bad_flags.bits.dead = true;
bad_flags.bits.inactive = true;
bad_flags.bits.hidden_ambusher = true;
bad_flags.bits.hidden_in_ambush = true;

@ -7,7 +7,7 @@
#include "modules/Filesystem.h"
#include "modules/Materials.h"
#include "jsoncpp.h"
#include "json/json.h"
#include "df/building.h"
#include "df/historical_figure.h"

Some files were not shown because too many files have changed in this diff Show More