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

Conflicts:
	library/modules/Items.cpp
	library/modules/MapCache.cpp
	library/modules/Maps.cpp
	library/modules/Materials.cpp
	library/modules/Translation.cpp
	plugins/CMakeLists.txt
develop
JapaMala 2014-08-08 20:54:44 +05:30
commit 36435ca8cf
41 changed files with 1220 additions and 1039 deletions

@ -58,8 +58,8 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif() endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.34.11") set(DF_VERSION "0.40.05")
SET(DFHACK_RELEASE "r5" CACHE STRING "Current release revision.") SET(DFHACK_RELEASE "r0" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}")

@ -382,10 +382,10 @@ ul.auto-toc {
<h2><a class="toc-backref" href="#id5">How to get the code</a></h2> <h2><a class="toc-backref" href="#id5">How to get the code</a></h2>
<p>DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. <p>DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.</p> Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.</p>
<p>The code resides here: <a class="reference external" href="https://github.com/peterix/dfhack">https://github.com/peterix/dfhack</a></p> <p>The code resides here: <a class="reference external" href="https://github.com/DFHack/dfhack">https://github.com/DFHack/dfhack</a></p>
<p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p> <p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p>
<pre class="literal-block"> <pre class="literal-block">
git clone git://github.com/peterix/dfhack.git git clone git://github.com/DFHack/dfhack.git
cd dfhack cd dfhack
git submodule init git submodule init
git submodule update git submodule update
@ -553,14 +553,14 @@ make install
You will need some sort of Windows port of git, or a GUI. Some examples:</p> You will need some sort of Windows port of git, or a GUI. Some examples:</p>
<blockquote> <blockquote>
<ul class="simple"> <ul class="simple">
<li><a class="reference external" href="http://code.google.com/p/msysgit/">http://code.google.com/p/msysgit/</a> - this is a command line version of git for windows. Most tutorials on git usage will apply.</li> <li><a class="reference external" href="http://msysgit.github.io/">http://msysgit.github.io/</a> - this is a command line version of git for windows. Most tutorials on git usage will apply.</li>
<li><a class="reference external" href="http://code.google.com/p/tortoisegit/">http://code.google.com/p/tortoisegit/</a> - this puts a pretty, graphical face on top of msysgit :)</li> <li><a class="reference external" href="http://code.google.com/p/tortoisegit/">http://code.google.com/p/tortoisegit/</a> - this puts a pretty, graphical face on top of msysgit :)</li>
</ul> </ul>
</blockquote> </blockquote>
<p>The code resides here: <a class="reference external" href="https://github.com/peterix/dfhack">https://github.com/peterix/dfhack</a></p> <p>The code resides here: <a class="reference external" href="https://github.com/DFHack/dfhack">https://github.com/DFHack/dfhack</a></p>
<p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p> <p>If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:</p>
<pre class="literal-block"> <pre class="literal-block">
git clone git://github.com/peterix/dfhack.git git clone git://github.com/DFHack/dfhack.git
cd dfhack cd dfhack
git submodule init git submodule init
git submodule update git submodule update
@ -577,13 +577,7 @@ to your binary search PATH so the tool can be later run from anywhere.</p>
<p>You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient. <p>You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient.
Grab it from Microsoft's site.</p> Grab it from Microsoft's site.</p>
<p>You'll also need the Visual Studio 2010 SP1 update.</p> <p>You'll also need the Visual Studio 2010 SP1 update.</p>
<p>For the code generation parts, you'll need perl and XML::LibXML. You can install them like this:</p> <p>For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. Strawberry Perl works nicely for this: http://strawberryperl.com/</p>
<ul class="simple">
<li>download and install strawberry perl from <a class="reference external" href="http://strawberryperl.com/">http://strawberryperl.com/</a></li>
<li>reboot so that the system can pick up the new binary path</li>
<li>open a cmd.exe window and run &quot;cpan XML::LibXML&quot; (obviously without the quotes). This can take a while to complete.</li>
<li>Same with &quot;cpan XML::LibXSLT&quot;.</li>
</ul>
<p>If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p> <p>If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.</p>
</div> </div>
<div class="section" id="id3"> <div class="section" id="id3">
@ -666,7 +660,7 @@ I'll make <em>you</em> fix it ;)</p>
the IRC channel to pull your code in. I'll review it and see if there the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.</p> are any problems. I'll fix them if they are minor.</p>
<p>Fixes are higher in priority. If you want to work on something, but <p>Fixes are higher in priority. If you want to work on something, but
don't know what, check out <a class="reference external" href="http://github.com/peterix/dfhack/issues">http://github.com/peterix/dfhack/issues</a> -- don't know what, check out <a class="reference external" href="http://github.com/DFHack/dfhack/issues">http://github.com/DFHack/dfhack/issues</a> --
this is also a good place to dump new ideas and/or bugs that need this is also a good place to dump new ideas and/or bugs that need
fixing.</p> fixing.</p>
</div> </div>
@ -678,13 +672,13 @@ to look at machine code without getting crazy :)</p>
<p>Good windows tools include:</p> <p>Good windows tools include:</p>
<ul class="simple"> <ul class="simple">
<li>Cheat Engine</li> <li>Cheat Engine</li>
<li>IDA Pro (the free version)</li> <li>IDA Pro 5.0 (freely available for non-commercial use)</li>
</ul> </ul>
<p>Good linux tools:</p> <p>Good linux tools:</p>
<ul class="simple"> <ul class="simple">
<li>angavrilov's df-structures gui (visit us on IRC for details).</li> <li>angavrilov's df-structures gui (visit us on IRC for details).</li>
<li>edb (Evan's Debugger)</li> <li>edb (Evan's Debugger)</li>
<li>IDA Pro running under wine.</li> <li>IDA Pro 5.0 running under Wine</li>
<li>Some of the tools residing in the <tt class="docutils literal">legacy</tt> dfhack branch.</li> <li>Some of the tools residing in the <tt class="docutils literal">legacy</tt> dfhack branch.</li>
</ul> </ul>
<p>Using publicly known information and analyzing the game's data is preferred.</p> <p>Using publicly known information and analyzing the game's data is preferred.</p>

@ -16,11 +16,11 @@ How to get the code
DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help. Having a 'git' package installed is the minimal requirement, but some sort of git gui or git integration for your favorite text editor/IDE will certainly help.
The code resides here: https://github.com/peterix/dfhack The code resides here: https://github.com/DFHack/dfhack
If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:: If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address::
git clone git://github.com/peterix/dfhack.git git clone git://github.com/DFHack/dfhack.git
cd dfhack cd dfhack
git submodule init git submodule init
git submodule update git submodule update
@ -165,14 +165,14 @@ How to get the code
DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git. DFHack doesn't have any kind of system of code snapshots in place, so you will have to get code from the github repository using git.
You will need some sort of Windows port of git, or a GUI. Some examples: You will need some sort of Windows port of git, or a GUI. Some examples:
* http://code.google.com/p/msysgit/ - this is a command line version of git for windows. Most tutorials on git usage will apply. * http://msysgit.github.io/ - this is a command line version of git for windows. Most tutorials on git usage will apply.
* http://code.google.com/p/tortoisegit/ - this puts a pretty, graphical face on top of msysgit :) * http://code.google.com/p/tortoisegit/ - this puts a pretty, graphical face on top of msysgit :)
The code resides here: https://github.com/peterix/dfhack The code resides here: https://github.com/DFHack/dfhack
If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address:: If you just want to compile DFHack or work on it by contributing patches, it's quite enough to clone from the read-only address::
git clone git://github.com/peterix/dfhack.git git clone git://github.com/DFHack/dfhack.git
cd dfhack cd dfhack
git submodule init git submodule init
git submodule update git submodule update
@ -194,12 +194,7 @@ Grab it from Microsoft's site.
You'll also need the Visual Studio 2010 SP1 update. You'll also need the Visual Studio 2010 SP1 update.
For the code generation parts, you'll need perl and XML::LibXML. You can install them like this: For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. Strawberry Perl works nicely for this: http://strawberryperl.com/
* download and install strawberry perl from http://strawberryperl.com/
* reboot so that the system can pick up the new binary path
* open a cmd.exe window and run "cpan XML::LibXML" (obviously without the quotes). This can take a while to complete.
* Same with "cpan XML::LibXSLT".
If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages. If you already have a different version of perl (for example the one from cygwin), you can run into some trouble. Either remove the other perl install from PATH, or install libxml and libxslt for it instead. Strawberry perl works though and has all the required packages.
@ -307,7 +302,7 @@ the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor. are any problems. I'll fix them if they are minor.
Fixes are higher in priority. If you want to work on something, but Fixes are higher in priority. If you want to work on something, but
don't know what, check out http://github.com/peterix/dfhack/issues -- don't know what, check out http://github.com/DFHack/dfhack/issues --
this is also a good place to dump new ideas and/or bugs that need this is also a good place to dump new ideas and/or bugs that need
fixing. fixing.
@ -321,13 +316,13 @@ to look at machine code without getting crazy :)
Good windows tools include: Good windows tools include:
* Cheat Engine * Cheat Engine
* IDA Pro (the free version) * IDA Pro 5.0 (freely available for non-commercial use)
Good linux tools: Good linux tools:
* angavrilov's df-structures gui (visit us on IRC for details). * angavrilov's df-structures gui (visit us on IRC for details).
* edb (Evan's Debugger) * edb (Evan's Debugger)
* IDA Pro running under wine. * IDA Pro 5.0 running under Wine
* Some of the tools residing in the ``legacy`` dfhack branch. * Some of the tools residing in the ``legacy`` dfhack branch.
Using publicly known information and analyzing the game's data is preferred. Using publicly known information and analyzing the game's data is preferred.

