Merge branch 'develop' into lua-ref-target

develop
lethosor 2020-04-27 23:24:29 -04:00
commit 9e085b66ac
35 changed files with 588 additions and 190 deletions

@ -0,0 +1,106 @@
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install \
libsdl-image1.2-dev \
libsdl-ttf2.0-dev \
libsdl1.2-dev \
libxml-libxml-perl \
libxml-libxslt-perl \
lua5.3 \
ninja-build \
zlib1g-dev
sudo pip3 install --system sphinx
- name: Clone DFHack
uses: actions/checkout@v1
with:
submodules: true
- name: Set up environment
run: |
echo export DF_VERSION="$(sh travis/get-df-version.sh)" >> "$HOME/.df-env"
echo export DF_FOLDER="$HOME/DF/$DF_VERSION/df_linux" >> "$HOME/.df-env"
- name: Download DF
run: |
source "$HOME/.df-env"
sh travis/download-df.sh
- name: Build docs
run: |
sphinx-build -qW -j3 . docs/html
- name: Upload docs
uses: actions/upload-artifact@v1
with:
name: docs
path: docs/html
- name: Build DFHack
run: |
source "$HOME/.df-env"
cmake \
-S . \
-B build-ci \
-G Ninja \
-DDFHACK_BUILD_ARCH=64 \
-DBUILD_DOCS:BOOL=ON \
-DBUILD_TESTS:BOOL=ON \
-DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
ninja -C build-ci install
- name: Run tests
run: |
source "$HOME/.df-env"
export TERM=dumb
mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init
script -qe -c "python travis/run-tests.py --headless --keep-status \"$DF_FOLDER\""
python travis/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt"
mkdir -p artifacts
cp "$DF_FOLDER/test_status.json" "$DF_FOLDER"/*.log artifacts
- name: Upload test artifacts
uses: actions/upload-artifact@v1
if: success() || failure()
with:
name: test-artifacts
path: artifacts
lint:
runs-on: ubuntu-18.04
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install \
lua5.3 \
ruby
- name: Clone DFHack
uses: actions/checkout@v1
with:
submodules: true
- name: Check whitespace
run: |
python travis/lint.py
- name: Check Authors.rst
run: |
python travis/authors-rst.py
- name: Check for missing documentation
run: |
python travis/script-docs.py
- name: Check Lua syntax
run: |
python travis/script-syntax.py --ext=lua --cmd="luac5.3 -p"
- name: Check Ruby syntax
run: |
python travis/script-syntax.py --ext=rb --cmd="ruby -c"
check-pr:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Check that PR is based on develop branch
env:
BASE_BRANCH: ${{ github.base_ref }}
run: |
echo "PR base branch: $BASE_BRANCH"
test "$BASE_BRANCH" = develop

@ -1,64 +0,0 @@
sudo: false
language: cpp
cache:
pip: true
directories:
- $HOME/DF-travis
- $HOME/lua53
addons:
apt:
packages: &default_packages
- libsdl-image1.2-dev
- libsdl-ttf2.0-dev
- libsdl1.2-dev
- libxml-libxml-perl
- libxml-libxslt-perl
- ninja-build
- zlib1g-dev
matrix:
include:
- env: GCC_VERSION=4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- *default_packages
- gcc-4.8
- g++-4.8
before_install:
- export DF_VERSION=$(sh travis/get-df-version.sh)
- export DF_FOLDER="$HOME/DF-travis/$DF_VERSION/df_linux"
- pip install --user "sphinx==1.4" "requests[security]"
- sh travis/build-lua.sh
- sh travis/download-df.sh
script:
- export PATH="$PATH:$HOME/lua53/bin"
- git tag tmp-travis-build
- sh travis/git-info.sh
- sphinx-build -qW -j3 . docs/html
- python travis/pr-check-base.py
- python travis/lint.py
- python travis/authors-rst.py
- python travis/script-docs.py
- python travis/script-syntax.py --ext=lua --cmd="luac5.3 -p"
- python travis/script-syntax.py --ext=rb --cmd="ruby -c"
- mkdir build-travis
- cd build-travis
- cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DBUILD_TESTS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
- ninja -j3 install
- mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init
- cd ..
- python travis/run-tests.py --headless --keep-status "$DF_FOLDER"
- python travis/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt"
- cat "$DF_FOLDER/test_status.json"
before_cache:
- cat "$DF_FOLDER/stderr.log"
- rm -rf "$DF_FOLDER"
notifications:
email: false
irc:
channels:
- "chat.freenode.net#dfhack"
on_success: change
on_failure: always

@ -10,6 +10,12 @@ if("${CMAKE_GENERATOR}" STREQUAL Ninja)
endif() endif()
endif() endif()
if(NOT("${CMAKE_VERSION}" VERSION_LESS 3.12))
# make ZLIB_ROOT work in CMake >= 3.12
# https://cmake.org/cmake/help/git-stage/policy/CMP0074.html
cmake_policy(SET CMP0074 NEW)
endif()
# Set up build types # Set up build types
if(CMAKE_CONFIGURATION_TYPES) if(CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "List of supported configuration types" FORCE) set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "List of supported configuration types" FORCE)
@ -174,8 +180,8 @@ endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.47.04") set(DF_VERSION "0.47.04")
set(DFHACK_RELEASE "beta1") set(DFHACK_RELEASE "r1")
set(DFHACK_PRERELEASE TRUE) set(DFHACK_PRERELEASE FALSE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")

@ -1,6 +1,6 @@
# DFHack Readme # DFHack Readme
[![Build Status](https://travis-ci.org/DFHack/dfhack.svg?branch=develop)](https://travis-ci.org/DFHack/dfhack) [![Build Status](https://github.com/DFHack/dfhack/workflows/Build/badge.svg?event=push)](https://github.com/DFHack/dfhack/actions?query=workflow%3ABuild)
[![Documentation Status](https://readthedocs.org/projects/dfhack/badge)](https://dfhack.readthedocs.org) [![Documentation Status](https://readthedocs.org/projects/dfhack/badge)](https://dfhack.readthedocs.org)
[![License](https://img.shields.io/badge/license-ZLib-blue.svg)](https://en.wikipedia.org/wiki/Zlib_License) [![License](https://img.shields.io/badge/license-ZLib-blue.svg)](https://en.wikipedia.org/wiki/Zlib_License)

@ -174,7 +174,7 @@ def all_keybinds_documented():
plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read())) plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read()))
undocumented_binds = configured_binds - script_commands - plugin_binds undocumented_binds = configured_binds - script_commands - plugin_binds
if undocumented_binds: if undocumented_binds:
raise ValueError('The following DFHack commands have undocumented' raise ValueError('The following DFHack commands have undocumented '
'keybindings: {}'.format(sorted(undocumented_binds))) 'keybindings: {}'.format(sorted(undocumented_binds)))

@ -32,6 +32,7 @@
// Based on original Protocol Buffers design by // Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others. // Sanjay Ghemawat, Jeff Dean, and others.
#include <fstream>
#include <stack> #include <stack>
#include <google/protobuf/stubs/hash.h> #include <google/protobuf/stubs/hash.h>

@ -245,6 +245,7 @@ enable \
dwarfmonitor \ dwarfmonitor \
mousequery \ mousequery \
autogems \ autogems \
autodump \
automelt \ automelt \
autotrade \ autotrade \
buildingplan \ buildingplan \

@ -34,6 +34,7 @@ Clayton Hughes
Clément Vuchener cvuchener Clément Vuchener cvuchener
Dan Amlund danamlund Dan Amlund danamlund
Daniel Brooks db48x Daniel Brooks db48x
David Nilsolm
David Corbett dscorbett David Corbett dscorbett
David Seguin dseguin David Seguin dseguin
David Timm dtimm David Timm dtimm
@ -116,6 +117,7 @@ Priit Laes plaes
Putnam Putnam3145 Putnam Putnam3145
Quietust quietust _Q Quietust quietust _Q
Raidau Raidau Raidau Raidau
Ralph Bisschops ralpha
Ramblurr Ramblurr Ramblurr Ramblurr
rampaging-poet rampaging-poet
Raoul van Putten Raoul van Putten

@ -1292,6 +1292,12 @@ Items module
Returns true *x,y,z* of the item, or *nil* if invalid; may be not equal to item.pos if in inventory. Returns true *x,y,z* of the item, or *nil* if invalid; may be not equal to item.pos if in inventory.
* ``dfhack.items.getBookTitle(item)``
Returns the title of the "book" item, or an empty string if the item isn't a "book" or it doesn't
have a title. A "book" is a codex or a tool item that has page or writings improvements, such as
scrolls and quires.
* ``dfhack.items.getDescription(item, type[, decorate])`` * ``dfhack.items.getDescription(item, type[, decorate])``
Returns the string description of the item, as produced by the ``getItemDescription`` Returns the string description of the item, as produced by the ``getItemDescription``

@ -2298,16 +2298,19 @@ by spaces.
Options: Options:
:``-t``: Select trees only (exclude shrubs) :``-t``: Tree: Select trees only (exclude shrubs)
:``-s``: Select shrubs only (exclude trees) :``-s``: Shrub: Select shrubs only (exclude trees)
:``-c``: Clear designations instead of setting them :``-f``: Farming: Designate only shrubs that yield seeds for farming. Implies -s
:``-x``: Apply selected action to all plants except those specified (invert :``-c``: Clear: Clear designations instead of setting them
:``-x``: eXcept: Apply selected action to all plants except those specified (invert
selection) selection)
:``-a``: Select every type of plant (obeys ``-t``/``-s``) :``-a``: All: Select every type of plant (obeys ``-t``/``-s``/``-f``)
:``-v``: Lists the number of (un)designations per plant :``-v``: Verbose: Lists the number of (un)designations per plant
:``-n *``: Number: Designate up to * (an integer number) plants of each species
Specifying both ``-t`` and ``-s`` will have no effect. If no plant IDs are specified, Specifying both ``-t`` and ``-s`` or ``-f`` will have no effect. If no plant IDs are
all valid plant IDs will be listed. specified, all valid plant IDs will be listed, with ``-t``, ``-s``, and ``-f``
restricting the list to trees, shrubs, and farmable shrubs, respectively.
.. note:: .. note::
@ -2318,6 +2321,12 @@ all valid plant IDs will be listed.
plant gatherer to walk there and do nothing (except clearing the plant gatherer to walk there and do nothing (except clearing the
designation). See :issue:`1479` for details. designation). See :issue:`1479` for details.
The implementation another known deficiency: it's incapable of detecting that
raw definitions that specify a seed extraction reaction for the structural part
but has no other use for it cannot actually yield any seeds, as the part is
never used (parts of :bug:`6940`, e.g. Red Spinach), even though DF
collects it, unless there's a workshop reaction to do it (which there isn't
in vanilla).
.. _infiniteSky: .. _infiniteSky:

@ -39,9 +39,24 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future # Future
# 0.47.04-r1
## Fixes ## Fixes
- Fixed translation of certain types of in-game names
- Fixed a crash in ``find()`` for some types when no world is loaded
- `autogems`: fixed an issue with binned gems being ignored in linked stockpiles
- `stocks`: fixed display of book titles
- `tweak` embark-profile-name: fixed handling of the native shift+space key - `tweak` embark-profile-name: fixed handling of the native shift+space key
## Misc Improvements
- ``dfhack.init-example``: enabled `autodump`
- `getplants`: added switches for designations for farming seeds and for max number designated per plant
- `manipulator`: added intrigue to displayed skills
- `search`: added support for the fortress mode justice screen
## API
- Added ``Items::getBookTitle`` to get titles of books. Catches titles buried in improvements, unlike getDescription.
## Lua ## Lua
- ``pairs()`` now returns available class methods for DF types - ``pairs()`` now returns available class methods for DF types

@ -235,6 +235,7 @@ def generate_changelog(all=False):
dev_entries[entry.dev_version][entry.section].append(entry) dev_entries[entry.dev_version][entry.section].append(entry)
consolidate_changelog(stable_entries) consolidate_changelog(stable_entries)
consolidate_changelog(dev_entries)
print_changelog(versions, stable_entries, 'docs/_auto/news.rst') print_changelog(versions, stable_entries, 'docs/_auto/news.rst')
print_changelog(versions, dev_entries, 'docs/_auto/news-dev.rst') print_changelog(versions, dev_entries, 'docs/_auto/news-dev.rst')

@ -12,10 +12,11 @@
namespace { namespace {
template<class T> template<class T>
inline T &_toref(T &r) { return r; } inline T *_toptr(T &r) { return &r; }
template<class T> template<class T>
inline T &_toref(T *&p) { return *p; } inline T *_toptr(T *&p) { return p; }
} }
#define _fieldptr(ptr, fn) (ptr) ? _toptr((ptr)->fn) : NULL
#define INIT_GLOBAL_FUNCTION_PREFIX \ #define INIT_GLOBAL_FUNCTION_PREFIX \
DFHack::VersionInfo *global_table_ = DFHack::Core::getInstance().vinfo.get(); \ DFHack::VersionInfo *global_table_ = DFHack::Core::getInstance().vinfo.get(); \

@ -1766,6 +1766,7 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = {
WRAPM(Items, getContainer), WRAPM(Items, getContainer),
WRAPM(Items, getHolderBuilding), WRAPM(Items, getHolderBuilding),
WRAPM(Items, getHolderUnit), WRAPM(Items, getHolderUnit),
WRAPM(Items, getBookTitle),
WRAPM(Items, getDescription), WRAPM(Items, getDescription),
WRAPM(Items, isCasteMaterial), WRAPM(Items, isCasteMaterial),
WRAPM(Items, getSubtypeCount), WRAPM(Items, getSubtypeCount),

@ -68,6 +68,8 @@ namespace DFHack
IDTYPE_UNION IDTYPE_UNION
}; };
// pointer flags (bitfield), stored in the count field of struct_field_info
// if mode is POINTER.
enum pointer_identity_flags { enum pointer_identity_flags {
PTRFLAG_IS_ARRAY = 1, PTRFLAG_IS_ARRAY = 1,
PTRFLAG_HAS_BAD_POINTERS = 2, PTRFLAG_HAS_BAD_POINTERS = 2,
@ -164,7 +166,12 @@ namespace DFHack
// Bitfields // Bitfields
struct bitfield_item_info { struct bitfield_item_info {
// the name of the field, or null if the field is unnamed
const char *name; const char *name;
// size is positive for defined fields, zero for bits past the end
// of the field, and negative for padding on multi-bit fields
//
// ex. if bits[2].size is -2, then bits[0].size is at least 3
int size; int size;
}; };

@ -159,6 +159,11 @@ DFHACK_EXPORT df::unit *getHolderUnit(df::item *item);
/// Returns the true position of the item. /// Returns the true position of the item.
DFHACK_EXPORT df::coord getPosition(df::item *item); DFHACK_EXPORT df::coord getPosition(df::item *item);
/// Returns the title of a codex or "tool", either as the codex title or as the title of the
/// first page or writing it has that has a non blank title. An empty string is returned if
/// no title is found (which is the case for everything that isn't a "book").
DFHACK_EXPORT std::string getBookTitle(df::item *item);
/// Returns the description string of the item. /// Returns the description string of the item.
DFHACK_EXPORT std::string getDescription(df::item *item, int type = 0, bool decorate = false); DFHACK_EXPORT std::string getDescription(df::item *item, int type = 0, bool decorate = false);

@ -59,6 +59,8 @@ using namespace std;
#include "df/general_ref_unit_holderst.h" #include "df/general_ref_unit_holderst.h"
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/item.h" #include "df/item.h"
#include "df/item_bookst.h"
#include "df/item_toolst.h"
#include "df/item_type.h" #include "df/item_type.h"
#include "df/itemdef_ammost.h" #include "df/itemdef_ammost.h"
#include "df/itemdef_armorst.h" #include "df/itemdef_armorst.h"
@ -74,6 +76,9 @@ using namespace std;
#include "df/itemdef_toyst.h" #include "df/itemdef_toyst.h"
#include "df/itemdef_trapcompst.h" #include "df/itemdef_trapcompst.h"
#include "df/itemdef_weaponst.h" #include "df/itemdef_weaponst.h"
#include "df/itemimprovement.h"
#include "df/itemimprovement_pagesst.h"
#include "df/itemimprovement_writingst.h"
#include "df/job_item.h" #include "df/job_item.h"
#include "df/mandate.h" #include "df/mandate.h"
#include "df/map_block.h" #include "df/map_block.h"
@ -90,6 +95,7 @@ using namespace std;
#include "df/viewscreen_itemst.h" #include "df/viewscreen_itemst.h"
#include "df/world.h" #include "df/world.h"
#include "df/world_site.h" #include "df/world_site.h"
#include "df/written_content.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums; using namespace df::enums;
@ -658,7 +664,7 @@ df::coord Items::getPosition(df::item *item)
switch (ref->type) switch (ref->type)
{ {
case specific_ref_type::VERMIN_ESCAPED_PET: case specific_ref_type::VERMIN_ESCAPED_PET:
return ref->data.VERMIN_ESCAPED_PET->pos; return ref->data.vermin->pos;
default: default:
break; break;
@ -681,6 +687,87 @@ static void addQuality(std::string &tmp, int quality)
} }
} }
// It's not impossible the functionality of this operation is provided by one of the unmapped item functions.
std::string Items::getBookTitle(df::item *item)
{
CHECK_NULL_POINTER(item);
std::string tmp;
if (item->getType() == df::item_type::BOOK)
{
auto book = virtual_cast<df::item_bookst>(item);
if (book->title != "")
{
return book->title;
}
else
{
for (size_t i = 0; i < book->improvements.size(); i++)
{
if (auto page = virtual_cast<df::itemimprovement_pagesst>(book->improvements[i]))
{
for (size_t k = 0; k < page->contents.size(); k++)
{
df::written_content *contents = world->written_contents.all[page->contents[k]];
if (contents->title != "")
{
return contents->title;
}
}
}
else if (auto writing = virtual_cast<df::itemimprovement_writingst>(book->improvements[i]))
{
for (size_t k = 0; k < writing->contents.size(); k++)
{
df::written_content *contents = world->written_contents.all[writing->contents[k]];
if (contents->title != "")
{
return contents->title;
}
}
}
}
}
}
else if (item->getType() == df::item_type::TOOL)
{
auto book = virtual_cast<df::item_toolst>(item);
if (book->hasToolUse(df::tool_uses::CONTAIN_WRITING))
{
for (size_t i = 0; i < book->improvements.size(); i++)
{
if (auto page = virtual_cast<df::itemimprovement_pagesst>(book->improvements[i]))
{
for (size_t k = 0; k < page->contents.size(); k++)
{
df::written_content *contents = world->written_contents.all[page->contents[k]];
if (contents->title != "")
{
return contents->title;
}
}
}
else if (auto writing = virtual_cast<df::itemimprovement_writingst>(book->improvements[i]))
{
for (size_t k = 0; k < writing->contents.size(); k++)
{
df::written_content *contents = world->written_contents.all[writing->contents[k]];
if (contents->title != "")
{
return contents->title;
}
}
}
}
}
}
return "";
}
std::string Items::getDescription(df::item *item, int type, bool decorate) std::string Items::getDescription(df::item *item, int type, bool decorate)
{ {
CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(item);

@ -311,7 +311,7 @@ void DFHack::Job::disconnectJobItem(df::job *job, df::job_item_ref *ref) {
auto ref = item->specific_refs[refIndex]; auto ref = item->specific_refs[refIndex];
if (ref->type == df::specific_ref_type::JOB) { if (ref->type == df::specific_ref_type::JOB) {
if (ref->data.JOB == job) { if (ref->data.job == job) {
vector_erase_at(item->specific_refs, refIndex); vector_erase_at(item->specific_refs, refIndex);
delete ref; delete ref;
} else { } else {
@ -579,7 +579,7 @@ bool DFHack::Job::attachJobItem(df::job *job, df::item *item,
auto item_link = new df::specific_ref(); auto item_link = new df::specific_ref();
item_link->type = specific_ref_type::JOB; item_link->type = specific_ref_type::JOB;
item_link->data.JOB = job; item_link->data.job = job;
item->specific_refs.push_back(item_link); item->specific_refs.push_back(item_link);
auto job_link = new df::job_item_ref(); auto job_link = new df::job_item_ref();

@ -180,14 +180,11 @@ string Translation::TranslateName(const df::language_name * name, bool inEnglish
word.append(*world->raws.language.translations[name->language]->words[name->words[1]]); word.append(*world->raws.language.translations[name->language]->words[name->words[1]]);
addNameWord(out, word); addNameWord(out, word);
} }
if (name->words[5] >= 0) word.clear();
{ for (int i = 2; i <= 5; i++)
word.clear(); if (name->words[i] >= 0)
for (int i = 2; i <= 5; i++) word.append(*world->raws.language.translations[name->language]->words[name->words[i]]);
if (name->words[i] >= 0) addNameWord(out, word);
word.append(*world->raws.language.translations[name->language]->words[name->words[i]]);
addNameWord(out, word);
}
if (name->words[6] >= 0) if (name->words[6] >= 0)
{ {
word.clear(); word.clear();
@ -206,18 +203,17 @@ string Translation::TranslateName(const df::language_name * name, bool inEnglish
word.append(world->raws.language.words[name->words[1]]->forms[name->parts_of_speech[1]]); word.append(world->raws.language.words[name->words[1]]->forms[name->parts_of_speech[1]]);
addNameWord(out, word); addNameWord(out, word);
} }
if (name->words[5] >= 0) if (name->words[2] >= 0 || name->words[3] >= 0 || name->words[4] >= 0 || name->words[5] >= 0)
{ {
if (out.length() > 0) if (out.length() > 0)
out.append(" the"); out.append(" the");
else else
out.append("The"); out.append("The");
}
for (int i = 2; i <= 5; i++) for (int i = 2; i <= 5; i++)
{ {
if (name->words[i] >= 0) if (name->words[i] >= 0)
addNameWord(out, world->raws.language.words[name->words[i]]->forms[name->parts_of_speech[i]]); addNameWord(out, world->raws.language.words[name->words[i]]->forms[name->parts_of_speech[i]]);
}
} }
if (name->words[6] >= 0) if (name->words[6] >= 0)
{ {

@ -58,6 +58,7 @@ using namespace std;
#include "df/entity_position_assignment.h" #include "df/entity_position_assignment.h"
#include "df/entity_raw.h" #include "df/entity_raw.h"
#include "df/entity_raw_flags.h" #include "df/entity_raw_flags.h"
#include "df/identity_type.h"
#include "df/game_mode.h" #include "df/game_mode.h"
#include "df/histfig_entity_link_positionst.h" #include "df/histfig_entity_link_positionst.h"
#include "df/historical_entity.h" #include "df/historical_entity.h"
@ -200,13 +201,23 @@ void Units::setNickname(df::unit *unit, std::string nick)
if (auto identity = getFigureIdentity(figure)) if (auto identity = getFigureIdentity(figure))
{ {
auto id_hfig = df::historical_figure::find(identity->histfig_id); df::historical_figure *id_hfig = NULL;
switch (identity->type) {
case df::identity_type::HidingCurse:
case df::identity_type::Identity:
case df::identity_type::FalseIdentity:
break; // We want the nickname to end up in the identity
case df::identity_type::Unk_1: // Guess, but that's how it worked in the past
case df::identity_type::TrueName:
case df::identity_type::Unk_4: // Pure guess, as this is a new case, still unseen
id_hfig = df::historical_figure::find(identity->histfig_id);
break;
}
if (id_hfig) if (id_hfig)
{ {
// Even DF doesn't do this bit, because it's apparently
// only used for demons masquerading as gods, so you
// can't ever change their nickname in-game.
Translation::setNickname(&id_hfig->name, nick); Translation::setNickname(&id_hfig->name, nick);
} }
else else
@ -247,7 +258,7 @@ bool Units::isHidingCurse(df::unit *unit)
if (!unit->job.hunt_target) if (!unit->job.hunt_target)
{ {
auto identity = Units::getIdentity(unit); auto identity = Units::getIdentity(unit);
if (identity && identity->unk_4c == 0) if (identity && identity->type == df::identity_type::HidingCurse)
return true; return true;
} }
@ -722,12 +733,11 @@ double Units::getAge(df::unit *unit, bool true_age)
double birth_time = unit->birth_year + unit->birth_time/year_ticks; double birth_time = unit->birth_year + unit->birth_time/year_ticks;
double cur_time = *cur_year + *cur_year_tick / year_ticks; double cur_time = *cur_year + *cur_year_tick / year_ticks;
if (!true_age && unit->curse_year >= 0) if (!true_age) {
{ if (auto identity = getIdentity(unit)) {
if (auto identity = getIdentity(unit)) if (identity->birth_year != -1) {
{ birth_time = identity->birth_year + identity->birth_second / year_ticks;
if (identity->histfig_id < 0) }
birth_time = identity->birth_year + identity->birth_second/year_ticks;
} }
} }

@ -1 +1 @@
Subproject commit 596e3032417fb3d8dbe7d62a544ddd415ae2d89f Subproject commit 0792fc0202fb6a04bfdaa262bc36a3b14c8581e5

@ -167,6 +167,16 @@ void create_jobs() {
stockpiled.insert(item->id); stockpiled.insert(item->id);
piled[item->getMaterialIndex()] += 1; piled[item->getMaterialIndex()] += 1;
} }
else if (item->flags.bits.container) {
std::vector<df::item*> binneditems;
Items::getContainedItems(item, &binneditems);
for (df::item *it : binneditems) {
if (valid_gem(it)) {
stockpiled.insert(it->id);
piled[it->getMaterialIndex()] += 1;
}
}
}
} }
// Decrement current jobs from all linked workshops, not just this one. // Decrement current jobs from all linked workshops, not just this one.

@ -67,6 +67,11 @@ bool Checker::queue_item(const QueueItem & item, CheckedStructure cs)
auto offset = uintptr_t(item.ptr) - uintptr_t(prev->first); auto offset = uintptr_t(item.ptr) - uintptr_t(prev->first);
if (!prev->second.second.has_type_at_offset(cs, offset)) if (!prev->second.second.has_type_at_offset(cs, offset))
{ {
if (offset == 0 && cs.identity == df::identity_traits<void *>::get())
{
FAIL("unknown pointer is " << prev->second.second.identity->getFullName() << ", previously seen at " << prev->second.first);
return false;
}
// TODO // TODO
FAIL("TODO: handle merging structures: " << item.path << " overlaps " << prev->second.first << " (backward)"); FAIL("TODO: handle merging structures: " << item.path << " overlaps " << prev->second.first << " (backward)");
return false; return false;

@ -129,7 +129,6 @@ bool CheckedStructure::has_type_at_offset(const CheckedStructure & type, size_t
auto st = dynamic_cast<struct_identity *>(identity); auto st = dynamic_cast<struct_identity *>(identity);
if (!st) if (!st)
{ {
UNEXPECTED;
return false; return false;
} }

@ -38,16 +38,53 @@ enum class selectability {
Unselected Unselected
}; };
//selectability selectablePlant(color_ostream &out, const df::plant_raw *plant) // Determination of whether seeds can be collected is somewhat messy:
selectability selectablePlant(const df::plant_raw *plant) // - Growths of type SEEDS are collected only if they are edible either raw or cooked.
// - Growths of type PLANT_GROWTH are collected provided the STOCKPILE_PLANT_GROWTH
// flag is set.
// The two points above were determined through examination of the DF code, while the ones
// below were determined through examination of the behavior of bugged, working, and
// RAW manipulated shrubs on embarks.
// - If seeds are defined as explicit growths, they are the source of seeds, overriding
// the default STRUCTURAL part as the source.
// - If a growth has the reaction that extracts seeds as a side effect of other
// processing (brewing, eating raw, etc.), this overrides the STRUCTURAL part as the
// source of seeds. However, for some reason it does not produce seeds from eating
// raw growths unless the structural part is collected (at least for shrubs: other
// processing was not examined).
// - If a growth has a (non vanilla) reaction that produces seeds, seeds are produced,
// provided there is something (such as a workshop order) that triggers it.
// The code below is satisfied with detection of a seed producing reaction, and does not
// detect the bugged case where a seed extraction process is defined but doesn't get
// triggered. Such a process can be triggered either as a side effect of other
// processing, or as a workshop reaction, and it would be overkill for this code to
// try to determine if a workshop reaction exists and has been permitted for the played
// race.
// There are two bugged cases of this in the current vanilla RAWs:
// Both Red Spinach and Elephant-Head Amaranth have the seed extraction reaction
// explicitly specified for the structural part, but no other use for it. This causes
// these parts to be collected (a valid reaction is defined), but remain unusable. This
// is one of the issues in bug 6940 on the bug tracker (the others cases are detected and
// result in the plants not being usable for farming or even collectable at all).
//selectability selectablePlant(color_ostream &out, const df::plant_raw *plant, bool farming)
selectability selectablePlant(const df::plant_raw *plant, bool farming)
{ {
const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant->material_defs.type_basic_mat, plant->material_defs.idx_basic_mat); const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant->material_defs.type_basic_mat, plant->material_defs.idx_basic_mat);
bool outOfSeason = false; bool outOfSeason = false;
selectability result = selectability::Nonselectable;
if (plant->flags.is_set(plant_raw_flags::TREE)) if (plant->flags.is_set(plant_raw_flags::TREE))
{ {
// out.print("%s is a selectable tree\n", plant->id.c_str()); // out.print("%s is a selectable tree\n", plant->id.c_str());
return selectability::Selectable; if (farming)
{
return selectability::Nonselectable;
}
else
{
return selectability::Selectable;
}
} }
else if (plant->flags.is_set(plant_raw_flags::GRASS)) else if (plant->flags.is_set(plant_raw_flags::GRASS))
{ {
@ -55,11 +92,26 @@ selectability selectablePlant(const df::plant_raw *plant)
return selectability::Grass; return selectability::Grass;
} }
if (farming && plant->material_defs.type_seed == -1)
{
return selectability::Nonselectable;
}
if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) ||
basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED))
{ {
// out.print("%s is edible\n", plant->id.c_str()); // out.print("%s is edible\n", plant->id.c_str());
return selectability::Selectable; if (farming)
{
if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW))
{
result = selectability::Selectable;
}
}
else
{
return selectability::Selectable;
}
} }
if (plant->flags.is_set(plant_raw_flags::THREAD) || if (plant->flags.is_set(plant_raw_flags::THREAD) ||
@ -69,14 +121,28 @@ selectability selectablePlant(const df::plant_raw *plant)
plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL))
{ {
// out.print("%s is thread/mill/extract\n", plant->id.c_str()); // out.print("%s is thread/mill/extract\n", plant->id.c_str());
return selectability::Selectable; if (farming)
{
result = selectability::Selectable;
}
else
{
return selectability::Selectable;
}
} }
if (basic_mat.material->reaction_product.id.size() > 0 || if (basic_mat.material->reaction_product.id.size() > 0 ||
basic_mat.material->reaction_class.size() > 0) basic_mat.material->reaction_class.size() > 0)
{ {
// out.print("%s has a reaction\n", plant->id.c_str()); // out.print("%s has a reaction\n", plant->id.c_str());
return selectability::Selectable; if (farming)
{
result = selectability::Selectable;
}
else
{
return selectability::Selectable;
}
} }
for (size_t i = 0; i < plant->growths.size(); i++) for (size_t i = 0; i < plant->growths.size(); i++)
@ -91,16 +157,37 @@ selectability selectablePlant(const df::plant_raw *plant)
(plant->growths[i]->item_type == df::item_type::PLANT_GROWTH && (plant->growths[i]->item_type == df::item_type::PLANT_GROWTH &&
growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now...
{ {
bool seedSource = plant->growths[i]->item_type == df::item_type::SEEDS;
if (plant->growths[i]->item_type == df::item_type::PLANT_GROWTH)
{
for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++)
{
if (growth_mat.material->reaction_product.material.mat_type[k] == plant->material_defs.type_seed &&
growth_mat.material->reaction_product.material.mat_index[k] == plant->material_defs.idx_seed)
{
seedSource = true;
break;
}
}
}
if (*cur_year_tick >= plant->growths[i]->timing_1 && if (*cur_year_tick >= plant->growths[i]->timing_1 &&
(plant->growths[i]->timing_2 == -1 || (plant->growths[i]->timing_2 == -1 ||
*cur_year_tick <= plant->growths[i]->timing_2)) *cur_year_tick <= plant->growths[i]->timing_2))
{ {
// out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str()); // out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str());
return selectability::Selectable; if (!farming || seedSource)
{
return selectability::Selectable;
}
} }
else else
{ {
outOfSeason = true; if (!farming || seedSource)
{
outOfSeason = true;
}
} }
} }
} }
@ -133,7 +220,7 @@ selectability selectablePlant(const df::plant_raw *plant)
else else
{ {
// out.printerr("%s cannot be gathered\n", plant->id.c_str()); // out.printerr("%s cannot be gathered\n", plant->id.c_str());
return selectability::Nonselectable; return result;
} }
} }
@ -143,8 +230,8 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
std::vector<selectability> plantSelections; std::vector<selectability> plantSelections;
std::vector<size_t> collectionCount; std::vector<size_t> collectionCount;
set<string> plantNames; set<string> plantNames;
bool deselect = false, exclude = false, treesonly = false, shrubsonly = false, all = false, verbose = false; bool deselect = false, exclude = false, treesonly = false, shrubsonly = false, all = false, verbose = false, farming = false;
size_t maxCount = 999999;
int count = 0; int count = 0;
plantSelections.resize(world->raws.plants.all.size()); plantSelections.resize(world->raws.plants.all.size());
@ -160,20 +247,43 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
for (size_t i = 0; i < parameters.size(); i++) for (size_t i = 0; i < parameters.size(); i++)
{ {
if(parameters[i] == "help" || parameters[i] == "?") if (parameters[i] == "help" || parameters[i] == "?")
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
else if(parameters[i] == "-t") else if (parameters[i] == "-t")
treesonly = true; treesonly = true;
else if(parameters[i] == "-s") else if (parameters[i] == "-s")
shrubsonly = true; shrubsonly = true;
else if(parameters[i] == "-c") else if (parameters[i] == "-c")
deselect = true; deselect = true;
else if(parameters[i] == "-x") else if (parameters[i] == "-x")
exclude = true; exclude = true;
else if(parameters[i] == "-a") else if (parameters[i] == "-a")
all = true; all = true;
else if(parameters[i] == "-v") else if (parameters[i] == "-v")
verbose = true; verbose = true;
else if (parameters[i] == "-f")
farming = true;
else if (parameters[i] == "-n")
{
if (parameters.size() > i + 1)
{
maxCount = atoi(parameters[i + 1].c_str());
if (maxCount >= 1)
{
i++; // We've consumed the next parameter, so we need to progress the iterator.
}
else
{
out.printerr("-n requires a positive integer parameter!\n");
return CR_WRONG_USAGE;
}
}
else
{
out.printerr("-n requires a positive integer parameter!\n");
return CR_WRONG_USAGE;
}
}
else else
plantNames.insert(parameters[i]); plantNames.insert(parameters[i]);
} }
@ -182,6 +292,11 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
out.printerr("Cannot specify both -t and -s at the same time!\n"); out.printerr("Cannot specify both -t and -s at the same time!\n");
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
} }
if (treesonly && farming)
{
out.printerr("Cannot specify both -t and -f at the same time!\n");
return CR_WRONG_USAGE;
}
if (all && exclude) if (all && exclude)
{ {
out.printerr("Cannot specify both -a and -x at the same time!\n"); out.printerr("Cannot specify both -a and -x at the same time!\n");
@ -200,14 +315,14 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
df::plant_raw *plant = world->raws.plants.all[i]; df::plant_raw *plant = world->raws.plants.all[i];
if (all) if (all)
{ {
// plantSelections[i] = selectablePlant(out, plant); // plantSelections[i] = selectablePlant(out, plant, farming);
plantSelections[i] = selectablePlant(plant); plantSelections[i] = selectablePlant(plant, farming);
} }
else if (plantNames.find(plant->id) != plantNames.end()) else if (plantNames.find(plant->id) != plantNames.end())
{ {
plantNames.erase(plant->id); plantNames.erase(plant->id);
// plantSelections[i] = selectablePlant(out, plant); // plantSelections[i] = selectablePlant(out, plant, farming);
plantSelections[i] = selectablePlant(plant); plantSelections[i] = selectablePlant(plant, farming);
switch (plantSelections[i]) switch (plantSelections[i])
{ {
case selectability::Grass: case selectability::Grass:
@ -215,7 +330,14 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
break; break;
case selectability::Nonselectable: case selectability::Nonselectable:
out.printerr("%s does not have any parts that can be gathered\n", plant->id.c_str()); if (farming)
{
out.printerr("%s does not have any parts that can be gathered for seeds for farming\n", plant->id.c_str());
}
else
{
out.printerr("%s does not have any parts that can be gathered\n", plant->id.c_str());
}
break; break;
case selectability::OutOfSeason: case selectability::OutOfSeason:
@ -255,8 +377,8 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
for (size_t i = 0; i < world->raws.plants.all.size(); i++) for (size_t i = 0; i < world->raws.plants.all.size(); i++)
{ {
df::plant_raw *plant = world->raws.plants.all[i]; df::plant_raw *plant = world->raws.plants.all[i];
// switch (selectablePlant(out, plant)) // switch (selectablePlant(out, plant, farming))
switch (selectablePlant(plant)) switch (selectablePlant(plant, farming))
{ {
case selectability::Grass: case selectability::Grass:
case selectability::Nonselectable: case selectability::Nonselectable:
@ -264,13 +386,21 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
case selectability::OutOfSeason: case selectability::OutOfSeason:
{ {
out.print("* (shrub) %s - %s is out of season\n", plant->id.c_str(), plant->name.c_str()); if (!treesonly)
{
out.print("* (shrub) %s - %s is out of season\n", plant->id.c_str(), plant->name.c_str());
}
break; break;
} }
case selectability::Selectable: case selectability::Selectable:
{ {
out.print("* (%s) %s - %s\n", plant->flags.is_set(plant_raw_flags::TREE) ? "tree" : "shrub", plant->id.c_str(), plant->name.c_str()); if ((treesonly && plant->flags.is_set(plant_raw_flags::TREE)) ||
(shrubsonly && !plant->flags.is_set(plant_raw_flags::TREE)) ||
(!treesonly && !shrubsonly)) // 'farming' weeds out trees when determining selectability, so no need to test that explicitly
{
out.print("* (%s) %s - %s\n", plant->flags.is_set(plant_raw_flags::TREE) ? "tree" : "shrub", plant->id.c_str(), plant->name.c_str());
}
break; break;
} }
@ -311,6 +441,8 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
continue; continue;
if (cur->designation[x][y].bits.hidden) if (cur->designation[x][y].bits.hidden)
continue; continue;
if (collectionCount[plant->material] >= maxCount)
continue;
if (deselect && Designations::unmarkPlant(plant)) if (deselect && Designations::unmarkPlant(plant))
{ {
collectionCount[plant->material]++; collectionCount[plant->material]++;
@ -350,12 +482,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCom
"Options:\n" "Options:\n"
" -t - Tree: Select trees only (exclude shrubs)\n" " -t - Tree: Select trees only (exclude shrubs)\n"
" -s - Shrub: Select shrubs only (exclude trees)\n" " -s - Shrub: Select shrubs only (exclude trees)\n"
" -f - Farming: Designate only shrubs that yield seeds for farming. Implies -s\n"
" -c - Clear: Clear designations instead of setting them\n" " -c - Clear: Clear designations instead of setting them\n"
" -x - eXcept: Apply selected action to all plants except those specified\n" " -x - eXcept: Apply selected action to all plants except those specified\n"
" -a - All: Select every type of plant (obeys -t/-s)\n" " -a - All: Select every type of plant (obeys -t/-s/-f)\n"
" -v - Verbose: lists the number of (un)designations per plant\n" " -v - Verbose: List the number of (un)designations per plant\n"
"Specifying both -t and -s will have no effect.\n" " -n * - Number: Designate up to * (an integer number) plants of each species\n"
"If no plant IDs are specified, all valid plant IDs will be listed.\n" "Specifying both -t and -s or -f will have no effect.\n"
"If no plant IDs are specified, and the -a switch isn't given, all valid plant\n"
"IDs will be listed with -t, -s, and -f restricting the list to trees, shrubs,\n"
"and farmable shrubs, respectively.\n"
)); ));
return CR_OK; return CR_OK;
} }

@ -242,6 +242,7 @@ const SkillColumn columns[] = {
{16, 3, profession::NONE, unit_labor::NONE, job_skill::FLATTERY, "Fl"}, {16, 3, profession::NONE, unit_labor::NONE, job_skill::FLATTERY, "Fl"},
{16, 3, profession::NONE, unit_labor::NONE, job_skill::CONSOLE, "Cs"}, {16, 3, profession::NONE, unit_labor::NONE, job_skill::CONSOLE, "Cs"},
{16, 3, profession::NONE, unit_labor::NONE, job_skill::PACIFY, "Pc"}, {16, 3, profession::NONE, unit_labor::NONE, job_skill::PACIFY, "Pc"},
{16, 3, profession::NONE, unit_labor::NONE, job_skill::INTRIGUE, "Sc"},
// Noble // Noble
{17, 5, profession::TRADER, unit_labor::NONE, job_skill::APPRAISAL, "Ap"}, {17, 5, profession::TRADER, unit_labor::NONE, job_skill::APPRAISAL, "Ap"},
{17, 5, profession::ADMINISTRATOR, unit_labor::NONE, job_skill::ORGANIZATION, "Or"}, {17, 5, profession::ADMINISTRATOR, unit_labor::NONE, job_skill::ORGANIZATION, "Or"},

@ -798,13 +798,13 @@ static command_result orders_clear_command(color_ostream & out)
{ {
delete condition; delete condition;
} }
if (order->anon_1) if (order->items)
{ {
for (auto anon_1 : *order->anon_1) for (auto item : *order->items)
{ {
delete anon_1; delete item;
} }
delete order->anon_1; delete order->items;
} }
delete order; delete order;

@ -1693,20 +1693,7 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques
using df::global::cur_year; using df::global::cur_year;
using df::global::cur_year_tick; using df::global::cur_year_tick;
int year_ticks = 403200; send_unit->set_age(Units::getAge(unit, false));
int birth_time = unit->birth_year * year_ticks + unit->birth_time;
int cur_time = *cur_year * year_ticks + *cur_year_tick;
if (unit->curse_year >= 0)
{
if (auto identity = Units::getIdentity(unit))
{
if (identity->histfig_id < 0)
birth_time = identity->birth_year * year_ticks + identity->birth_second;
}
}
send_unit->set_age(cur_time - birth_time);
ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color()); ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color());
send_unit->set_flags1(unit->flags1.whole); send_unit->set_flags1(unit->flags1.whole);

@ -25,6 +25,7 @@
#include "df/viewscreen_buildinglistst.h" #include "df/viewscreen_buildinglistst.h"
#include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_joblistst.h" #include "df/viewscreen_joblistst.h"
#include "df/viewscreen_justicest.h"
#include "df/viewscreen_kitchenprefst.h" #include "df/viewscreen_kitchenprefst.h"
#include "df/viewscreen_layer_militaryst.h" #include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_layer_noblelistst.h" #include "df/viewscreen_layer_noblelistst.h"
@ -2327,6 +2328,85 @@ IMPLEMENT_HOOKS(df::viewscreen_layer_stone_restrictionst, stone_search);
// END: Stone status screen search // END: Stone status screen search
// //
//
// START: Justice screen conviction search
//
typedef search_generic<df::viewscreen_justicest, df::unit*> justice_conviction_search_base;
class justice_conviction_search : public justice_conviction_search_base
{
public:
bool can_init (df::viewscreen_justicest *screen)
{
return screen->cur_column == df::viewscreen_justicest::ConvictChoices;
}
string get_element_description (df::unit *unit) const
{
return get_unit_description(unit);
}
void render() const
{
print_search_option(37);
}
vector<df::unit*> *get_primary_list()
{
return &viewscreen->convict_choices;
}
virtual int32_t *get_viewscreen_cursor()
{
return &viewscreen->cursor_right;
}
};
IMPLEMENT_HOOKS(df::viewscreen_justicest, justice_conviction_search);
//
// END: Justice screen conviction search
//
//
// START: Justice screen interrogation search
//
typedef search_generic<df::viewscreen_justicest, df::unit*> justice_interrogation_search_base;
class justice_interrogation_search : public justice_interrogation_search_base
{
public:
bool can_init (df::viewscreen_justicest *screen)
{
return screen->cur_column == df::viewscreen_justicest::InterrogateChoices;
}
string get_element_description (df::unit *unit) const
{
return get_unit_description(unit);
}
void render() const
{
print_search_option(37);
}
vector<df::unit*> *get_primary_list()
{
return &viewscreen->interrogate_choices;
}
virtual int32_t *get_viewscreen_cursor()
{
return &viewscreen->cursor_right;
}
};
IMPLEMENT_HOOKS(df::viewscreen_justicest, justice_interrogation_search);
//
// END: Justice screen conviction search
//
#define SEARCH_HOOKS \ #define SEARCH_HOOKS \
HOOK_ACTION(unitlist_search_hook) \ HOOK_ACTION(unitlist_search_hook) \
@ -2350,6 +2430,8 @@ IMPLEMENT_HOOKS(df::viewscreen_layer_stone_restrictionst, stone_search);
HOOK_ACTION(location_assign_occupation_search_hook) \ HOOK_ACTION(location_assign_occupation_search_hook) \
HOOK_ACTION(kitchen_pref_search_hook) \ HOOK_ACTION(kitchen_pref_search_hook) \
HOOK_ACTION(stone_search_hook) \ HOOK_ACTION(stone_search_hook) \
HOOK_ACTION(justice_conviction_search_hook) \
HOOK_ACTION(justice_interrogation_search_hook) \
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)

@ -417,7 +417,7 @@ void StockpileSerializer::serialize_list_itemdef ( FuncWriteExport add_value, s
{ {
const df::itemdef *a = items.at ( i ); const df::itemdef *a = items.at ( i );
// skip procedurally generated items // skip procedurally generated items
if ( a->base_flags.is_set ( 0 ) ) continue; if ( a->base_flags.is_set ( df::itemdef_flags::GENERATED ) ) continue;
ItemTypeInfo ii; ItemTypeInfo ii;
if ( !ii.decode ( type, i ) ) continue; if ( !ii.decode ( type, i ) ) continue;
add_value ( ii.getToken() ); add_value ( ii.getToken() );

@ -30,7 +30,7 @@
#include "df/ui_advmode.h" #include "df/ui_advmode.h"
DFHACK_PLUGIN("stocks"); DFHACK_PLUGIN("stocks");
#define PLUGIN_VERSION 0.12 #define PLUGIN_VERSION 0.13
REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(world);
@ -179,8 +179,8 @@ static map<df::item *, bool> items_in_cages;
static df::job *get_item_job(df::item *item) static df::job *get_item_job(df::item *item)
{ {
auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); auto ref = Items::getSpecificRef(item, specific_ref_type::JOB);
if (ref && ref->data.JOB) if (ref && ref->data.job)
return ref->data.JOB; return ref->data.job;
return nullptr; return nullptr;
} }
@ -248,7 +248,11 @@ static string get_keywords(df::item *item)
static string get_item_label(df::item *item, bool trim = false) static string get_item_label(df::item *item, bool trim = false)
{ {
auto label = Items::getDescription(item, 0, false); auto label = Items::getBookTitle(item);
if (label == "")
{
label = Items::getDescription(item, 0, false);
}
if (trim && item->getType() == item_type::BIN) if (trim && item->getType() == item_type::BIN)
{ {
auto pos = label.find("<#"); auto pos = label.find("<#");
@ -562,7 +566,11 @@ class StockListColumn : public ListColumn<T>
if (!ListColumn<T>::showEntry(entry, search_tokens)) if (!ListColumn<T>::showEntry(entry, search_tokens))
return false; return false;
string item_name = toLower(Items::getDescription(entry->elem->entries[0], 0, false)); string item_name = toLower(Items::getBookTitle(entry->elem->entries[0]));
if (item_name == "")
{
item_name = toLower(Items::getDescription(entry->elem->entries[0], 0, false));
}
if ((match_start || match_end) && raw_search.size() > item_name.size()) if ((match_start || match_end) && raw_search.size() > item_name.size())
return false; return false;
@ -1008,12 +1016,12 @@ private:
if (item->flags.bits.in_job) if (item->flags.bits.in_job)
{ {
auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); auto ref = Items::getSpecificRef(item, specific_ref_type::JOB);
if (ref && ref->data.JOB) if (ref && ref->data.job)
{ {
if (ref->data.JOB->job_type == job_type::Eat || ref->data.JOB->job_type == job_type::Drink) if (ref->data.job->job_type == job_type::Eat || ref->data.job->job_type == job_type::Drink)
return pos; return pos;
auto unit = Job::getWorker(ref->data.JOB); auto unit = Job::getWorker(ref->data.job);
if (unit) if (unit)
return unit->pos; return unit->pos;
} }

@ -1151,10 +1151,10 @@ static bool itemInRealJob(df::item *item)
return false; return false;
auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); auto ref = Items::getSpecificRef(item, specific_ref_type::JOB);
if (!ref || !ref->data.JOB) if (!ref || !ref->data.job)
return true; return true;
return ENUM_ATTR(job_type, type, ref->data.JOB->job_type) return ENUM_ATTR(job_type, type, ref->data.job->job_type)
!= job_type_class::Hauling; != job_type_class::Hauling;
} }

@ -1 +1 @@
Subproject commit 8618cd0b0a17935fe07f329b249726cda61f5bdf Subproject commit 2079b9fb69b8b4db48aa35ec54a96f5cca7cc8ef

@ -1,2 +0,0 @@
#!/bin/sh
git log --pretty="commit %h (parents: %p): %s" -1

@ -1,18 +0,0 @@
import os, sys
repo = os.environ.get('TRAVIS_REPO_SLUG', 'dfhack/dfhack').lower()
branch = os.environ.get('TRAVIS_BRANCH', 'master')
try:
pr_id = int(os.environ.get('TRAVIS_PULL_REQUEST', 'false'))
except ValueError:
print('Not a pull request')
sys.exit(0)
print('Pull request %s#%i' % (repo, pr_id))
if repo != 'dfhack/dfhack':
print('Not in dfhack/dfhack')
sys.exit(0)
if branch != 'develop':
print('Not based on develop branch')
sys.exit(1)
else:
print('Ok')
sys.exit(0)