@ -61,6 +61,8 @@ using namespace DFHack;
#include "df/world_data.h" #include "df/world_data.h"
#include "df/interfacest.h" #include "df/interfacest.h"
#include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include <df/graphic.h> #include <df/graphic.h>
#include <stdio.h> #include <stdio.h>
@ -394,12 +396,14 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
} }
string findScript(string path, string name) { string findScript(string path, string name) {
//first try the save folder if it exists if (df::global::world) {
string save = World::ReadWorldFolder(); //first try the save folder if it exists
if ( save != "" ) { string save = World::ReadWorldFolder();
string file = path + "/data/save/" + save + "/raw/scripts/" + name; if ( save != "" ) {
if (fileExists(file)) { string file = path + "/data/save/" + save + "/raw/scripts/" + name;
return file; if (fileExists(file)) {
return file;
}
} }
} }
string file = path + "/raw/scripts/" + name; string file = path + "/raw/scripts/" + name;
@ -839,6 +843,12 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
static void run_dfhack_init(color_ostream &out, Core *core) static void run_dfhack_init(color_ostream &out, Core *core)
{ {
if (!df::global::world || !df::global::ui || !df::global::gview)
{
out.printerr("Key globals are missing, skipping loading dfhack.init.\n");
return;
}
if (!core->loadScriptFile(out, "dfhack.init", true)) if (!core->loadScriptFile(out, "dfhack.init", true))
{ {
core->runCommand(out, "gui/no-dfhack-init"); core->runCommand(out, "gui/no-dfhack-init");
@ -1260,10 +1270,23 @@ void Core::doUpdate(color_ostream &out, bool first_update)
if (first_update) if (first_update)
onStateChange(out, SC_CORE_INITIALIZED); onStateChange(out, SC_CORE_INITIALIZED);
// find the current viewscreen
df::viewscreen *screen = NULL;
if (df::global::gview)
{
screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
}
bool is_load_save =
strict_virtual_cast<df::viewscreen_loadgamest>(screen) ||
strict_virtual_cast<df::viewscreen_savegamest>(screen);
// detect if the game was loaded or unloaded in the meantime // detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL; void *new_wdata = NULL;
void *new_mapdata = NULL; void *new_mapdata = NULL;
if (df::global::world) if (df::global::world && !is_load_save)
{ {
df::world_data *wdata = df::global::world->world_data; df::world_data *wdata = df::global::world->world_data;
// when the game is unloaded, world_data isn't deleted, but its contents are // when the game is unloaded, world_data isn't deleted, but its contents are
@ -1305,16 +1328,10 @@ void Core::doUpdate(color_ostream &out, bool first_update)
} }
// detect if the viewscreen changed // detect if the viewscreen changed
if (df::global::gview) if (screen != top_viewscreen)
{ {
df::viewscreen *screen = &df::global::gview->view; top_viewscreen = screen;
while (screen->child) onStateChange(out, SC_VIEWSCREEN_CHANGED);
screen = screen->child;
if (screen != top_viewscreen)
{
top_viewscreen = screen;
onStateChange(out, SC_VIEWSCREEN_CHANGED);
}
} }
if (df::global::pause_state) if (df::global::pause_state)
@ -1407,7 +1424,9 @@ void Core::onUpdate(color_ostream &out)
} }
static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_change_event event) { static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_change_event event) {
//TODO: use different separators for windows if (!df::global::world)
return;
//TODO: use different separators for windows
#ifdef _WIN32 #ifdef _WIN32
static const std::string separator = "\\"; static const std::string separator = "\\";
#else #else

@ -66,7 +66,7 @@ distribution.
#include "df/item.h" #include "df/item.h"
#include "df/material.h" #include "df/material.h"
#include "df/viewscreen.h" #include "df/viewscreen.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/nemesis_record.h" #include "df/nemesis_record.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/historical_entity.h" #include "df/historical_entity.h"

@ -76,7 +76,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/squad.h" #include "df/squad.h"
#include "df/squad_position.h" #include "df/squad_position.h"
#include "df/death_info.h" #include "df/incident.h"
#include "BasicApi.pb.h" #include "BasicApi.pb.h"
@ -283,7 +283,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (unit->counters.death_id >= 0) if (unit->counters.death_id >= 0)
{ {
info->set_death_id(unit->counters.death_id); info->set_death_id(unit->counters.death_id);
if (auto death = df::death_info::find(unit->counters.death_id)) if (auto death = df::incident::find(unit->counters.death_id))
info->set_death_flags(death->flags.whole); info->set_death_flags(death->flags.whole);
} }
@ -455,7 +455,7 @@ static command_result ListEnums(color_ostream &stream,
BITFIELD(cie_add_tag_mask1); BITFIELD(cie_add_tag_mask1);
BITFIELD(cie_add_tag_mask2); BITFIELD(cie_add_tag_mask2);
describe_bitfield<df::death_info::T_flags>(out->mutable_death_info_flags()); describe_bitfield<df::incident::T_flags>(out->mutable_death_info_flags());
ENUM(profession); ENUM(profession);

@ -41,7 +41,7 @@ namespace df
{ {
struct nemesis_record; struct nemesis_record;
struct burrow; struct burrow;
struct assumed_identity; struct identity;
struct historical_entity; struct historical_entity;
struct entity_position_assignment; struct entity_position_assignment;
struct entity_position; struct entity_position;
@ -213,7 +213,7 @@ DFHACK_EXPORT df::item *getContainer(df::unit *unit);
DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick);
DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit); DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit);
DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit); DFHACK_EXPORT df::identity *getIdentity(df::unit *unit);
DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit); DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isHidingCurse(df::unit *unit); DFHACK_EXPORT bool isHidingCurse(df::unit *unit);

@ -268,7 +268,7 @@ function found_offset(name,val)
if cval then if cval then
if cval ~= val then if cval ~= val then
error(string.format('Mismatch with the current value: %x',val)) error(string.format('Mismatch with the current value: %x != %x',val,cval))
end end
else else
dfhack.internal.setAddress(name, val) dfhack.internal.setAddress(name, val)

@ -237,6 +237,16 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
//out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick); //out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick);
*/ */
//tickQueue.clear(); //tickQueue.clear();
if (!df::global::item_next_id)
return;
if (!df::global::building_next_id)
return;
if (!df::global::job_next_id)
return;
if (!df::global::ui)
return;
if (!df::global::world)
return;
nextItem = *df::global::item_next_id; nextItem = *df::global::item_next_id;
nextBuilding = *df::global::building_next_id; nextBuilding = *df::global::building_next_id;
@ -291,6 +301,9 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
if ( !gameLoaded ) { if ( !gameLoaded ) {
return; return;
} }
if (!df::global::world)
return;
CoreSuspender suspender; CoreSuspender suspender;
int32_t tick = df::global::world->frame_counter; int32_t tick = df::global::world->frame_counter;
@ -316,6 +329,8 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
} }
static void manageTickEvent(color_ostream& out) { static void manageTickEvent(color_ostream& out) {
if (!df::global::world)
return;
unordered_set<EventHandler> toRemove; unordered_set<EventHandler> toRemove;
int32_t tick = df::global::world->frame_counter; int32_t tick = df::global::world->frame_counter;
while ( !tickQueue.empty() ) { while ( !tickQueue.empty() ) {
@ -342,6 +357,10 @@ static void manageTickEvent(color_ostream& out) {
} }
static void manageJobInitiatedEvent(color_ostream& out) { static void manageJobInitiatedEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::job_next_id)
return;
if ( lastJobId == -1 ) { if ( lastJobId == -1 ) {
lastJobId = *df::global::job_next_id - 1; lastJobId = *df::global::job_next_id - 1;
return; return;
@ -375,6 +394,8 @@ static int32_t getWorkerID(df::job* job) {
TODO: consider checking item creation / experience gain just in case TODO: consider checking item creation / experience gain just in case
*/ */
static void manageJobCompletedEvent(color_ostream& out) { static void manageJobCompletedEvent(color_ostream& out) {
if (!df::global::world)
return;
int32_t tick0 = eventLastTick[EventType::JOB_COMPLETED]; int32_t tick0 = eventLastTick[EventType::JOB_COMPLETED];
int32_t tick1 = df::global::world->frame_counter; int32_t tick1 = df::global::world->frame_counter;
@ -500,6 +521,8 @@ static void manageJobCompletedEvent(color_ostream& out) {
} }
static void manageUnitDeathEvent(color_ostream& out) { static void manageUnitDeathEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end());
for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) {
df::unit* unit = df::global::world->units.all[a]; df::unit* unit = df::global::world->units.all[a];
@ -520,6 +543,10 @@ static void manageUnitDeathEvent(color_ostream& out) {
} }
static void manageItemCreationEvent(color_ostream& out) { static void manageItemCreationEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::item_next_id)
return;
if ( nextItem >= *df::global::item_next_id ) { if ( nextItem >= *df::global::item_next_id ) {
return; return;
} }
@ -552,6 +579,10 @@ static void manageItemCreationEvent(color_ostream& out) {
} }
static void manageBuildingEvent(color_ostream& out) { static void manageBuildingEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::building_next_id)
return;
/* /*
* TODO: could be faster * TODO: could be faster
* consider looking at jobs: building creation / destruction * consider looking at jobs: building creation / destruction
@ -591,6 +622,8 @@ static void manageBuildingEvent(color_ostream& out) {
} }
static void manageConstructionEvent(color_ostream& out) { static void manageConstructionEvent(color_ostream& out) {
if (!df::global::world)
return;
//unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); //unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end());
multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end());
@ -626,6 +659,8 @@ static void manageConstructionEvent(color_ostream& out) {
} }
static void manageSyndromeEvent(color_ostream& out) { static void manageSyndromeEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end());
int32_t highestTime = -1; int32_t highestTime = -1;
for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) { for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) {
@ -653,6 +688,8 @@ static void manageSyndromeEvent(color_ostream& out) {
} }
static void manageInvasionEvent(color_ostream& out) { static void manageInvasionEvent(color_ostream& out) {
if (!df::global::ui)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end());
if ( df::global::ui->invasions.next_id <= nextInvasion ) if ( df::global::ui->invasions.next_id <= nextInvasion )
@ -666,6 +703,8 @@ static void manageInvasionEvent(color_ostream& out) {
} }
static void manageEquipmentEvent(color_ostream& out) { static void manageEquipmentEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end());
unordered_map<int32_t, InventoryItem> itemIdToInventoryItem; unordered_map<int32_t, InventoryItem> itemIdToInventoryItem;
@ -747,6 +786,8 @@ static void manageEquipmentEvent(color_ostream& out) {
} }
static void updateReportToRelevantUnits() { static void updateReportToRelevantUnits() {
if (!df::global::world)
return;
if ( df::global::world->frame_counter <= reportToRelevantUnitsTime ) if ( df::global::world->frame_counter <= reportToRelevantUnitsTime )
return; return;
reportToRelevantUnitsTime = df::global::world->frame_counter; reportToRelevantUnitsTime = df::global::world->frame_counter;
@ -767,6 +808,8 @@ static void updateReportToRelevantUnits() {
} }
static void manageReportEvent(color_ostream& out) { static void manageReportEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end());
std::vector<df::report*>& reports = df::global::world->status.reports; std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReport, false); size_t a = df::report::binsearch_index(reports, lastReport, false);
@ -795,6 +838,8 @@ static df::unit_wound* getWound(df::unit* attacker, df::unit* defender) {
} }
static void manageUnitAttackEvent(color_ostream& out) { static void manageUnitAttackEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end());
std::vector<df::report*>& reports = df::global::world->status.reports; std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false); size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false);
@ -929,6 +974,8 @@ static std::string getVerb(df::unit* unit, std::string reportStr) {
} }
static void manageInteractionEvent(color_ostream& out) { static void manageInteractionEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end());
std::vector<df::report*>& reports = df::global::world->status.reports; std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); size_t a = df::report::binsearch_index(reports, lastReportInteraction, false);

@ -1310,6 +1310,9 @@ void Gui::showAutoAnnouncement(
df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed) df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed)
{ {
if (!gview)
return NULL;
df::viewscreen * ws = &gview->view; df::viewscreen * ws = &gview->view;
while (ws && ws->child) while (ws && ws->child)
ws = ws->child; ws = ws->child;

@ -1158,7 +1158,7 @@ int Items::getItemBaseValue(int16_t item_type, int16_t item_subtype, int16_t mat
case item_type::MEAT: case item_type::MEAT:
case item_type::PLANT: case item_type::PLANT:
// case item_type::LEAVES: case item_type::PLANT_GROWTH:
case item_type::CHEESE: case item_type::CHEESE:
value = 2; value = 2;
break; break;

@ -253,15 +253,15 @@ void Maps::enableBlockUpdates(df::map_block *blk, bool flow, bool temperature)
blk->flags.bits.update_liquid_twice = true; blk->flags.bits.update_liquid_twice = true;
} }
//auto z_flags = world->map.z_level_flags; auto z_flags = world->map_extras.z_level_flags;
//int z_level = blk->map_pos.z; int z_level = blk->map_pos.z;
//if (z_flags && z_level >= 0 && z_level < world->map.z_count_block) if (z_flags && z_level >= 0 && z_level < world->map.z_count_block)
//{ {
// z_flags += z_level; z_flags += z_level;
// z_flags->bits.update = true; z_flags->bits.update = true;
// z_flags->bits.update_twice = true; z_flags->bits.update_twice = true;
//} }
} }
df::flow_info *Maps::spawnFlow(df::coord pos, df::flow_type type, int mat_type, int mat_index, int density) df::flow_info *Maps::spawnFlow(df::coord pos, df::flow_type type, int mat_type, int mat_index, int density)

@ -479,7 +479,6 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma
TEST(extract_bearing_fish, false); TEST(extract_bearing_fish, false);
TEST(extract_bearing_vermin, false); TEST(extract_bearing_vermin, false);
TEST(processable_to_vial, structural && FLAG(plant, plant_raw_flags::EXTRACT_VIAL)); TEST(processable_to_vial, structural && FLAG(plant, plant_raw_flags::EXTRACT_VIAL));
// TEST(processable_to_bag, structural && FLAG(plant, plant_raw_flags::LEAVES));
TEST(processable_to_barrel, structural && FLAG(plant, plant_raw_flags::EXTRACT_BARREL)); TEST(processable_to_barrel, structural && FLAG(plant, plant_raw_flags::EXTRACT_BARREL));
TEST(solid, !(MAT_FLAG(ALCOHOL_PLANT) || TEST(solid, !(MAT_FLAG(ALCOHOL_PLANT) ||
MAT_FLAG(ALCOHOL_CREATURE) || MAT_FLAG(ALCOHOL_CREATURE) ||

@ -46,6 +46,7 @@ using namespace df::enums;
using df::global::world; using df::global::world;
using df::global::d_init; using df::global::d_init;
using df::global::gametype;
bool Translation::IsValid () bool Translation::IsValid ()
{ {
@ -153,17 +154,17 @@ string Translation::TranslateName(const df::language_name * name, bool inEnglish
if (!name->nickname.empty()) if (!name->nickname.empty())
{ {
word = "`" + name->nickname + "'"; word = "`" + name->nickname + "'";
//switch (d_init ? d_init->nickname_dwarf : d_init_nickname::CENTRALIZE) switch ((d_init && gametype) ? d_init->nickname[*gametype] : d_init_nickname::CENTRALIZE)
//{ {
//case d_init_nickname::REPLACE_ALL: case d_init_nickname::REPLACE_ALL:
// out = word; out = word;
// return out; return out;
//case d_init_nickname::REPLACE_FIRST: case d_init_nickname::REPLACE_FIRST:
// out = ""; out = "";
// break; break;
//case d_init_nickname::CENTRALIZE: case d_init_nickname::CENTRALIZE:
// break; break;
//} }
addNameWord(out, word); addNameWord(out, word);
} }
} }

@ -61,7 +61,7 @@ using namespace std;
#include "df/entity_position.h" #include "df/entity_position.h"
#include "df/entity_position_assignment.h" #include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.h" #include "df/histfig_entity_link_positionst.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/burrow.h" #include "df/burrow.h"
#include "df/creature_raw.h" #include "df/creature_raw.h"
#include "df/caste_raw.h" #include "df/caste_raw.h"
@ -544,15 +544,15 @@ df::item *Units::getContainer(df::unit *unit)
return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM); return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM);
} }
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) static df::identity *getFigureIdentity(df::historical_figure *figure)
{ {
if (figure && figure->info && figure->info->reputation) if (figure && figure->info && figure->info->reputation)
return df::assumed_identity::find(figure->info->reputation->cur_identity); return df::identity::find(figure->info->reputation->cur_identity);
return NULL; return NULL;
} }
df::assumed_identity *Units::getIdentity(df::unit *unit) df::identity *Units::getIdentity(df::unit *unit)
{ {
CHECK_NULL_POINTER(unit); CHECK_NULL_POINTER(unit);

@ -1 +1 @@
Subproject commit 0d6d3ae7ea6f2836b72472739f696a94ec2a3f01 Subproject commit fc3c5ce3329edcdca5dadb4c1f8b03b0c98f0472

@ -13,16 +13,16 @@ endif()
OPTION(BUILD_ISOWORLD "Build isoworld (needs a checkout first)." OFF) OPTION(BUILD_ISOWORLD "Build isoworld (needs a checkout first)." OFF)
if(BUILD_ISOWORLD) if(BUILD_ISOWORLD)
add_subdirectory (isoworld) add_subdirectory (isoworld)
IF(UNIX) IF(UNIX)
if (APPLE) if (APPLE)
#TODO: add an OSX runner script #TODO: add an OSX runner script
else() else()
# On linux, copy our version of the df launch script which sets LD_PRELOAD # On linux, copy our version of the df launch script which sets LD_PRELOAD
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/runisoworld install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/runisoworld
DESTINATION .) DESTINATION .)
endif() endif()
ENDIF() ENDIF()
endif() endif()
OPTION(BUILD_DEV_PLUGINS "Build developer plugins." OFF) OPTION(BUILD_DEV_PLUGINS "Build developer plugins." OFF)
@ -38,12 +38,12 @@ endif()
OPTION(BUILD_MAPEXPORT "Build map exporter." ON) OPTION(BUILD_MAPEXPORT "Build map exporter." ON)
if (BUILD_MAPEXPORT) if (BUILD_MAPEXPORT)
add_subdirectory (mapexport) # add_subdirectory (mapexport)
endif() endif()
OPTION(BUILD_DWARFEXPORT "Build dwarf exporter." ON) OPTION(BUILD_DWARFEXPORT "Build dwarf exporter." ON)
if (BUILD_DWARFEXPORT) if (BUILD_DWARFEXPORT)
add_subdirectory (dwarfexport) # add_subdirectory (dwarfexport)
endif() endif()
OPTION(BUILD_RUBY "Build ruby binding." ON) OPTION(BUILD_RUBY "Build ruby binding." ON)
@ -82,84 +82,85 @@ SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE )
# Plugins # Plugins
OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON) OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON)
if (BUILD_SUPPORTED) if (BUILD_SUPPORTED)
# DFHACK_PLUGIN(3dveins 3dveins.cpp) DFHACK_PLUGIN(3dveins 3dveins.cpp)
# DFHACK_PLUGIN(add-spatter add-spatter.cpp) DFHACK_PLUGIN(add-spatter add-spatter.cpp)
# DFHACK_PLUGIN(advtools advtools.cpp) # DFHACK_PLUGIN(advtools advtools.cpp)
# DFHACK_PLUGIN(autodump autodump.cpp) DFHACK_PLUGIN(autochop autochop.cpp)
# DFHACK_PLUGIN(autolabor autolabor.cpp) DFHACK_PLUGIN(autodump autodump.cpp)
# DFHACK_PLUGIN(automaterial automaterial.cpp) DFHACK_PLUGIN(autolabor autolabor.cpp)
# DFHACK_PLUGIN(autotrade autotrade.cpp) DFHACK_PLUGIN(automaterial automaterial.cpp)
# DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(autotrade autotrade.cpp)
# DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(buildingplan buildingplan.cpp) DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(catsplosion catsplosion.cpp) DFHACK_PLUGIN(buildingplan buildingplan.cpp)
# DFHACK_PLUGIN(changeitem changeitem.cpp) DFHACK_PLUGIN(catsplosion catsplosion.cpp)
# DFHACK_PLUGIN(changelayer changelayer.cpp) DFHACK_PLUGIN(changeitem changeitem.cpp)
# DFHACK_PLUGIN(changevein changevein.cpp) DFHACK_PLUGIN(changelayer changelayer.cpp)
# DFHACK_PLUGIN(cleanconst cleanconst.cpp) DFHACK_PLUGIN(changevein changevein.cpp)
# DFHACK_PLUGIN(cleaners cleaners.cpp) DFHACK_PLUGIN(cleanconst cleanconst.cpp)
# DFHACK_PLUGIN(cleanowned cleanowned.cpp) DFHACK_PLUGIN(cleaners cleaners.cpp)
# DFHACK_PLUGIN(colonies colonies.cpp) DFHACK_PLUGIN(cleanowned cleanowned.cpp)
# DFHACK_PLUGIN(command-prompt command-prompt.cpp) DFHACK_PLUGIN(colonies colonies.cpp)
# DFHACK_PLUGIN(createitem createitem.cpp) DFHACK_PLUGIN(command-prompt command-prompt.cpp)
# DFHACK_PLUGIN(cursecheck cursecheck.cpp) DFHACK_PLUGIN(createitem createitem.cpp)
# DFHACK_PLUGIN(deramp deramp.cpp) DFHACK_PLUGIN(cursecheck cursecheck.cpp)
# DFHACK_PLUGIN(dig dig.cpp) DFHACK_PLUGIN(deramp deramp.cpp)
# DFHACK_PLUGIN(digFlood digFlood.cpp) DFHACK_PLUGIN(dig dig.cpp)
# add_subdirectory(diggingInvaders) DFHACK_PLUGIN(digFlood digFlood.cpp)
# DFHACK_PLUGIN(drybuckets drybuckets.cpp) add_subdirectory(diggingInvaders)
# DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp) DFHACK_PLUGIN(drybuckets drybuckets.cpp)
# DFHACK_PLUGIN(embark-tools embark-tools.cpp) DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
# DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(embark-tools embark-tools.cpp)
# DFHACK_PLUGIN(fastdwarf fastdwarf.cpp) DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(feature feature.cpp) DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
# DFHACK_PLUGIN(filltraffic filltraffic.cpp) DFHACK_PLUGIN(feature feature.cpp)
DFHACK_PLUGIN(filltraffic filltraffic.cpp)
# DFHACK_PLUGIN(fix-armory fix-armory.cpp) # DFHACK_PLUGIN(fix-armory fix-armory.cpp)
# DFHACK_PLUGIN(fixpositions fixpositions.cpp) DFHACK_PLUGIN(fixpositions fixpositions.cpp)
# DFHACK_PLUGIN(fixveins fixveins.cpp) DFHACK_PLUGIN(fixveins fixveins.cpp)
# DFHACK_PLUGIN(flows flows.cpp) DFHACK_PLUGIN(flows flows.cpp)
# DFHACK_PLUGIN(follow follow.cpp) DFHACK_PLUGIN(follow follow.cpp)
# DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp)
# DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(getplants getplants.cpp)
# DFHACK_PLUGIN(infiniteSky infiniteSky.cpp) DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
# DFHACK_PLUGIN(initflags initflags.cpp) DFHACK_PLUGIN(initflags initflags.cpp)
DFHACK_PLUGIN(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) DFHACK_PLUGIN(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote)
# DFHACK_PLUGIN(jobutils jobutils.cpp) DFHACK_PLUGIN(jobutils jobutils.cpp)
# DFHACK_PLUGIN(lair lair.cpp) DFHACK_PLUGIN(lair lair.cpp)
# DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua)
# DFHACK_PLUGIN(manipulator manipulator.cpp) DFHACK_PLUGIN(manipulator manipulator.cpp)
# DFHACK_PLUGIN(mode mode.cpp) DFHACK_PLUGIN(mode mode.cpp)
# DFHACK_PLUGIN(misery misery.cpp) DFHACK_PLUGIN(misery misery.cpp)
# DFHACK_PLUGIN(mousequery mousequery.cpp) DFHACK_PLUGIN(mousequery mousequery.cpp)
# DFHACK_PLUGIN(petcapRemover petcapRemover.cpp) DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
# DFHACK_PLUGIN(plants plants.cpp) DFHACK_PLUGIN(plants plants.cpp)
DFHACK_PLUGIN(probe probe.cpp) DFHACK_PLUGIN(probe probe.cpp)
# DFHACK_PLUGIN(prospector prospector.cpp) DFHACK_PLUGIN(prospector prospector.cpp)
# DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(regrass regrass.cpp)
DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader) DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader)
# DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename)
# add_subdirectory(rendermax) # add_subdirectory(rendermax)
# DFHACK_PLUGIN(resume resume.cpp) DFHACK_PLUGIN(resume resume.cpp)
# DFHACK_PLUGIN(reveal reveal.cpp) DFHACK_PLUGIN(reveal reveal.cpp)
# DFHACK_PLUGIN(search search.cpp) DFHACK_PLUGIN(search search.cpp)
# DFHACK_PLUGIN(seedwatch seedwatch.cpp) DFHACK_PLUGIN(seedwatch seedwatch.cpp)
# DFHACK_PLUGIN(showmood showmood.cpp) DFHACK_PLUGIN(showmood showmood.cpp)
# DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(steam-engine steam-engine.cpp) DFHACK_PLUGIN(steam-engine steam-engine.cpp)
# DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(stockpiles stockpiles.cpp) DFHACK_PLUGIN(stockpiles stockpiles.cpp)
# DFHACK_PLUGIN(stocks stocks.cpp) DFHACK_PLUGIN(stocks stocks.cpp)
# DFHACK_PLUGIN(strangemood strangemood.cpp) DFHACK_PLUGIN(strangemood strangemood.cpp)
# DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h) DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
# DFHACK_PLUGIN(treefarm treefarm.cpp) # DFHACK_PLUGIN(treefarm treefarm.cpp)
# DFHACK_PLUGIN(tubefill tubefill.cpp) DFHACK_PLUGIN(tubefill tubefill.cpp)
# DFHACK_PLUGIN(tweak tweak.cpp) DFHACK_PLUGIN(tweak tweak.cpp)
# DFHACK_PLUGIN(weather weather.cpp) DFHACK_PLUGIN(weather weather.cpp)
# DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(workNow workNow.cpp) DFHACK_PLUGIN(workNow workNow.cpp)
# DFHACK_PLUGIN(zone zone.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(zone zone.cpp LINK_LIBRARIES lua)
endif() endif()
# this is the skeleton plugin. If you want to make your own, make a copy and then change it # this is the skeleton plugin. If you want to make your own, make a copy and then change it

@ -0,0 +1,722 @@
// automatically chop trees
#include "uicommon.h"
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "DataDefs.h"
#include "TileTypes.h"
#include "df/world.h"
#include "df/map_block.h"
#include "df/tile_dig_designation.h"
#include "df/plant_raw.h"
#include "df/plant.h"
#include "df/ui.h"
#include "df/burrow.h"
#include "df/item_flags.h"
#include "df/item.h"
#include "df/items_other_id.h"
#include "df/viewscreen_dwarfmodest.h"
#include "modules/Screen.h"
#include "modules/Maps.h"
#include "modules/Burrows.h"
#include "modules/World.h"
#include "modules/MapCache.h"
#include "modules/Gui.h"
#include <set>
using std::string;
using std::vector;
using std::set;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::ui;
#define PLUGIN_VERSION 0.3
DFHACK_PLUGIN("autochop");
static bool autochop_enabled = false;
static int min_logs, max_logs;
static bool wait_for_threshold;
static PersistentDataItem config_autochop;
struct WatchedBurrow
{
int32_t id;
df::burrow *burrow;
WatchedBurrow(df::burrow *burrow) : burrow(burrow)
{
id = burrow->id;
}
};
class WatchedBurrows
{
public:
string getSerialisedIds()
{
validate();
stringstream burrow_ids;
bool append_started = false;
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
if (append_started)
burrow_ids << " ";
burrow_ids << it->id;
append_started = true;
}
return burrow_ids.str();
}
void clear()
{
burrows.clear();
}
void add(const int32_t id)
{
if (!isValidBurrow(id))
return;
WatchedBurrow wb(getBurrow(id));
burrows.push_back(wb);
}
void add(const string burrow_ids)
{
istringstream iss(burrow_ids);
int id;
while (iss >> id)
{
add(id);
}
}
bool isValidPos(const df::coord &plant_pos)
{
validate();
if (!burrows.size())
return true;
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
df::burrow *burrow = it->burrow;
if (Burrows::isAssignedTile(burrow, plant_pos))
return true;
}
return false;
}
bool isBurrowWatched(const df::burrow *burrow)
{
validate();
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
if (it->burrow == burrow)
return true;
}
return false;
}
private:
static bool isValidBurrow(const int32_t id)
{
return getBurrow(id);
}
static df::burrow *getBurrow(const int32_t id)
{
return df::burrow::find(id);
}
void validate()
{
for (auto it = burrows.begin(); it != burrows.end();)
{
if (!isValidBurrow(it->id))
it = burrows.erase(it);
else
++it;
}
}
vector<WatchedBurrow> burrows;
};
static WatchedBurrows watchedBurrows;
static void save_config()
{
config_autochop.val() = watchedBurrows.getSerialisedIds();
config_autochop.ival(0) = autochop_enabled;
config_autochop.ival(1) = min_logs;
config_autochop.ival(2) = max_logs;
config_autochop.ival(3) = wait_for_threshold;
}
static void initialize()
{
watchedBurrows.clear();
autochop_enabled = false;
min_logs = 80;
max_logs = 100;
wait_for_threshold = false;
config_autochop = World::GetPersistentData("autochop/config");
if (config_autochop.isValid())
{
watchedBurrows.add(config_autochop.val());
autochop_enabled = config_autochop.ival(0);
min_logs = config_autochop.ival(1);
max_logs = config_autochop.ival(2);
wait_for_threshold = config_autochop.ival(3);
}
else
{
config_autochop = World::AddPersistentData("autochop/config");
if (config_autochop.isValid())
save_config();
}
}
static int do_chop_designation(bool chop, bool count_only)
{
int count = 0;
for (size_t i = 0; i < world->plants.all.size(); i++)
{
const df::plant *plant = world->plants.all[i];
df::map_block *cur = Maps::getTileBlock(plant->pos);
if (!cur)
continue;
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
if (plant->flags.bits.is_shrub)
continue;
if (cur->designation[x][y].bits.hidden)
continue;
df::tiletype_material material = tileMaterial(cur->tiletype[x][y]);
if (material != tiletype_material::TREE)
continue;
if (!count_only && !watchedBurrows.isValidPos(plant->pos))
continue;
bool dirty = false;
if (chop && cur->designation[x][y].bits.dig == tile_dig_designation::No)
{
if (count_only)
{
++count;
}
else
{
cur->designation[x][y].bits.dig = tile_dig_designation::Default;
dirty = true;
}
}
if (!chop && cur->designation[x][y].bits.dig == tile_dig_designation::Default)
{
if (count_only)
{
++count;
}
else
{
cur->designation[x][y].bits.dig = tile_dig_designation::No;
dirty = true;
}
}
if (dirty)
{
cur->flags.bits.designated = true;
++count;
}
}
return count;
}
static bool is_valid_item(df::item *item)
{
for (size_t i = 0; i < item->general_refs.size(); i++)
{
df::general_ref *ref = item->general_refs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
return false;
case general_ref_type::UNIT_HOLDER:
return false;
case general_ref_type::BUILDING_HOLDER:
return false;
default:
break;
}
}
for (size_t i = 0; i < item->specific_refs.size(); i++)
{
df::specific_ref *ref = item->specific_refs[i];
if (ref->type == specific_ref_type::JOB)
{
// Ignore any items assigned to a job
return false;
}
}
return true;
}
static int get_log_count()
{
std::vector<df::item*> &items = world->items.other[items_other_id::IN_PLAY];
// Pre-compute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
#define F(x) bad_flags.bits.x = true;
F(dump); F(forbid); F(garbage_collect);
F(hostile); F(on_fire); F(rotten); F(trader);
F(in_building); F(construction); F(artifact);
F(spider_web); F(owned); F(in_job);
#undef F
size_t valid_count = 0;
for (size_t i = 0; i < items.size(); i++)
{
df::item *item = items[i];
if (item->getType() != item_type::WOOD)
continue;
if (item->flags.whole & bad_flags.whole)
continue;
if (!is_valid_item(item))
continue;
++valid_count;
}
return valid_count;
}
static void set_threshold_check(bool state)
{
wait_for_threshold = state;
save_config();
}
static void do_autochop()
{
int log_count = get_log_count();
if (wait_for_threshold)
{
if (log_count < min_logs)
{
set_threshold_check(false);
do_chop_designation(true, false);
}
}
else
{
if (log_count >= max_logs)
{
set_threshold_check(true);
do_chop_designation(false, false);
}
else
{
do_chop_designation(true, false);
}
}
}
class ViewscreenAutochop : public dfhack_viewscreen
{
public:
ViewscreenAutochop()
{
burrows_column.multiselect = true;
burrows_column.setTitle("Burrows");
burrows_column.bottom_margin = 3;
burrows_column.allow_search = false;
burrows_column.text_clip_at = 30;
populateBurrowsColumn();
message.clear();
}
void populateBurrowsColumn()
{
selected_column = 0;
auto last_selected_index = burrows_column.highlighted_index;
burrows_column.clear();
for (auto iter = ui->burrows.list.begin(); iter != ui->burrows.list.end(); iter++)
{
df::burrow* burrow = *iter;
auto elem = ListEntry<df::burrow *>(burrow->name, burrow);
elem.selected = watchedBurrows.isBurrowWatched(burrow);
burrows_column.add(elem);
}
burrows_column.fixWidth();
burrows_column.filterDisplay();
current_log_count = get_log_count();
marked_tree_count = do_chop_designation(false, true);
}
void change_min_logs(int delta)
{
if (!autochop_enabled)
return;
min_logs += delta;
if (min_logs < 0)
min_logs = 0;
if (min_logs > max_logs)
max_logs = min_logs;
}
void change_max_logs(int delta)
{
if (!autochop_enabled)
return;
max_logs += delta;
if (max_logs < min_logs)
min_logs = max_logs;
}
void feed(set<df::interface_key> *input)
{
bool key_processed = false;
message.clear();
switch (selected_column)
{
case 0:
key_processed = burrows_column.feed(input);
break;
}
if (key_processed)
{
if (input->count(interface_key::SELECT))
updateAutochopBurrows();
return;
}
if (input->count(interface_key::LEAVESCREEN))
{
save_config();
input->clear();
Screen::dismiss(this);
if (autochop_enabled)
do_autochop();
return;
}
else if (input->count(interface_key::CUSTOM_A))
{
autochop_enabled = !autochop_enabled;
}
else if (input->count(interface_key::CUSTOM_D))
{
int count = do_chop_designation(true, false);
message = "Trees marked for chop: " + int_to_string(count);
marked_tree_count = do_chop_designation(false, true);
}
else if (input->count(interface_key::CUSTOM_U))
{
int count = do_chop_designation(false, false);
message = "Trees unmarked: " + int_to_string(count);
marked_tree_count = do_chop_designation(false, true);
}
else if (input->count(interface_key::CUSTOM_H))
{
change_min_logs(-1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_H))
{
change_min_logs(-10);
}
else if (input->count(interface_key::CUSTOM_J))
{
change_min_logs(1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_J))
{
change_min_logs(10);
}
else if (input->count(interface_key::CUSTOM_K))
{
change_max_logs(-1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_K))
{
change_max_logs(-10);
}
else if (input->count(interface_key::CUSTOM_L))
{
change_max_logs(1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_L))
{
change_max_logs(10);
}
else if (enabler->tracking_on && enabler->mouse_lbut)
{
if (burrows_column.setHighlightByMouse())
{
selected_column = 0;
}
enabler->mouse_lbut = enabler->mouse_rbut = 0;
}
}
void render()
{
if (Screen::isDismissed(this))
return;
dfhack_viewscreen::render();
Screen::clear();
Screen::drawBorder(" Autochop ");
burrows_column.display(selected_column == 0);
int32_t y = gps->dimy - 3;
int32_t x = 2;
OutputHotkeyString(x, y, "Leave", "Esc");
x += 3;
OutputString(COLOR_YELLOW, x, y, message);
y = 3;
int32_t left_margin = burrows_column.getMaxItemWidth() + 3;
x = left_margin;
if (burrows_column.getSelectedElems().size() > 0)
{
OutputString(COLOR_GREEN, x, y, "Will chop in selected burrows", true, left_margin);
}
else
{
OutputString(COLOR_YELLOW, x, y, "Will chop from whole map", true, left_margin);
OutputString(COLOR_YELLOW, x, y, "Select from left to chop in specific burrows", true, left_margin);
}
++y;
OutputToggleString(x, y, "Autochop", "a", autochop_enabled, true, left_margin);
OutputHotkeyString(x, y, "Designate Now", "d", true, left_margin);
OutputHotkeyString(x, y, "Undesignate Now", "u", true, left_margin);
OutputHotkeyString(x, y, "Toggle Burrow", "Enter", true, left_margin);
if (autochop_enabled)
{
OutputLabelString(x, y, "Min Logs", "hjHJ", int_to_string(min_logs), true, left_margin);
OutputLabelString(x, y, "Max Logs", "klKL", int_to_string(max_logs), true, left_margin);
}
++y;
OutputString(COLOR_BROWN, x, y, "Current Counts", true, left_margin);
OutputString(COLOR_WHITE, x, y, "Current Logs: ");
OutputString(COLOR_GREEN, x, y, int_to_string(current_log_count), true, left_margin);
OutputString(COLOR_WHITE, x, y, "Marked Trees: ");
OutputString(COLOR_GREEN, x, y, int_to_string(marked_tree_count), true, left_margin);
}
std::string getFocusString() { return "autochop"; }
void updateAutochopBurrows()
{
watchedBurrows.clear();
vector<df::burrow *> v = burrows_column.getSelectedElems();
for_each_<df::burrow *>(v, [] (df::burrow *b) { watchedBurrows.add(b->id); });
}
private:
ListColumn<df::burrow *> burrows_column;
int selected_column;
int current_log_count;
int marked_tree_count;
MapExtras::MapCache mcache;
string message;
void validateColumn()
{
set_to_limit(selected_column, 0);
}
void resize(int32_t x, int32_t y)
{
dfhack_viewscreen::resize(x, y);
burrows_column.resize();
}
};
struct autochop_hook : public df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool isInDesignationMenu()
{
using namespace df::enums::ui_sidebar_mode;
return (ui->main.mode == DesignateChopTrees);
}
void sendKey(const df::interface_key &key)
{
set<df::interface_key> tmp;
tmp.insert(key);
INTERPOSE_NEXT(feed)(&tmp);
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C))
{
sendKey(interface_key::LEAVESCREEN);
Screen::show(new ViewscreenAutochop());
}
else
{
INTERPOSE_NEXT(feed)(input);
}
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
auto dims = Gui::getDwarfmodeViewDims();
if (dims.menu_x1 <= 0)
return;
df::ui_sidebar_mode d = ui->main.mode;
if (!isInDesignationMenu())
return;
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = 26;
OutputHotkeyString(x, y, "Autochop Dashboard", "c");
}
};
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, feed, 100);
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, render, 100);
command_result df_autochop (color_ostream &out, vector <string> & parameters)
{
for (size_t i = 0; i < parameters.size(); i++)
{
if (parameters[i] == "help" || parameters[i] == "?")
return CR_WRONG_USAGE;
if (parameters[i] == "debug")
save_config();
else
return CR_WRONG_USAGE;
}
if (Maps::IsValid())
Screen::show(new ViewscreenAutochop());
return CR_OK;
}
DFhackCExport command_result plugin_onupdate (color_ostream &out)
{
if (!autochop_enabled)
return CR_OK;
if(!Maps::IsValid())
return CR_OK;
static decltype(world->frame_counter) last_frame_count = 0;
if (DFHack::World::ReadPauseState())
return CR_OK;
if (world->frame_counter - last_frame_count < 1200) // Check every day
return CR_OK;
last_frame_count = world->frame_counter;
do_autochop();
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(autochop_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(autochop_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
initialize();
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"autochop", "Allows automatic harvesting of trees based on the number of stockpiled logs",
df_autochop, false,
"Opens the automated chopping control screen. Specify 'debug' to forcibly save settings.\n"
));
initialize();
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)
{
switch (event) {
case SC_MAP_LOADED:
initialize();
break;
default:
break;
}
return CR_OK;
}

@ -476,9 +476,6 @@ static bool is_valid_building_site(building_site &site, bool orthogonal_check, b
return false; return false;
} }
if (shape == tiletype_shape::TREE)
return false;
if (material == tiletype_material::FIRE || if (material == tiletype_material::FIRE ||
material == tiletype_material::POOL || material == tiletype_material::POOL ||
material == tiletype_material::BROOK || material == tiletype_material::BROOK ||

@ -38,11 +38,11 @@ using namespace std;
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/historical_figure_info.h" #include "df/historical_figure_info.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/language_name.h" #include "df/language_name.h"
#include "df/world.h" #include "df/world.h"
#include "df/world_raws.h" #include "df/world_raws.h"
#include "df/death_info.h" #include "df/incident.h"
using std::vector; using std::vector;
using std::string; using std::string;
@ -104,7 +104,7 @@ void setUnitNickname(df::unit *unit, const std::string &nick)
// v0.34.01: added the vampire's assumed identity // v0.34.01: added the vampire's assumed identity
if (figure->info && figure->info->reputation) if (figure->info && figure->info->reputation)
{ {
auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity); auto identity = df::identity::find(figure->info->reputation->cur_identity);
if (identity) if (identity)
{ {
@ -260,7 +260,7 @@ command_result cursecheck (color_ostream &out, vector <string> & parameters)
out.print("Unnamed creature "); out.print("Unnamed creature ");
} }
auto death = df::death_info::find(unit->counters.death_id); auto death = df::incident::find(unit->counters.death_id);
bool missing = false; bool missing = false;
if (death && !death->flags.bits.discovered) if (death && !death->flags.bits.discovered)
{ {

@ -231,7 +231,7 @@ command_result df_dumpmats (color_ostream &out, vector<string> &parameters)
for (int i = 0; i < mat->reaction_class.size(); i++) for (int i = 0; i < mat->reaction_class.size(); i++)
out.print("\t[REACTION_CLASS:%s]\n", mat->reaction_class[i]->c_str()); out.print("\t[REACTION_CLASS:%s]\n", mat->reaction_class[i]->c_str());
for (int i = 0; i < mat->reaction_product.id.size(); i++) for (int i = 0; i < mat->reaction_product.id.size(); i++)
out.print("\t[MATERIAL_REACTION_PRODUCT:%s:%s:%s%s%s]\n", mat->reaction_product.id[i]->c_str(), mat->reaction_product.str[0][i]->c_str(), mat->reaction_product.str[1][i]->c_str(), mat->reaction_product.str[2][i]->size() ? ":" : "", mat->reaction_product.str[2][i]->c_str()); out.print("\t[MATERIAL_REACTION_PRODUCT:%s:%s:%s%s%s]\n", mat->reaction_product.id[i]->c_str(), mat->reaction_product.str[2][i]->c_str(), mat->reaction_product.str[3][i]->c_str(), mat->reaction_product.str[4][i]->size() ? ":" : "", mat->reaction_product.str[4][i]->c_str());
if (mat->hardens_with_water.mat_type != -1) if (mat->hardens_with_water.mat_type != -1)
out.print("\t[HARDENS_WITH_WATER:%s:%s%s%s]\n", mat->hardens_with_water.str[0].c_str(), mat->hardens_with_water.str[1].c_str(), mat->hardens_with_water.str[2].size() ? ":" : "", mat->hardens_with_water.str[2].c_str()); out.print("\t[HARDENS_WITH_WATER:%s:%s%s%s]\n", mat->hardens_with_water.str[0].c_str(), mat->hardens_with_water.str[1].c_str(), mat->hardens_with_water.str[2].size() ? ":" : "", mat->hardens_with_water.str[2].c_str());

@ -134,7 +134,9 @@ bool dig (MapExtras::MapCache & MCache,
} }
if (tsb == tiletype_shape_basic::Floor if (tsb == tiletype_shape_basic::Floor
&& (type == tile_dig_designation::DownStair || type == tile_dig_designation::Channel) && (type == tile_dig_designation::DownStair || type == tile_dig_designation::Channel)
&& ts != tiletype_shape::TREE && ts != tiletype_shape::BRANCH
&& ts != tiletype_shape::TRUNK_BRANCH
&& ts != tiletype_shape::TWIG
) )
{ {
std::cerr << "allowing tt" << (int)tt << ", is floor\n"; std::cerr << "allowing tt" << (int)tt << ", is floor\n";

@ -73,7 +73,9 @@ cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilitie
return -1; return -1;
} }
if ( shape2 == df::enums::tiletype_shape::TREE ) if ( shape2 == df::enums::tiletype_shape::BRANCH ||
shape2 == df::enums::tiletype_shape::TRUNK_BRANCH ||
shape2 == df::enums::tiletype_shape::TWIG )
return -1; return -1;
/* /*

@ -524,8 +524,6 @@ public:
case job_type::StoreItemInStockpile: case job_type::StoreItemInStockpile:
case job_type::StoreItemInBag: case job_type::StoreItemInBag:
case job_type::StoreItemInHospital: case job_type::StoreItemInHospital:
case job_type::StoreItemInChest:
case job_type::StoreItemInCabinet:
case job_type::StoreWeapon: case job_type::StoreWeapon:
case job_type::StoreArmor: case job_type::StoreArmor:
case job_type::StoreItemInBarrel: case job_type::StoreItemInBarrel:
@ -575,7 +573,6 @@ public:
case job_type::MakeHelm: case job_type::MakeHelm:
case job_type::MakePants: case job_type::MakePants:
case job_type::StudWith: case job_type::StudWith:
case job_type::ProcessPlantsBag:
case job_type::ProcessPlantsVial: case job_type::ProcessPlantsVial:
case job_type::ProcessPlantsBarrel: case job_type::ProcessPlantsBarrel:
case job_type::WeaveCloth: case job_type::WeaveCloth:
@ -699,7 +696,6 @@ public:
case job_type::MakeCheese: case job_type::MakeCheese:
case job_type::PrepareMeal: case job_type::PrepareMeal:
case job_type::ProcessPlants: case job_type::ProcessPlants:
case job_type::BrewDrink:
case job_type::CollectHiveProducts: case job_type::CollectHiveProducts:
real_activity = JOB_FOOD_PROD; real_activity = JOB_FOOD_PROD;
break; break;

@ -106,7 +106,7 @@ public:
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
{ {
bool is_top = false; bool is_top = false;
if (screen->embark_pos_min.y == 0) if (screen->location.embark_pos_min.y == 0)
is_top = true; is_top = true;
std::set<df::interface_key> keys; std::set<df::interface_key> keys;
keys.insert(df::interface_key::SETUP_LOCAL_Y_MUP); keys.insert(df::interface_key::SETUP_LOCAL_Y_MUP);
@ -123,10 +123,10 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
/* Reproduces DF's embark resizing functionality /* Reproduces DF's embark resizing functionality
* Local area resizes up and to the right, unless it's already touching the edge * Local area resizes up and to the right, unless it's already touching the edge
*/ */
int x1 = screen->embark_pos_min.x, int x1 = screen->location.embark_pos_min.x,
x2 = screen->embark_pos_max.x, x2 = screen->location.embark_pos_max.x,
y1 = screen->embark_pos_min.y, y1 = screen->location.embark_pos_min.y,
y2 = screen->embark_pos_max.y, y2 = screen->location.embark_pos_max.y,
width = x2 - x1 + dx, width = x2 - x1 + dx,
height = y2 - y1 + dy; height = y2 - y1 + dy;
if (x1 == x2 && dx == -1) if (x1 == x2 && dx == -1)
@ -150,10 +150,10 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
} }
y2 = std::min(15, y2); y2 = std::min(15, y2);
screen->embark_pos_min.x = x1; screen->location.embark_pos_min.x = x1;
screen->embark_pos_max.x = x2; screen->location.embark_pos_max.x = x2;
screen->embark_pos_min.y = y1; screen->location.embark_pos_min.y = y1;
screen->embark_pos_max.y = y2; screen->location.embark_pos_max.y = y2;
update_embark_sidebar(screen); update_embark_sidebar(screen);
} }
@ -183,10 +183,10 @@ int sticky_pos[] = {0, 0, 3, 3};
bool sticky_moved = false; bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen) void sticky_save (df::viewscreen_choose_start_sitest * screen)
{ {
sticky_pos[0] = screen->embark_pos_min.x; sticky_pos[0] = screen->location.embark_pos_min.x;
sticky_pos[1] = screen->embark_pos_max.x; sticky_pos[1] = screen->location.embark_pos_max.x;
sticky_pos[2] = screen->embark_pos_min.y; sticky_pos[2] = screen->location.embark_pos_min.y;
sticky_pos[3] = screen->embark_pos_max.y; sticky_pos[3] = screen->location.embark_pos_max.y;
} }
void sticky_apply (df::viewscreen_choose_start_sitest * screen) void sticky_apply (df::viewscreen_choose_start_sitest * screen)
@ -196,10 +196,10 @@ void sticky_apply (df::viewscreen_choose_start_sitest * screen)
// Site finder is active - don't override default local position // Site finder is active - don't override default local position
return; return;
} }
screen->embark_pos_min.x = sticky_pos[0]; screen->location.embark_pos_min.x = sticky_pos[0];
screen->embark_pos_max.x = sticky_pos[1]; screen->location.embark_pos_max.x = sticky_pos[1];
screen->embark_pos_min.y = sticky_pos[2]; screen->location.embark_pos_min.y = sticky_pos[2];
screen->embark_pos_max.y = sticky_pos[3]; screen->location.embark_pos_max.y = sticky_pos[3];
update_embark_sidebar(screen); update_embark_sidebar(screen);
} }

@ -8,13 +8,17 @@
#include "DataDefs.h" #include "DataDefs.h"
#include "df/world.h" #include "df/world.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/unit_action.h"
#include "df/map_block.h" #include "df/map_block.h"
using std::string; using std::string;
using std::vector; using std::vector;
using namespace DFHack; using namespace DFHack;
using namespace df::enums;
using df::global::world; using df::global::world;
using df::global::debug_turbospeed;
// dfhack interface // dfhack interface
DFHACK_PLUGIN("fastdwarf"); DFHACK_PLUGIN("fastdwarf");
@ -26,8 +30,8 @@ static bool enable_teledwarf = false;
DFhackCExport command_result plugin_shutdown ( color_ostream &out ) DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{ {
if (df::global::debug_turbospeed) if (debug_turbospeed)
*df::global::debug_turbospeed = false; *debug_turbospeed = false;
return CR_OK; return CR_OK;
} }
@ -51,22 +55,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (!Units::isCitizen(unit)) if (!Units::isCitizen(unit))
continue; continue;
if (enable_fastdwarf && !enable_teledwarf )
{
if (unit->counters.job_counter > 0)
unit->counters.job_counter = 0;
// could also patch the unit->job.current_job->completion_timer
}
if (enable_teledwarf) do if (enable_teledwarf) do
{ {
// don't do anything if the dwarf isn't going anywhere
if (!unit->pos.isValid() || !unit->path.dest.isValid() || unit->pos == unit->path.dest) {
if ( enable_fastdwarf && unit->counters.job_counter > 0 )
unit->counters.job_counter = 0;
break;
}
// skip dwarves that are dragging creatures or being dragged // skip dwarves that are dragging creatures or being dragged
if ((unit->relations.draggee_id != -1) || (unit->relations.dragger_id != -1)) if ((unit->relations.draggee_id != -1) || (unit->relations.dragger_id != -1))
break; break;
@ -79,6 +69,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (unit->counters.unconscious > 0) if (unit->counters.unconscious > 0)
break; break;
// don't do anything if the dwarf isn't going anywhere
if (!unit->pos.isValid() || !unit->path.dest.isValid() || unit->pos == unit->path.dest) {
break;
}
// make sure source and dest map blocks are valid // make sure source and dest map blocks are valid
auto old_occ = Maps::getTileOccupancy(unit->pos); auto old_occ = Maps::getTileOccupancy(unit->pos);
auto new_occ = Maps::getTileOccupancy(unit->path.dest); auto new_occ = Maps::getTileOccupancy(unit->path.dest);
@ -106,6 +101,62 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
unit->pos = unit->path.dest; unit->pos = unit->path.dest;
unit->path.path.clear(); unit->path.path.clear();
} while (0); } while (0);
if (enable_fastdwarf)
{
for (size_t i = 0; i < unit->actions.size(); i++)
{
df::unit_action *action = unit->actions[i];
switch (action->type)
{
case unit_action_type::Move:
action->data.move.timer = 1;
break;
case unit_action_type::Attack:
action->data.attack.timer1 = 1;
action->data.attack.timer2 = 1;
break;
case unit_action_type::Hold:
action->data.hold.timer = 1;
break;
case unit_action_type::Climb:
action->data.climb.timer = 1;
break;
case unit_action_type::Job:
action->data.job.timer = 1;
// could also patch the unit->job.current_job->completion_timer
break;
case unit_action_type::Talk:
action->data.talk.timer = 1;
break;
case unit_action_type::Unsteady:
action->data.unsteady.timer = 1;
break;
case unit_action_type::Dodge:
action->data.dodge.timer = 1;
break;
case unit_action_type::Recover:
action->data.recover.timer = 1;
break;
case unit_action_type::StandUp:
action->data.standup.timer = 1;
break;
case unit_action_type::LieDown:
action->data.liedown.timer = 1;
break;
case unit_action_type::Job2:
action->data.job2.timer = 1;
// could also patch the unit->job.current_job->completion_timer
break;
case unit_action_type::PushObject:
action->data.pushobject.timer = 1;
break;
case unit_action_type::SuckBlood:
action->data.suckblood.timer = 1;
break;
}
}
}
} }
return CR_OK; return CR_OK;
} }
@ -131,21 +182,21 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
if (parameters[0] == "0") if (parameters[0] == "0")
{ {
enable_fastdwarf = false; enable_fastdwarf = false;
if (df::global::debug_turbospeed) if (debug_turbospeed)
*df::global::debug_turbospeed = false; *debug_turbospeed = false;
} }
else if (parameters[0] == "1") else if (parameters[0] == "1")
{ {
enable_fastdwarf = true; enable_fastdwarf = true;
if (df::global::debug_turbospeed) if (debug_turbospeed)
*df::global::debug_turbospeed = false; *debug_turbospeed = false;
} }
else if (parameters[0] == "2") else if (parameters[0] == "2")
{ {
if (df::global::debug_turbospeed) if (debug_turbospeed)
{ {
enable_fastdwarf = false; enable_fastdwarf = false;
*df::global::debug_turbospeed = true; *debug_turbospeed = true;
} }
else else
{ {
@ -160,7 +211,7 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
active = enable_fastdwarf || enable_teledwarf; active = enable_fastdwarf || enable_teledwarf;
out.print("Current state: fast = %d, teleport = %d.\n", out.print("Current state: fast = %d, teleport = %d.\n",
(df::global::debug_turbospeed && *df::global::debug_turbospeed) ? 2 : (enable_fastdwarf ? 1 : 0), (debug_turbospeed && *debug_turbospeed) ? 2 : (enable_fastdwarf ? 1 : 0),
enable_teledwarf ? 1 : 0); enable_teledwarf ? 1 : 0);
return CR_OK; return CR_OK;

@ -33,9 +33,9 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
{ {
if (parameters.size() != 1) if (parameters.size() != 1)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
for (size_t i = 0; i < world->cur_savegame.map_features.size(); i++) for (size_t i = 0; i < world->features.map_features.size(); i++)
{ {
df::feature_init *feature_init = world->cur_savegame.map_features[i]; df::feature_init *feature_init = world->features.map_features[i];
string name; string name;
feature_init->getName(&name); feature_init->getName(&name);
out.print("Feature #%i (\"%s\", type %s) is %s\n", out.print("Feature #%i (\"%s\", type %s) is %s\n",
@ -48,12 +48,12 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
if (parameters.size() != 2) if (parameters.size() != 2)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
size_t i = atoi(parameters[1].c_str()); size_t i = atoi(parameters[1].c_str());
if ((i < 0) || (i >= world->cur_savegame.map_features.size())) if ((i < 0) || (i >= world->features.map_features.size()))
{ {
out.print("No such feature!\n"); out.print("No such feature!\n");
return CR_FAILURE; return CR_FAILURE;
} }
df::feature_init *feature_init = world->cur_savegame.map_features[i]; df::feature_init *feature_init = world->features.map_features[i];
if (feature_init->flags.is_set(feature_init_flags::Discovered)) if (feature_init->flags.is_set(feature_init_flags::Discovered))
{ {
out.print("Selected feature is already discovered!\n"); out.print("Selected feature is already discovered!\n");
@ -70,12 +70,12 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
if (parameters.size() != 2) if (parameters.size() != 2)
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
size_t i = atoi(parameters[1].c_str()); size_t i = atoi(parameters[1].c_str());
if ((i < 0) || (i >= world->cur_savegame.map_features.size())) if ((i < 0) || (i >= world->features.map_features.size()))
{ {
out.print("No such feature!\n"); out.print("No such feature!\n");
return CR_FAILURE; return CR_FAILURE;
} }
df::feature_init *feature_init = world->cur_savegame.map_features[i]; df::feature_init *feature_init = world->features.map_features[i];
if (!feature_init->flags.is_set(feature_init_flags::Discovered)) if (!feature_init->flags.is_set(feature_init_flags::Discovered))
{ {
out.print("Selected feature is already hidden!\n"); out.print("Selected feature is already hidden!\n");

@ -1,7 +1,5 @@
// (un)designate matching plants for gathering/cutting // (un)designate matching plants for gathering/cutting
#include "uicommon.h"
#include "Core.h" #include "Core.h"
#include "Console.h" #include "Console.h"
#include "Export.h" #include "Export.h"
@ -14,20 +12,8 @@
#include "df/tile_dig_designation.h" #include "df/tile_dig_designation.h"
#include "df/plant_raw.h" #include "df/plant_raw.h"
#include "df/plant.h" #include "df/plant.h"
#include "df/ui.h"
#include "df/burrow.h"
#include "df/item_flags.h"
#include "df/item.h"
#include "df/items_other_id.h"
#include "df/viewscreen_dwarfmodest.h"
#include "modules/Screen.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/Burrows.h"
#include "modules/World.h"
#include "modules/MapCache.h"
#include "modules/Gui.h"
#include <set> #include <set>
using std::string; using std::string;
@ -37,601 +23,6 @@ using namespace DFHack;
using namespace df::enums; using namespace df::enums;
using df::global::world; using df::global::world;
using df::global::ui;
#define PLUGIN_VERSION 0.3
DFHACK_PLUGIN("getplants");
static bool autochop_enabled = false;
static int min_logs, max_logs;
static bool wait_for_threshold;
static PersistentDataItem config_autochop;
struct WatchedBurrow
{
int32_t id;
df::burrow *burrow;
WatchedBurrow(df::burrow *burrow) : burrow(burrow)
{
id = burrow->id;
}
};
class WatchedBurrows
{
public:
string getSerialisedIds()
{
validate();
stringstream burrow_ids;
bool append_started = false;
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
if (append_started)
burrow_ids << " ";
burrow_ids << it->id;
append_started = true;
}
return burrow_ids.str();
}
void clear()
{
burrows.clear();
}
void add(const int32_t id)
{
if (!isValidBurrow(id))
return;
WatchedBurrow wb(getBurrow(id));
burrows.push_back(wb);
}
void add(const string burrow_ids)
{
istringstream iss(burrow_ids);
int id;
while (iss >> id)
{
add(id);
}
}
bool isValidPos(const df::coord &plant_pos)
{
validate();
if (!burrows.size())
return true;
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
df::burrow *burrow = it->burrow;
if (Burrows::isAssignedTile(burrow, plant_pos))
return true;
}
return false;
}
bool isBurrowWatched(const df::burrow *burrow)
{
validate();
for (auto it = burrows.begin(); it != burrows.end(); it++)
{
if (it->burrow == burrow)
return true;
}
return false;
}
private:
static bool isValidBurrow(const int32_t id)
{
return getBurrow(id);
}
static df::burrow *getBurrow(const int32_t id)
{
return df::burrow::find(id);
}
void validate()
{
for (auto it = burrows.begin(); it != burrows.end();)
{
if (!isValidBurrow(it->id))
it = burrows.erase(it);
else
++it;
}
}
vector<WatchedBurrow> burrows;
};
static WatchedBurrows watchedBurrows;
static void save_config()
{
config_autochop.val() = watchedBurrows.getSerialisedIds();
config_autochop.ival(0) = autochop_enabled;
config_autochop.ival(1) = min_logs;
config_autochop.ival(2) = max_logs;
config_autochop.ival(3) = wait_for_threshold;
}
static void initialize()
{
watchedBurrows.clear();
autochop_enabled = false;
min_logs = 80;
max_logs = 100;
wait_for_threshold = false;
config_autochop = World::GetPersistentData("autochop/config");
if (config_autochop.isValid())
{
watchedBurrows.add(config_autochop.val());
autochop_enabled = config_autochop.ival(0);
min_logs = config_autochop.ival(1);
max_logs = config_autochop.ival(2);
wait_for_threshold = config_autochop.ival(3);
}
else
{
config_autochop = World::AddPersistentData("autochop/config");
if (config_autochop.isValid())
save_config();
}
}
static int do_chop_designation(bool chop, bool count_only)
{
int count = 0;
for (size_t i = 0; i < world->map.map_blocks.size(); i++)
{
df::map_block *cur = world->map.map_blocks[i];
for (size_t j = 0; j < cur->plants.size(); j++)
{
const df::plant *plant = cur->plants[j];
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
if (plant->flags.bits.is_shrub)
continue;
if (cur->designation[x][y].bits.hidden)
continue;
df::tiletype_shape shape = tileShape(cur->tiletype[x][y]);
if (shape != tiletype_shape::TREE)
continue;
if (!count_only && !watchedBurrows.isValidPos(plant->pos))
continue;
bool dirty = false;
if (chop && cur->designation[x][y].bits.dig == tile_dig_designation::No)
{
if (count_only)
{
++count;
}
else
{
cur->designation[x][y].bits.dig = tile_dig_designation::Default;
dirty = true;
}
}
if (!chop && cur->designation[x][y].bits.dig == tile_dig_designation::Default)
{
if (count_only)
{
++count;
}
else
{
cur->designation[x][y].bits.dig = tile_dig_designation::No;
dirty = true;
}
}
if (dirty)
{
cur->flags.bits.designated = true;
++count;
}
}
}
return count;
}
static bool is_valid_item(df::item *item)
{
for (size_t i = 0; i < item->general_refs.size(); i++)
{
df::general_ref *ref = item->general_refs[i];
switch (ref->getType())
{
case general_ref_type::CONTAINED_IN_ITEM:
return false;
case general_ref_type::UNIT_HOLDER:
return false;
case general_ref_type::BUILDING_HOLDER:
return false;
default:
break;
}
}
for (size_t i = 0; i < item->specific_refs.size(); i++)
{
df::specific_ref *ref = item->specific_refs[i];
if (ref->type == specific_ref_type::JOB)
{
// Ignore any items assigned to a job
return false;
}
}
return true;
}
static int get_log_count()
{
std::vector<df::item*> &items = world->items.other[items_other_id::IN_PLAY];
// Pre-compute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
#define F(x) bad_flags.bits.x = true;
F(dump); F(forbid); F(garbage_collect);
F(hostile); F(on_fire); F(rotten); F(trader);
F(in_building); F(construction); F(artifact);
F(spider_web); F(owned); F(in_job);
#undef F
size_t valid_count = 0;
for (size_t i = 0; i < items.size(); i++)
{
df::item *item = items[i];
if (item->getType() != item_type::WOOD)
continue;
if (item->flags.whole & bad_flags.whole)
continue;
if (!is_valid_item(item))
continue;
++valid_count;
}
return valid_count;
}
static void set_threshold_check(bool state)
{
wait_for_threshold = state;
save_config();
}
static void do_autochop()
{
int log_count = get_log_count();
if (wait_for_threshold)
{
if (log_count < min_logs)
{
set_threshold_check(false);
do_chop_designation(true, false);
}
}
else
{
if (log_count >= max_logs)
{
set_threshold_check(true);
do_chop_designation(false, false);
}
else
{
do_chop_designation(true, false);
}
}
}
class ViewscreenAutochop : public dfhack_viewscreen
{
public:
ViewscreenAutochop()
{
burrows_column.multiselect = true;
burrows_column.setTitle("Burrows");
burrows_column.bottom_margin = 3;
burrows_column.allow_search = false;
burrows_column.text_clip_at = 30;
populateBurrowsColumn();
message.clear();
}
void populateBurrowsColumn()
{
selected_column = 0;
auto last_selected_index = burrows_column.highlighted_index;
burrows_column.clear();
for (auto iter = ui->burrows.list.begin(); iter != ui->burrows.list.end(); iter++)
{
df::burrow* burrow = *iter;
auto elem = ListEntry<df::burrow *>(burrow->name, burrow);
elem.selected = watchedBurrows.isBurrowWatched(burrow);
burrows_column.add(elem);
}
burrows_column.fixWidth();
burrows_column.filterDisplay();
current_log_count = get_log_count();
marked_tree_count = do_chop_designation(false, true);
}
void change_min_logs(int delta)
{
if (!autochop_enabled)
return;
min_logs += delta;
if (min_logs < 0)
min_logs = 0;
if (min_logs > max_logs)
max_logs = min_logs;
}
void change_max_logs(int delta)
{
if (!autochop_enabled)
return;
max_logs += delta;
if (max_logs < min_logs)
min_logs = max_logs;
}
void feed(set<df::interface_key> *input)
{
bool key_processed = false;
message.clear();
switch (selected_column)
{
case 0:
key_processed = burrows_column.feed(input);
break;
}
if (key_processed)
{
if (input->count(interface_key::SELECT))
updateAutochopBurrows();
return;
}
if (input->count(interface_key::LEAVESCREEN))
{
save_config();
input->clear();
Screen::dismiss(this);
if (autochop_enabled)
do_autochop();
return;
}
else if (input->count(interface_key::CUSTOM_A))
{
autochop_enabled = !autochop_enabled;
}
else if (input->count(interface_key::CUSTOM_D))
{
int count = do_chop_designation(true, false);
message = "Trees marked for chop: " + int_to_string(count);
marked_tree_count = do_chop_designation(false, true);
}
else if (input->count(interface_key::CUSTOM_U))
{
int count = do_chop_designation(false, false);
message = "Trees unmarked: " + int_to_string(count);
marked_tree_count = do_chop_designation(false, true);
}
else if (input->count(interface_key::CUSTOM_H))
{
change_min_logs(-1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_H))
{
change_min_logs(-10);
}
else if (input->count(interface_key::CUSTOM_J))
{
change_min_logs(1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_J))
{
change_min_logs(10);
}
else if (input->count(interface_key::CUSTOM_K))
{
change_max_logs(-1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_K))
{
change_max_logs(-10);
}
else if (input->count(interface_key::CUSTOM_L))
{
change_max_logs(1);
}
else if (input->count(interface_key::CUSTOM_SHIFT_L))
{
change_max_logs(10);
}
else if (enabler->tracking_on && enabler->mouse_lbut)
{
if (burrows_column.setHighlightByMouse())
{
selected_column = 0;
}
enabler->mouse_lbut = enabler->mouse_rbut = 0;
}
}
void render()
{
if (Screen::isDismissed(this))
return;
dfhack_viewscreen::render();
Screen::clear();
Screen::drawBorder(" Autochop ");
burrows_column.display(selected_column == 0);
int32_t y = gps->dimy - 3;
int32_t x = 2;
OutputHotkeyString(x, y, "Leave", "Esc");
x += 3;
OutputString(COLOR_YELLOW, x, y, message);
y = 3;
int32_t left_margin = burrows_column.getMaxItemWidth() + 3;
x = left_margin;
if (burrows_column.getSelectedElems().size() > 0)
{
OutputString(COLOR_GREEN, x, y, "Will chop in selected burrows", true, left_margin);
}
else
{
OutputString(COLOR_YELLOW, x, y, "Will chop from whole map", true, left_margin);
OutputString(COLOR_YELLOW, x, y, "Select from left to chop in specific burrows", true, left_margin);
}
++y;
OutputToggleString(x, y, "Autochop", "a", autochop_enabled, true, left_margin);
OutputHotkeyString(x, y, "Designate Now", "d", true, left_margin);
OutputHotkeyString(x, y, "Undesignate Now", "u", true, left_margin);
OutputHotkeyString(x, y, "Toggle Burrow", "Enter", true, left_margin);
if (autochop_enabled)
{
OutputLabelString(x, y, "Min Logs", "hjHJ", int_to_string(min_logs), true, left_margin);
OutputLabelString(x, y, "Max Logs", "klKL", int_to_string(max_logs), true, left_margin);
}
++y;
OutputString(COLOR_BROWN, x, y, "Current Counts", true, left_margin);
OutputString(COLOR_WHITE, x, y, "Current Logs: ");
OutputString(COLOR_GREEN, x, y, int_to_string(current_log_count), true, left_margin);
OutputString(COLOR_WHITE, x, y, "Marked Trees: ");
OutputString(COLOR_GREEN, x, y, int_to_string(marked_tree_count), true, left_margin);
}
std::string getFocusString() { return "autochop"; }
void updateAutochopBurrows()
{
watchedBurrows.clear();
vector<df::burrow *> v = burrows_column.getSelectedElems();
for_each_<df::burrow *>(v, [] (df::burrow *b) { watchedBurrows.add(b->id); });
}
private:
ListColumn<df::burrow *> burrows_column;
int selected_column;
int current_log_count;
int marked_tree_count;
MapExtras::MapCache mcache;
string message;
void validateColumn()
{
set_to_limit(selected_column, 0);
}
void resize(int32_t x, int32_t y)
{
dfhack_viewscreen::resize(x, y);
burrows_column.resize();
}
};
struct autochop_hook : public df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool isInDesignationMenu()
{
using namespace df::enums::ui_sidebar_mode;
return (ui->main.mode == DesignateChopTrees);
}
void sendKey(const df::interface_key &key)
{
set<df::interface_key> tmp;
tmp.insert(key);
INTERPOSE_NEXT(feed)(&tmp);
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C))
{
sendKey(interface_key::LEAVESCREEN);
Screen::show(new ViewscreenAutochop());
}
else
{
INTERPOSE_NEXT(feed)(input);
}
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
auto dims = Gui::getDwarfmodeViewDims();
if (dims.menu_x1 <= 0)
return;
df::ui_sidebar_mode d = ui->main.mode;
if (!isInDesignationMenu())
return;
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = 26;
OutputHotkeyString(x, y, "Autochop Dashboard", "c");
}
};
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, feed, 100);
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, render, 100);
command_result df_getplants (color_ostream &out, vector <string> & parameters) command_result df_getplants (color_ostream &out, vector <string> & parameters)
{ {
@ -655,17 +46,6 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
exclude = true; exclude = true;
else if(parameters[i] == "-a") else if(parameters[i] == "-a")
all = true; all = true;
else if(parameters[i] == "debug")
{
save_config();
}
else if(parameters[i] == "autochop")
{
if(Maps::IsValid())
Screen::show(new ViewscreenAutochop());
return CR_OK;
}
else else
plantNames.insert(parameters[i]); plantNames.insert(parameters[i]);
} }
@ -721,45 +101,44 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
} }
count = 0; count = 0;
for (size_t i = 0; i < world->map.map_blocks.size(); i++) for (size_t i = 0; i < world->plants.all.size(); i++)
{ {
df::map_block *cur = world->map.map_blocks[i]; const df::plant *plant = world->plants.all[i];
df::map_block *cur = Maps::getTileBlock(plant->pos);
bool dirty = false; bool dirty = false;
for (size_t j = 0; j < cur->plants.size(); j++)
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
if (plantIDs.find(plant->material) != plantIDs.end())
{ {
const df::plant *plant = cur->plants[j]; if (exclude)
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
if (plantIDs.find(plant->material) != plantIDs.end())
{
if (exclude)
continue;
}
else
{
if (!exclude)
continue;
}
df::tiletype_shape shape = tileShape(cur->tiletype[x][y]);
df::tiletype_special special = tileSpecial(cur->tiletype[x][y]);
if (plant->flags.bits.is_shrub && (treesonly || !(shape == tiletype_shape::SHRUB && special != tiletype_special::DEAD)))
continue;
if (!plant->flags.bits.is_shrub && (shrubsonly || !(shape == tiletype_shape::TREE)))
continue; continue;
if (cur->designation[x][y].bits.hidden) }
else
{
if (!exclude)
continue; continue;
if (deselect && cur->designation[x][y].bits.dig == tile_dig_designation::Default) }
{ df::tiletype_shape shape = tileShape(cur->tiletype[x][y]);
cur->designation[x][y].bits.dig = tile_dig_designation::No; df::tiletype_material material = tileMaterial(cur->tiletype[x][y]);
dirty = true; df::tiletype_special special = tileSpecial(cur->tiletype[x][y]);
++count; if (plant->flags.bits.is_shrub && (treesonly || !(shape == tiletype_shape::SHRUB && special != tiletype_special::DEAD)))
} continue;
if (!deselect && cur->designation[x][y].bits.dig == tile_dig_designation::No) if (!plant->flags.bits.is_shrub && (shrubsonly || !(material == tiletype_material::TREE)))
{ continue;
cur->designation[x][y].bits.dig = tile_dig_designation::Default; if (cur->designation[x][y].bits.hidden)
dirty = true; continue;
++count; if (deselect && cur->designation[x][y].bits.dig == tile_dig_designation::Default)
} {
cur->designation[x][y].bits.dig = tile_dig_designation::No;
dirty = true;
++count;
}
if (!deselect && cur->designation[x][y].bits.dig == tile_dig_designation::No)
{
cur->designation[x][y].bits.dig = tile_dig_designation::Default;
dirty = true;
++count;
} }
if (dirty) if (dirty)
cur->flags.bits.designated = true; cur->flags.bits.designated = true;
@ -769,48 +148,7 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_onupdate (color_ostream &out) DFHACK_PLUGIN("getplants");
{
if (!autochop_enabled)
return CR_OK;
if(!Maps::IsValid())
return CR_OK;
static decltype(world->frame_counter) last_frame_count = 0;
if (DFHack::World::ReadPauseState())
return CR_OK;
if (world->frame_counter - last_frame_count < 1200) // Check every day
return CR_OK;
last_frame_count = world->frame_counter;
do_autochop();
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(autochop_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(autochop_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
initialize();
}
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands) DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{ {
@ -826,11 +164,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCom
" -x - Apply selected action to all plants except those specified\n" " -x - Apply selected action to all plants except those specified\n"
" -a - Select every type of plant (obeys -t/-s)\n" " -a - Select every type of plant (obeys -t/-s)\n"
"Specifying both -t and -s will have no effect.\n" "Specifying both -t and -s will have no effect.\n"
"If no plant IDs are specified, all valid plant IDs will be listed.\n\n" "If no plant IDs are specified, all valid plant IDs will be listed.\n"
" autochop - Opens the automated chopping control screen\n"
)); ));
initialize();
return CR_OK; return CR_OK;
} }
@ -838,16 +173,3 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{ {
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_MAP_LOADED:
initialize();
break;
default:
break;
}
return CR_OK;
}

@ -144,13 +144,13 @@ void doInfiniteSky(color_ostream& out, int32_t howMany) {
} }
} }
df::z_level_flags* flags = new df::z_level_flags[z_count_block+1]; df::z_level_flags* flags = new df::z_level_flags[z_count_block+1];
memcpy(flags, world->map.z_level_flags, z_count_block*sizeof(df::z_level_flags)); memcpy(flags, world->map_extras.z_level_flags, z_count_block*sizeof(df::z_level_flags));
flags[z_count_block].whole = 0; flags[z_count_block].whole = 0;
flags[z_count_block].bits.update = 1; flags[z_count_block].bits.update = 1;
world->map.z_count_block++; world->map.z_count_block++;
world->map.z_count++; world->map.z_count++;
delete[] world->map.z_level_flags; delete[] world->map_extras.z_level_flags;
world->map.z_level_flags = flags; world->map_extras.z_level_flags = flags;
} }
} }

@ -180,7 +180,7 @@ const SkillColumn columns[] = {
{11, 3, profession::NONE, unit_labor::HAUL_REFUSE, job_skill::NONE, "Re"}, {11, 3, profession::NONE, unit_labor::HAUL_REFUSE, job_skill::NONE, "Re"},
{11, 3, profession::NONE, unit_labor::HAUL_FURNITURE, job_skill::NONE, "Fu"}, {11, 3, profession::NONE, unit_labor::HAUL_FURNITURE, job_skill::NONE, "Fu"},
{11, 3, profession::NONE, unit_labor::HAUL_ANIMAL, job_skill::NONE, "An"}, {11, 3, profession::NONE, unit_labor::HAUL_ANIMAL, job_skill::NONE, "An"},
{11, 3, profession::NONE, unit_labor::PUSH_HAUL_VEHICLE, job_skill::NONE, "Ve"}, {11, 3, profession::NONE, unit_labor::HANDLE_VEHICLES, job_skill::NONE, "Ve"},
// Other Jobs // Other Jobs
{12, 4, profession::ARCHITECT, unit_labor::ARCHITECT, job_skill::DESIGNBUILDING, "Ar"}, {12, 4, profession::ARCHITECT, unit_labor::ARCHITECT, job_skill::DESIGNBUILDING, "Ar"},
{12, 4, profession::ALCHEMIST, unit_labor::ALCHEMIST, job_skill::ALCHEMY, "Al"}, {12, 4, profession::ALCHEMIST, unit_labor::ALCHEMIST, job_skill::ALCHEMY, "Al"},
@ -217,6 +217,7 @@ const SkillColumn columns[] = {
{15, 8, profession::NONE, unit_labor::NONE, job_skill::SITUATIONAL_AWARENESS, "Ob"}, {15, 8, profession::NONE, unit_labor::NONE, job_skill::SITUATIONAL_AWARENESS, "Ob"},
{15, 8, profession::NONE, unit_labor::NONE, job_skill::COORDINATION, "Cr"}, {15, 8, profession::NONE, unit_labor::NONE, job_skill::COORDINATION, "Cr"},
{15, 8, profession::NONE, unit_labor::NONE, job_skill::BALANCE, "Ba"}, {15, 8, profession::NONE, unit_labor::NONE, job_skill::BALANCE, "Ba"},
{15, 8, profession::NONE, unit_labor::NONE, job_skill::CLIMBING, "Cl"},
// Social // Social
{16, 3, profession::NONE, unit_labor::NONE, job_skill::PERSUASION, "Pe"}, {16, 3, profession::NONE, unit_labor::NONE, job_skill::PERSUASION, "Pe"},
{16, 3, profession::NONE, unit_labor::NONE, job_skill::NEGOTIATION, "Ne"}, {16, 3, profession::NONE, unit_labor::NONE, job_skill::NEGOTIATION, "Ne"},
@ -760,7 +761,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
{ {
if ((gps->mouse_y >= 1) && (gps->mouse_y <= 2)) if ((gps->mouse_y >= 1) && (gps->mouse_y <= 2))
click_header = i; click_header = i;
if ((gps->mouse_y >= 4) && (gps->mouse_y <= 4 + num_rows)) if ((gps->mouse_y >= 4) && (gps->mouse_y < 4 + num_rows))
click_body = i; click_body = i;
} }
} }
@ -768,7 +769,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
if ((gps->mouse_x >= col_offsets[DISP_COLUMN_LABORS]) && if ((gps->mouse_x >= col_offsets[DISP_COLUMN_LABORS]) &&
(gps->mouse_x < col_offsets[DISP_COLUMN_LABORS] + col_widths[DISP_COLUMN_LABORS])) (gps->mouse_x < col_offsets[DISP_COLUMN_LABORS] + col_widths[DISP_COLUMN_LABORS]))
click_labor = gps->mouse_x - col_offsets[DISP_COLUMN_LABORS] + first_column; click_labor = gps->mouse_x - col_offsets[DISP_COLUMN_LABORS] + first_column;
if ((gps->mouse_y >= 4) && (gps->mouse_y <= 4 + num_rows)) if ((gps->mouse_y >= 4) && (gps->mouse_y < 4 + num_rows))
click_unit = gps->mouse_y - 4 + first_row; click_unit = gps->mouse_y - 4 + first_row;
switch (click_header) switch (click_header)

@ -20,10 +20,11 @@ using std::string;
using namespace DFHack; using namespace DFHack;
using df::global::world; using df::global::world;
const uint32_t sapling_to_tree_threshold = 120 * 28 * 12 * 3; // 3 years const uint32_t sapling_to_tree_threshold = 120 * 28 * 12 * 3 - 1; // 3 years minus 1 - let the game handle the actual growing-up
DFHACK_PLUGIN("plants"); DFHACK_PLUGIN("plants");
/* Immolate/Extirpate no longer work in 0.40
enum do_what enum do_what
{ {
do_immolate, do_immolate,
@ -59,12 +60,13 @@ static bool getoptions( vector <string> & parameters, bool & shrubs, bool & tree
return true; return true;
} }
/** //
* Book of Immolations, chapter 1, verse 35: // Book of Immolations, chapter 1, verse 35:
* Armok emerged from the hellish depths and beheld the sunny realms for the first time. // Armok emerged from the hellish depths and beheld the sunny realms for the first time.
* And he cursed the plants and trees for their bloodless wood, turning them into ash and smoldering ruin. // And he cursed the plants and trees for their bloodless wood, turning them into ash and smoldering ruin.
* Armok was pleased and great temples were built by the dwarves, for they shared his hatred for trees and plants. // Armok was pleased and great temples were built by the dwarves, for they shared his hatred for trees and plants.
*/ //
static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees) static command_result immolations (color_ostream &out, do_what what, bool shrubs, bool trees)
{ {
CoreSuspender suspend; CoreSuspender suspend;
@ -90,37 +92,30 @@ static command_result immolations (color_ostream &out, do_what what, bool shrubs
destroyed ++; destroyed ++;
} }
} }
out.print("Praise Armok!\n"); out.print("Praise Armok! %i plants destroyed.\n", destroyed);
} }
else else
{ {
int32_t x,y,z; int32_t x,y,z;
if(Gui::getCursorCoords(x,y,z)) if(Gui::getCursorCoords(x,y,z))
{ {
auto block = Maps::getTileBlock(x,y,z); bool didit = false;
vector<df::plant *> *alltrees = block ? &block->plants : NULL; for(size_t i = 0; i < world->plants.all.size(); i++)
if(alltrees)
{ {
bool didit = false; df::plant *tree = world->plants.all[i];
for(size_t i = 0 ; i < alltrees->size(); i++) if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
{
df::plant * tree = alltrees->at(i);
if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
{
if(what == do_immolate)
tree->damage_flags.bits.is_burning = true;
tree->hitpoints = 0;
didit = true;
break;
}
}
/*
if(!didit)
{ {
cout << "----==== There's NOTHING there! ====----" << endl; if(what == do_immolate)
tree->damage_flags.bits.is_burning = true;
tree->hitpoints = 0;
didit = true;
break;
} }
*/
} }
if(didit)
out.print("Praise Armok! Selected plant destroyed.\n");
else
out.printerr("No plant found at specified location!\n");
} }
else else
{ {
@ -157,16 +152,16 @@ command_result df_immolate (color_ostream &out, vector <string> & parameters, do
return CR_OK; return CR_OK;
} }
*/
command_result df_grow (color_ostream &out, vector <string> & parameters) command_result df_grow (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] == "?")
{ {
out << "Usage:\n" out.print("Usage:\n"
"This command turns all living saplings on the map into full-grown trees.\n" "This command turns all living saplings on the map into full-grown trees.\n"
"With active cursor, work on the targetted one only.\n"; "With active cursor, work on the targetted one only.\n");
return CR_OK; return CR_OK;
} }
} }
@ -180,30 +175,26 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
} }
MapExtras::MapCache map; MapExtras::MapCache map;
int32_t x,y,z; int32_t x,y,z;
int grown = 0;
if(Gui::getCursorCoords(x,y,z)) if(Gui::getCursorCoords(x,y,z))
{ {
auto block = Maps::getTileBlock(x,y,z); for(size_t i = 0; i < world->plants.all.size(); i++)
vector<df::plant *> *alltrees = block ? &block->plants : NULL;
if(alltrees)
{ {
for(size_t i = 0 ; i < alltrees->size(); i++) df::plant * tree = world->plants.all[i];
if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
{ {
df::plant * tree = alltrees->at(i); if(tileShape(map.tiletypeAt(DFCoord(x,y,z))) == tiletype_shape::SAPLING &&
if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z) tileSpecial(map.tiletypeAt(DFCoord(x,y,z))) != tiletype_special::DEAD)
{ {
if(tileShape(map.tiletypeAt(DFCoord(x,y,z))) == tiletype_shape::SAPLING && tree->grow_counter = sapling_to_tree_threshold;
tileSpecial(map.tiletypeAt(DFCoord(x,y,z))) != tiletype_special::DEAD) grown++;
{
tree->grow_counter = sapling_to_tree_threshold;
}
break;
} }
break;
} }
} }
} }
else else
{ {
int grown = 0;
for(size_t i = 0 ; i < world->plants.all.size(); i++) for(size_t i = 0 ; i < world->plants.all.size(); i++)
{ {
df::plant *p = world->plants.all[i]; df::plant *p = world->plants.all[i];
@ -211,9 +202,14 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
if(!p->flags.bits.is_shrub && tileShape(ttype) == tiletype_shape::SAPLING && tileSpecial(ttype) != tiletype_special::DEAD) if(!p->flags.bits.is_shrub && tileShape(ttype) == tiletype_shape::SAPLING && tileSpecial(ttype) != tiletype_special::DEAD)
{ {
p->grow_counter = sapling_to_tree_threshold; p->grow_counter = sapling_to_tree_threshold;
grown++;
} }
} }
} }
if (grown)
out.print("%i plants grown.\n", grown);
else
out.printerr("No plant(s) found!\n");
return CR_OK; return CR_OK;
} }
@ -222,10 +218,10 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
{ {
if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?")) if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
{ {
out << "Usage:\n" out.print("Usage:\n"
"Create a new plant at the cursor.\n" "Create a new plant at the cursor.\n"
"Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n" "Specify the type of plant to create by its raw ID (e.g. TOWER_CAP or MUSHROOM_HELMET_PLUMP).\n"
"Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n"; "Only shrubs and saplings can be placed, and they must be located on a dirt or grass floor.\n");
return CR_OK; return CR_OK;
} }
@ -244,7 +240,8 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
return CR_FAILURE; return CR_FAILURE;
} }
df::map_block *map = Maps::getTileBlock(x, y, z); df::map_block *map = Maps::getTileBlock(x, y, z);
if (!map) df::map_block_column *col = Maps::getBlockColumn((x / 48) * 3, (y / 48) * 3);
if (!map || !col)
{ {
out.printerr("Invalid location selected!\n"); out.printerr("Invalid location selected!\n");
return CR_FAILURE; return CR_FAILURE;
@ -296,10 +293,6 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
plant->pos.y = y; plant->pos.y = y;
plant->pos.z = z; plant->pos.z = z;
plant->update_order = rand() % 10; plant->update_order = rand() % 10;
plant->temperature_tile_tick = -1;
plant->temperature_tile = 60001;
plant->min_safe_temp = 9900;
plant->max_safe_temp = 60001;
world->plants.all.push_back(plant); world->plants.all.push_back(plant);
switch (plant->flags.whole & 3) switch (plant->flags.whole & 3)
@ -309,7 +302,7 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
case 2: world->plants.shrub_dry.push_back(plant); break; case 2: world->plants.shrub_dry.push_back(plant); break;
case 3: world->plants.shrub_wet.push_back(plant); break; case 3: world->plants.shrub_wet.push_back(plant); break;
} }
map->plants.push_back(plant); col->plants.push_back(plant);
if (plant->flags.bits.is_shrub) if (plant->flags.bits.is_shrub)
map->tiletype[tx][ty] = tiletype::Shrub; map->tiletype[tx][ty] = tiletype::Shrub;
else else
@ -326,6 +319,7 @@ command_result df_plant (color_ostream &out, vector <string> & parameters)
parameters.erase(parameters.begin()); parameters.erase(parameters.begin());
return df_grow(out, parameters); return df_grow(out, parameters);
} else } else
/*
if (parameters[0] == "immolate") { if (parameters[0] == "immolate") {
parameters.erase(parameters.begin()); parameters.erase(parameters.begin());
return df_immolate(out, parameters, do_immolate); return df_immolate(out, parameters, do_immolate);
@ -334,6 +328,7 @@ command_result df_plant (color_ostream &out, vector <string> & parameters)
parameters.erase(parameters.begin()); parameters.erase(parameters.begin());
return df_immolate(out, parameters, do_extirpate); return df_immolate(out, parameters, do_extirpate);
} else } else
*/
if (parameters[0] == "create") { if (parameters[0] == "create") {
parameters.erase(parameters.begin()); parameters.erase(parameters.begin());
return df_createplant(out, parameters); return df_createplant(out, parameters);
@ -347,8 +342,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false, commands.push_back(PluginCommand("plant", "Plant creation and removal.", df_plant, false,
"Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n" "Command to create, grow or remove plants on the map. For more details, check the subcommand help :\n"
"plant grow help - Grows saplings into trees.\n" "plant grow help - Grows saplings into trees.\n"
"plant immolate help - Set plants on fire.\n" // "plant immolate help - Set plants on fire.\n"
"plant extirpate help - Kill plants.\n" // "plant extirpate help - Kill plants.\n"
"plant create help - Create a new plant.\n")); "plant create help - Create a new plant.\n"));
return CR_OK; return CR_OK;

@ -489,7 +489,7 @@ static command_result embark_prospector(color_ostream &out, df::viewscreen_choos
} }
df::world_data *data = world->world_data; df::world_data *data = world->world_data;
coord2d cur_region = screen->region_pos; coord2d cur_region = screen->location.region_pos;
auto cur_details = get_details(data, cur_region); auto cur_details = get_details(data, cur_region);
if (!cur_details) if (!cur_details)
@ -512,9 +512,9 @@ static command_result embark_prospector(color_ostream &out, df::viewscreen_choos
biomes[screen->biome_rgn[screen->biome_idx]]++; biomes[screen->biome_rgn[screen->biome_idx]]++;
}*/ }*/
for (int x = screen->embark_pos_min.x; x <= screen->embark_pos_max.x; x++) for (int x = screen->location.embark_pos_min.x; x <= screen->location.embark_pos_max.x; x++)
{ {
for (int y = screen->embark_pos_min.y; y <= screen->embark_pos_max.y; y++) for (int y = screen->location.embark_pos_min.y; y <= screen->location.embark_pos_max.y; y++)
{ {
EmbarkTileLayout tile; EmbarkTileLayout tile;
if (!estimate_underground(out, tile, cur_details, x, y) || if (!estimate_underground(out, tile, cur_details, x, y) ||
@ -737,13 +737,15 @@ command_result prospector (color_ostream &con, vector <string> & parameters)
// and we can check visibility more easily here // and we can check visibility more easily here
if (showPlants) if (showPlants)
{ {
auto block = Maps::getBlock(b_x,b_y,z); auto block = Maps::getBlockColumn(b_x,b_y);
vector<df::plant *> *plants = block ? &block->plants : NULL; vector<df::plant *> *plants = block ? &block->plants : NULL;
if(plants) if(plants)
{ {
for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++) for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++)
{ {
const df::plant & plant = *(*it); const df::plant & plant = *(*it);
if (plant.pos.z != z)
continue;
df::coord2d loc(plant.pos.x, plant.pos.y); df::coord2d loc(plant.pos.x, plant.pos.y);
loc = loc % 16; loc = loc % 16;
if (showHidden || !b->DesignationAt(loc).bits.hidden) if (showHidden || !b->DesignationAt(loc).bits.hidden)

@ -22,7 +22,7 @@
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/historical_figure_info.h" #include "df/historical_figure_info.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/language_name.h" #include "df/language_name.h"
#include "df/building_stockpilest.h" #include "df/building_stockpilest.h"
#include "df/building_workshopst.h" #include "df/building_workshopst.h"

@ -430,7 +430,9 @@ command_result revflood(color_ostream &out, vector<string> & params)
case tiletype_shape::STAIR_UP: case tiletype_shape::STAIR_UP:
case tiletype_shape::RAMP: case tiletype_shape::RAMP:
case tiletype_shape::FLOOR: case tiletype_shape::FLOOR:
case tiletype_shape::TREE: case tiletype_shape::BRANCH:
case tiletype_shape::TRUNK_BRANCH:
case tiletype_shape::TWIG:
case tiletype_shape::SAPLING: case tiletype_shape::SAPLING:
case tiletype_shape::SHRUB: case tiletype_shape::SHRUB:
case tiletype_shape::BOULDER: case tiletype_shape::BOULDER:
@ -442,6 +444,12 @@ command_result revflood(color_ostream &out, vector<string> & params)
above = sides = true; above = sides = true;
break; break;
} }
if (tileMaterial(tt) == tiletype_material::PLANT || tileMaterial(tt) == tiletype_material::MUSHROOM)
{
if(from_below)
unhide = 0;
above = sides = true;
}
if(unhide) if(unhide)
{ {
des.bits.hidden = false; des.bits.hidden = false;

@ -741,7 +741,6 @@ private:
virtual void do_post_init() virtual void do_post_init()
{ {
is_vermin = &viewscreen->is_vermin; is_vermin = &viewscreen->is_vermin;
pet_info = &viewscreen->pet_info;
is_tame = &viewscreen->is_tame; is_tame = &viewscreen->is_tame;
is_adopting = &viewscreen->is_adopting; is_adopting = &viewscreen->is_adopting;
} }
@ -764,7 +763,6 @@ private:
void save_secondary_values() void save_secondary_values()
{ {
is_vermin_s = *is_vermin; is_vermin_s = *is_vermin;
pet_info_s = *pet_info;
is_tame_s = *is_tame; is_tame_s = *is_tame;
is_adopting_s = *is_adopting; is_adopting_s = *is_adopting;
} }
@ -772,7 +770,6 @@ private:
void reset_secondary_viewscreen_vectors() void reset_secondary_viewscreen_vectors()
{ {
is_vermin = NULL; is_vermin = NULL;
pet_info = NULL;
is_tame = NULL; is_tame = NULL;
is_adopting = NULL; is_adopting = NULL;
} }
@ -780,7 +777,6 @@ private:
void update_saved_secondary_list_item(size_t i, size_t j) void update_saved_secondary_list_item(size_t i, size_t j)
{ {
is_vermin_s[i] = (*is_vermin)[j]; is_vermin_s[i] = (*is_vermin)[j];
pet_info_s[i] = (*pet_info)[j];
is_tame_s[i] = (*is_tame)[j]; is_tame_s[i] = (*is_tame)[j];
is_adopting_s[i] = (*is_adopting)[j]; is_adopting_s[i] = (*is_adopting)[j];
} }
@ -788,7 +784,6 @@ private:
void clear_secondary_viewscreen_vectors() void clear_secondary_viewscreen_vectors()
{ {
is_vermin->clear(); is_vermin->clear();
pet_info->clear();
is_tame->clear(); is_tame->clear();
is_adopting->clear(); is_adopting->clear();
} }
@ -796,7 +791,6 @@ private:
void add_to_filtered_secondary_lists(size_t i) void add_to_filtered_secondary_lists(size_t i)
{ {
is_vermin->push_back(is_vermin_s[i]); is_vermin->push_back(is_vermin_s[i]);
pet_info->push_back(pet_info_s[i]);
is_tame->push_back(is_tame_s[i]); is_tame->push_back(is_tame_s[i]);
is_adopting->push_back(is_adopting_s[i]); is_adopting->push_back(is_adopting_s[i]);
} }
@ -804,7 +798,6 @@ private:
void clear_secondary_saved_lists() void clear_secondary_saved_lists()
{ {
is_vermin_s.clear(); is_vermin_s.clear();
pet_info_s.clear();
is_tame_s.clear(); is_tame_s.clear();
is_adopting_s.clear(); is_adopting_s.clear();
} }
@ -812,7 +805,6 @@ private:
void restore_secondary_values() void restore_secondary_values()
{ {
*is_vermin = is_vermin_s; *is_vermin = is_vermin_s;
*pet_info = pet_info_s;
*is_tame = is_tame_s; *is_tame = is_tame_s;
*is_adopting = is_adopting_s; *is_adopting = is_adopting_s;
} }
@ -834,7 +826,6 @@ private:
} }
std::vector<char > *is_vermin, is_vermin_s; std::vector<char > *is_vermin, is_vermin_s;
std::vector<df::pet_info* > *pet_info, pet_info_s;
std::vector<char > *is_tame, is_tame_s; std::vector<char > *is_tame, is_tame_s;
std::vector<char > *is_adopting, is_adopting_s; std::vector<char > *is_adopting, is_adopting_s;
}; };

@ -42,7 +42,7 @@
#include "df/creature_raw.h" #include "df/creature_raw.h"
#include "df/caste_raw.h" #include "df/caste_raw.h"
#include "df/caste_raw_flags.h" #include "df/caste_raw_flags.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/game_mode.h" #include "df/game_mode.h"
#include "df/unit_misc_trait.h" #include "df/unit_misc_trait.h"
#include "df/job.h" #include "df/job.h"
@ -900,7 +900,10 @@ static bool isTreeTile(df::coord pos)
{ {
auto ptile = Maps::getTileType(pos); auto ptile = Maps::getTileType(pos);
return ptile && tileShape(*ptile) == tiletype_shape::TREE; return ptile &&
(tileShape(*ptile) == tiletype_shape::BRANCH ||
tileShape(*ptile) == tiletype_shape::TRUNK_BRANCH ||
tileShape(*ptile) == tiletype_shape::TWIG);
} }
static bool adjustToTarget(EngineInfo *engine, df::coord *pos) static bool adjustToTarget(EngineInfo *engine, df::coord *pos)

@ -363,7 +363,6 @@ DEFINE_SORT_HANDLER(unit_sorters, pet, "/List", animals)
reorder_cursor(&animals->cursor, order); reorder_cursor(&animals->cursor, order);
reorder_vector(&animals->animal, order); reorder_vector(&animals->animal, order);
reorder_vector(&animals->is_vermin, order); reorder_vector(&animals->is_vermin, order);
reorder_vector(&animals->pet_info, order);
reorder_vector(&animals->is_tame, order); reorder_vector(&animals->is_tame, order);
reorder_vector(&animals->is_adopting, order); reorder_vector(&animals->is_adopting, order);
} }

@ -25,10 +25,10 @@
#include "df/historical_entity.h" #include "df/historical_entity.h"
#include "df/historical_figure.h" #include "df/historical_figure.h"
#include "df/historical_figure_info.h" #include "df/historical_figure_info.h"
#include "df/assumed_identity.h" #include "df/identity.h"
#include "df/language_name.h" #include "df/language_name.h"
#include "df/death_info.h" #include "df/incident.h"
#include "df/criminal_case.h" #include "df/crime.h"
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_layer_unit_actionst.h" #include "df/viewscreen_layer_unit_actionst.h"
@ -980,6 +980,7 @@ struct military_training_ct_hook : df::activity_event_combat_trainingst {
} }
}; };
/*
IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process); IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process);
struct military_training_sd_hook : df::activity_event_skill_demonstrationst { struct military_training_sd_hook : df::activity_event_skill_demonstrationst {
@ -1040,6 +1041,7 @@ struct military_training_id_hook : df::activity_event_individual_skill_drillst {
}; };
IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process); IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process);
*/
struct hive_crash_hook : df::building_hivest { struct hive_crash_hook : df::building_hivest {
typedef df::building_hivest interpose_base; typedef df::building_hivest interpose_base;
@ -1209,13 +1211,13 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
if (!unit) if (!unit)
return CR_FAILURE; return CR_FAILURE;
auto death = df::death_info::find(unit->counters.death_id); auto death = df::incident::find(unit->counters.death_id);
if (death) if (death)
{ {
death->flags.bits.discovered = true; death->flags.bits.discovered = true;
auto crime = df::criminal_case::find(death->crime_id); auto crime = df::crime::find(death->crime_id);
if (crime) if (crime)
crime->flags.bits.discovered = true; crime->flags.bits.discovered = true;
} }
@ -1357,13 +1359,14 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
{ {
enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters); enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters);
} }
/*
else if (cmd == "military-training") else if (cmd == "military-training")
{ {
enable_hook(out, INTERPOSE_HOOK(military_training_ct_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_ct_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_sd_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_sd_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters);
} }*/
else if (cmd == "hive-crash") else if (cmd == "hive-crash")
{ {
enable_hook(out, INTERPOSE_HOOK(hive_crash_hook, updateAction), parameters); enable_hook(out, INTERPOSE_HOOK(hive_crash_hook, updateAction), parameters);

@ -87,9 +87,9 @@ offset('dwarf_civ_index',globals,'ui','civ_id')
vector('races_vector',globals,'world','raws','creatures','all') vector('races_vector',globals,'world','raws','creatures','all')
vector('reactions_vector',globals,'world','raws','reactions') vector('reactions_vector',globals,'world','raws','reactions')
vector('historical_figures',globals,'world','history','figures') vector('historical_figures',globals,'world','history','figures')
vector('fake_identities',globals,'world','assumed_identities','all') vector('fake_identities',globals,'world','identities','all')
vector('historical_figures_vector',globals,'world','history','figures') vector('historical_figures_vector',globals,'world','history','figures')
vector('fake_identities_vector',globals,'world','assumed_identities','all') vector('fake_identities_vector',globals,'world','identities','all')
offset('fortress_entity',globals,'ui','main','fortress_entity') offset('fortress_entity',globals,'ui','main','fortress_entity')
vector('historical_entities_vector',globals,'world','entities','all') vector('historical_entities_vector',globals,'world','entities','all')
vector('weapons_vector',globals,'world','raws','itemdefs','weapons') vector('weapons_vector',globals,'world','raws','itemdefs','weapons')
@ -178,9 +178,9 @@ offset('id',df.historical_figure,'id')
offset('hist_fig_info',df.historical_figure,'info') offset('hist_fig_info',df.historical_figure,'info')
offset('reputation',df.historical_figure_info,'reputation') offset('reputation',df.historical_figure_info,'reputation')
offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity') offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity')
offset('fake_name',df.assumed_identity,'name') offset('fake_name',df.identity,'name')
offset('fake_birth_year',df.assumed_identity,'birth_year') offset('fake_birth_year',df.identity,'birth_year')
offset('fake_birth_time',df.assumed_identity,'birth_second') offset('fake_birth_time',df.identity,'birth_second')
header('weapon_offsets') header('weapon_offsets')
offset('name_plural',df.itemdef_weaponst,'name_plural') offset('name_plural',df.itemdef_weaponst,'name_plural')

@ -4,6 +4,17 @@ local utils = require 'utils'
local ms = require 'memscan' local ms = require 'memscan'
local gui = require 'gui' local gui = require 'gui'
--[[
Arguments:
* global names to force finding them
* 'all' to force all globals
* 'nofeed' to block automated fake input searches
* 'nozoom' to disable neighboring object heuristics
]]
local is_known = dfhack.internal.getAddress local is_known = dfhack.internal.getAddress
local os_type = dfhack.getOSType() local os_type = dfhack.getOSType()
@ -119,12 +130,14 @@ local function zoomed_searcher(startn, end_or_sz)
end end
end end
local finder_searches = {}
local function exec_finder(finder, names) local function exec_finder(finder, names)
if type(names) ~= 'table' then if type(names) ~= 'table' then
names = { names } names = { names }
end end
local search = force_scan['all'] local search = force_scan['all']
for _,v in ipairs(names) do for _,v in ipairs(names) do
table.insert(finder_searches, v)
if force_scan[v] or not is_known(v) then if force_scan[v] or not is_known(v) then
search = true search = true
end end
@ -400,6 +413,12 @@ local function find_gview()
return return
end end
idx, addr = data.uint32_t:find_one{100, vs_vtable}
if idx then
ms.found_offset('gview', addr)
return
end
dfhack.printerr('Could not find gview') dfhack.printerr('Could not find gview')
end end
@ -488,7 +507,7 @@ local function is_valid_world(world)
if not ms.is_valid_vector(world.units.all, 4) if not ms.is_valid_vector(world.units.all, 4)
or not ms.is_valid_vector(world.units.bad, 4) or not ms.is_valid_vector(world.units.bad, 4)
or not ms.is_valid_vector(world.history.figures, 4) or not ms.is_valid_vector(world.history.figures, 4)
or not ms.is_valid_vector(world.cur_savegame.map_features, 4) or not ms.is_valid_vector(world.features.map_features, 4)
then then
dfhack.printerr('Vector layout check failed.') dfhack.printerr('Vector layout check failed.')
return false return false
@ -1409,7 +1428,7 @@ local function find_process_jobs()
Searching for process_jobs. Please do as instructed below:]], Searching for process_jobs. Please do as instructed below:]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'designate a building to be constructed, e.g a bed', { [1] = 'designate a building to be constructed, e.g a bed or a wall',
[0] = 'step or unpause the game to reset the flag' } [0] = 'step or unpause the game to reset the flag' }
) )
ms.found_offset('process_jobs', addr) ms.found_offset('process_jobs', addr)
@ -1527,5 +1546,14 @@ exec_finder(find_process_jobs, 'process_jobs')
exec_finder(find_process_dig, 'process_dig') exec_finder(find_process_dig, 'process_dig')
exec_finder(find_pause_state, 'pause_state') exec_finder(find_pause_state, 'pause_state')
print('\nDone. Now add newly-found globals to symbols.xml.') print('\nDone. Now add newly-found globals to symbols.xml.\n')
for _, global in ipairs(finder_searches) do
local addr = dfhack.internal.getAddress(global)
if addr ~= nil then
local ival = addr - dfhack.internal.getRebaseDelta()
print(string.format("<global-address name='%s' value='0x%x'/>", global, ival))
end
end
searcher:reset() searcher:reset()

@ -417,16 +417,16 @@ function Identity:init(args)
end end
end end
if u.counters.death_id > -1 then -- if undead/ghostly dead or dead-dead if u.counters.death_id > -1 then -- if undead/ghostly dead or dead-dead
self.death_info = df.global.world.deaths.all[u.counters.death_id] self.incident = df.global.world.incidents.all[u.counters.death_id]
if not self.death_info.flags.discovered then if not self.incident.flags.discovered then
self.missing = true self.missing = true
end end
end end
-- slaughtered? -- slaughtered?
if self.death_event then if self.death_event then
self.death_date = Time{year = self.death_event.year, ticks = self.death_event.seconds} self.death_date = Time{year = self.death_event.year, ticks = self.death_event.seconds}
elseif self.death_info then elseif self.incident then
self.death_date = Time{year = self.death_info.event_year, ticks = self.death_info.event_time} self.death_date = Time{year = self.incident.event_year, ticks = self.incident.event_time}
end end
-- age now or age death? -- age now or age death?
if self.dead and self.death_date then -- if cursed with no age? -- if hacked a ressurection, such that they aren't dead anymore, don't use the death date if self.dead and self.death_date then -- if cursed with no age? -- if hacked a ressurection, such that they aren't dead anymore, don't use the death date
@ -753,14 +753,14 @@ function UnitInfoViewer:chunk_Dead()
--str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON --str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON
str = DEATH_TYPES[i.death_event.death_cause]..PERIOD str = DEATH_TYPES[i.death_event.death_cause]..PERIOD
pen = pens.MAGENTA pen = pens.MAGENTA
elseif i.death_info then elseif i.incident then
--str = "The #{u.race_tg.name[0]}" --str = "The #{u.race_tg.name[0]}"
--str << " #{u.name}" if u.name.has_name --str << " #{u.name}" if u.name.has_name
--str << " died" --str << " died"
--str << " in year #{death_info.event_year}" if death_info --str << " in year #{incident.event_year}" if incident
--str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1 --str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1
--str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer --str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer
str = DEATH_TYPES[i.death_info.death_cause]..PERIOD str = DEATH_TYPES[i.incident.death_cause]..PERIOD
pen = pens.MAGENTA pen = pens.MAGENTA
elseif i.unit.flags2.slaughter and i.unit.flags2.killed then elseif i.unit.flags2.slaughter and i.unit.flags2.killed then
str = ' was slaughtered.' str = ' was slaughtered.'