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()
# set up versioning.
set(DF_VERSION "0.34.11")
SET(DFHACK_RELEASE "r5" CACHE STRING "Current release revision.")
set(DF_VERSION "0.40.05")
SET(DFHACK_RELEASE "r0" CACHE STRING "Current release revision.")
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
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>
<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>
<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>
<pre class="literal-block">
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
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>
<blockquote>
<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>
</ul>
</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>
<pre class="literal-block">
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
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.
Grab it from Microsoft's site.</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>
<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>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>
<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 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
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
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
fixing.</p>
</div>
@ -678,13 +672,13 @@ to look at machine code without getting crazy :)</p>
<p>Good windows tools include:</p>
<ul class="simple">
<li>Cheat Engine</li>
<li>IDA Pro (the free version)</li>
<li>IDA Pro 5.0 (freely available for non-commercial use)</li>
</ul>
<p>Good linux tools:</p>
<ul class="simple">
<li>angavrilov's df-structures gui (visit us on IRC for details).</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>
</ul>
<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.
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::
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
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.
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 :)
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::
git clone git://github.com/peterix/dfhack.git
git clone git://github.com/DFHack/dfhack.git
cd dfhack
git submodule init
git submodule update
@ -194,12 +194,7 @@ Grab it from Microsoft's site.
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:
* 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".
For the code generation parts, you'll need perl with XML::LibXML and XML::LibXSLT. Strawberry Perl works nicely for this: http://strawberryperl.com/
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.
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
fixing.
@ -321,13 +316,13 @@ to look at machine code without getting crazy :)
Good windows tools include:
* Cheat Engine
* IDA Pro (the free version)
* IDA Pro 5.0 (freely available for non-commercial use)
Good linux tools:
* angavrilov's df-structures gui (visit us on IRC for details).
* 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.
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/interfacest.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_loadgamest.h"
#include "df/viewscreen_savegamest.h"
#include <df/graphic.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) {
//first try the save folder if it exists
string save = World::ReadWorldFolder();
if ( save != "" ) {
string file = path + "/data/save/" + save + "/raw/scripts/" + name;
if (fileExists(file)) {
return file;
if (df::global::world) {
//first try the save folder if it exists
string save = World::ReadWorldFolder();
if ( save != "" ) {
string file = path + "/data/save/" + save + "/raw/scripts/" + name;
if (fileExists(file)) {
return file;
}
}
}
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)
{
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))
{
core->runCommand(out, "gui/no-dfhack-init");
@ -1260,10 +1270,23 @@ void Core::doUpdate(color_ostream &out, bool first_update)
if (first_update)
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
void *new_wdata = 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;
// 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
if (df::global::gview)
if (screen != top_viewscreen)
{
df::viewscreen *screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
if (screen != top_viewscreen)
{
top_viewscreen = screen;
onStateChange(out, SC_VIEWSCREEN_CHANGED);
}
top_viewscreen = screen;
onStateChange(out, SC_VIEWSCREEN_CHANGED);
}
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) {
//TODO: use different separators for windows
if (!df::global::world)
return;
//TODO: use different separators for windows
#ifdef _WIN32
static const std::string separator = "\\";
#else

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

@ -76,7 +76,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "df/historical_entity.h"
#include "df/squad.h"
#include "df/squad_position.h"
#include "df/death_info.h"
#include "df/incident.h"
#include "BasicApi.pb.h"
@ -283,7 +283,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit,
if (unit->counters.death_id >= 0)
{
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);
}
@ -455,7 +455,7 @@ static command_result ListEnums(color_ostream &stream,
BITFIELD(cie_add_tag_mask1);
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);

@ -41,7 +41,7 @@ namespace df
{
struct nemesis_record;
struct burrow;
struct assumed_identity;
struct identity;
struct historical_entity;
struct entity_position_assignment;
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 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 bool isHidingCurse(df::unit *unit);

@ -268,7 +268,7 @@ function found_offset(name,val)
if cval 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
else
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);
*/
//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;
nextBuilding = *df::global::building_next_id;
@ -291,6 +301,9 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
if ( !gameLoaded ) {
return;
}
if (!df::global::world)
return;
CoreSuspender suspender;
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) {
if (!df::global::world)
return;
unordered_set<EventHandler> toRemove;
int32_t tick = df::global::world->frame_counter;
while ( !tickQueue.empty() ) {
@ -342,6 +357,10 @@ static void manageTickEvent(color_ostream& out) {
}
static void manageJobInitiatedEvent(color_ostream& out) {
if (!df::global::world)
return;
if (!df::global::job_next_id)
return;
if ( lastJobId == -1 ) {
lastJobId = *df::global::job_next_id - 1;
return;
@ -375,6 +394,8 @@ static int32_t getWorkerID(df::job* job) {
TODO: consider checking item creation / experience gain just in case
*/
static void manageJobCompletedEvent(color_ostream& out) {
if (!df::global::world)
return;
int32_t tick0 = eventLastTick[EventType::JOB_COMPLETED];
int32_t tick1 = df::global::world->frame_counter;
@ -500,6 +521,8 @@ static void manageJobCompletedEvent(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());
for ( size_t a = 0; a < df::global::world->units.all.size(); 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) {
if (!df::global::world)
return;
if (!df::global::item_next_id)
return;
if ( nextItem >= *df::global::item_next_id ) {
return;
}
@ -552,6 +579,10 @@ static void manageItemCreationEvent(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
* consider looking at jobs: building creation / destruction
@ -591,6 +622,8 @@ static void manageBuildingEvent(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());
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) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end());
int32_t highestTime = -1;
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) {
if (!df::global::ui)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end());
if ( df::global::ui->invasions.next_id <= nextInvasion )
@ -666,6 +703,8 @@ static void manageInvasionEvent(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());
unordered_map<int32_t, InventoryItem> itemIdToInventoryItem;
@ -747,6 +786,8 @@ static void manageEquipmentEvent(color_ostream& out) {
}
static void updateReportToRelevantUnits() {
if (!df::global::world)
return;
if ( df::global::world->frame_counter <= reportToRelevantUnitsTime )
return;
reportToRelevantUnitsTime = df::global::world->frame_counter;
@ -767,6 +808,8 @@ static void updateReportToRelevantUnits() {
}
static void manageReportEvent(color_ostream& out) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
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) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
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) {
if (!df::global::world)
return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end());
std::vector<df::report*>& reports = df::global::world->status.reports;
size_t a = df::report::binsearch_index(reports, lastReportInteraction, false);

@ -1310,6 +1310,9 @@ void Gui::showAutoAnnouncement(
df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed)
{
if (!gview)
return NULL;
df::viewscreen * ws = &gview->view;
while (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::PLANT:
// case item_type::LEAVES:
case item_type::PLANT_GROWTH:
case item_type::CHEESE:
value = 2;
break;

@ -253,15 +253,15 @@ void Maps::enableBlockUpdates(df::map_block *blk, bool flow, bool temperature)
blk->flags.bits.update_liquid_twice = true;
}
//auto z_flags = world->map.z_level_flags;
//int z_level = blk->map_pos.z;
//if (z_flags && z_level >= 0 && z_level < world->map.z_count_block)
//{
// z_flags += z_level;
// z_flags->bits.update = true;
// z_flags->bits.update_twice = true;
//}
auto z_flags = world->map_extras.z_level_flags;
int z_level = blk->map_pos.z;
if (z_flags && z_level >= 0 && z_level < world->map.z_count_block)
{
z_flags += z_level;
z_flags->bits.update = 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)

@ -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_vermin, false);
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(solid, !(MAT_FLAG(ALCOHOL_PLANT) ||
MAT_FLAG(ALCOHOL_CREATURE) ||

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

@ -61,7 +61,7 @@ using namespace std;
#include "df/entity_position.h"
#include "df/entity_position_assignment.h"
#include "df/histfig_entity_link_positionst.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/burrow.h"
#include "df/creature_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);
}
static df::assumed_identity *getFigureIdentity(df::historical_figure *figure)
static df::identity *getFigureIdentity(df::historical_figure *figure)
{
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;
}
df::assumed_identity *Units::getIdentity(df::unit *unit)
df::identity *Units::getIdentity(df::unit *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)
if(BUILD_ISOWORLD)
add_subdirectory (isoworld)
IF(UNIX)
if (APPLE)
#TODO: add an OSX runner script
else()
# On linux, copy our version of the df launch script which sets LD_PRELOAD
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/runisoworld
DESTINATION .)
endif()
ENDIF()
add_subdirectory (isoworld)
IF(UNIX)
if (APPLE)
#TODO: add an OSX runner script
else()
# On linux, copy our version of the df launch script which sets LD_PRELOAD
install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/runisoworld
DESTINATION .)
endif()
ENDIF()
endif()
OPTION(BUILD_DEV_PLUGINS "Build developer plugins." OFF)
@ -38,12 +38,12 @@ endif()
OPTION(BUILD_MAPEXPORT "Build map exporter." ON)
if (BUILD_MAPEXPORT)
add_subdirectory (mapexport)
# add_subdirectory (mapexport)
endif()
OPTION(BUILD_DWARFEXPORT "Build dwarf exporter." ON)
if (BUILD_DWARFEXPORT)
add_subdirectory (dwarfexport)
# add_subdirectory (dwarfexport)
endif()
OPTION(BUILD_RUBY "Build ruby binding." ON)
@ -82,84 +82,85 @@ SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE )
# Plugins
OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON)
if (BUILD_SUPPORTED)
# DFHACK_PLUGIN(3dveins 3dveins.cpp)
# DFHACK_PLUGIN(add-spatter add-spatter.cpp)
DFHACK_PLUGIN(3dveins 3dveins.cpp)
DFHACK_PLUGIN(add-spatter add-spatter.cpp)
# DFHACK_PLUGIN(advtools advtools.cpp)
# DFHACK_PLUGIN(autodump autodump.cpp)
# DFHACK_PLUGIN(autolabor autolabor.cpp)
# DFHACK_PLUGIN(automaterial automaterial.cpp)
# DFHACK_PLUGIN(autotrade autotrade.cpp)
# DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(buildingplan buildingplan.cpp)
# DFHACK_PLUGIN(catsplosion catsplosion.cpp)
# DFHACK_PLUGIN(changeitem changeitem.cpp)
# DFHACK_PLUGIN(changelayer changelayer.cpp)
# DFHACK_PLUGIN(changevein changevein.cpp)
# DFHACK_PLUGIN(cleanconst cleanconst.cpp)
# DFHACK_PLUGIN(cleaners cleaners.cpp)
# DFHACK_PLUGIN(cleanowned cleanowned.cpp)
# DFHACK_PLUGIN(colonies colonies.cpp)
# DFHACK_PLUGIN(command-prompt command-prompt.cpp)
# DFHACK_PLUGIN(createitem createitem.cpp)
# DFHACK_PLUGIN(cursecheck cursecheck.cpp)
# DFHACK_PLUGIN(deramp deramp.cpp)
# DFHACK_PLUGIN(dig dig.cpp)
# DFHACK_PLUGIN(digFlood digFlood.cpp)
# add_subdirectory(diggingInvaders)
# DFHACK_PLUGIN(drybuckets drybuckets.cpp)
# DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
# DFHACK_PLUGIN(embark-tools embark-tools.cpp)
# DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
# DFHACK_PLUGIN(feature feature.cpp)
# DFHACK_PLUGIN(filltraffic filltraffic.cpp)
DFHACK_PLUGIN(autochop autochop.cpp)
DFHACK_PLUGIN(autodump autodump.cpp)
DFHACK_PLUGIN(autolabor autolabor.cpp)
DFHACK_PLUGIN(automaterial automaterial.cpp)
DFHACK_PLUGIN(autotrade autotrade.cpp)
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(buildingplan buildingplan.cpp)
DFHACK_PLUGIN(catsplosion catsplosion.cpp)
DFHACK_PLUGIN(changeitem changeitem.cpp)
DFHACK_PLUGIN(changelayer changelayer.cpp)
DFHACK_PLUGIN(changevein changevein.cpp)
DFHACK_PLUGIN(cleanconst cleanconst.cpp)
DFHACK_PLUGIN(cleaners cleaners.cpp)
DFHACK_PLUGIN(cleanowned cleanowned.cpp)
DFHACK_PLUGIN(colonies colonies.cpp)
DFHACK_PLUGIN(command-prompt command-prompt.cpp)
DFHACK_PLUGIN(createitem createitem.cpp)
DFHACK_PLUGIN(cursecheck cursecheck.cpp)
DFHACK_PLUGIN(deramp deramp.cpp)
DFHACK_PLUGIN(dig dig.cpp)
DFHACK_PLUGIN(digFlood digFlood.cpp)
add_subdirectory(diggingInvaders)
DFHACK_PLUGIN(drybuckets drybuckets.cpp)
DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp)
DFHACK_PLUGIN(embark-tools embark-tools.cpp)
DFHACK_PLUGIN(eventful eventful.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
DFHACK_PLUGIN(feature feature.cpp)
DFHACK_PLUGIN(filltraffic filltraffic.cpp)
# DFHACK_PLUGIN(fix-armory fix-armory.cpp)
# DFHACK_PLUGIN(fixpositions fixpositions.cpp)
# DFHACK_PLUGIN(fixveins fixveins.cpp)
# DFHACK_PLUGIN(flows flows.cpp)
# DFHACK_PLUGIN(follow follow.cpp)
# DFHACK_PLUGIN(forceequip forceequip.cpp)
# DFHACK_PLUGIN(getplants getplants.cpp)
# DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
# DFHACK_PLUGIN(initflags initflags.cpp)
DFHACK_PLUGIN(fixpositions fixpositions.cpp)
DFHACK_PLUGIN(fixveins fixveins.cpp)
DFHACK_PLUGIN(flows flows.cpp)
DFHACK_PLUGIN(follow follow.cpp)
DFHACK_PLUGIN(forceequip forceequip.cpp)
DFHACK_PLUGIN(getplants getplants.cpp)
DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)
DFHACK_PLUGIN(initflags initflags.cpp)
DFHACK_PLUGIN(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote)
# DFHACK_PLUGIN(jobutils jobutils.cpp)
# DFHACK_PLUGIN(lair lair.cpp)
# DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua)
# DFHACK_PLUGIN(manipulator manipulator.cpp)
# DFHACK_PLUGIN(mode mode.cpp)
# DFHACK_PLUGIN(misery misery.cpp)
# DFHACK_PLUGIN(mousequery mousequery.cpp)
# DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
# DFHACK_PLUGIN(plants plants.cpp)
DFHACK_PLUGIN(jobutils jobutils.cpp)
DFHACK_PLUGIN(lair lair.cpp)
DFHACK_PLUGIN(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua)
DFHACK_PLUGIN(manipulator manipulator.cpp)
DFHACK_PLUGIN(mode mode.cpp)
DFHACK_PLUGIN(misery misery.cpp)
DFHACK_PLUGIN(mousequery mousequery.cpp)
DFHACK_PLUGIN(petcapRemover petcapRemover.cpp)
DFHACK_PLUGIN(plants plants.cpp)
DFHACK_PLUGIN(probe probe.cpp)
# DFHACK_PLUGIN(prospector prospector.cpp)
# DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(regrass regrass.cpp)
DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader)
# DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename)
DFHACK_PLUGIN(prospector prospector.cpp)
DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(regrass regrass.cpp)
DFHACK_PLUGIN(remotefortressreader remotefortressreader.cpp PROTOBUFS RemoteFortressReader)
DFHACK_PLUGIN(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename)
# add_subdirectory(rendermax)
# DFHACK_PLUGIN(resume resume.cpp)
# DFHACK_PLUGIN(reveal reveal.cpp)
# DFHACK_PLUGIN(search search.cpp)
# DFHACK_PLUGIN(seedwatch seedwatch.cpp)
# DFHACK_PLUGIN(showmood showmood.cpp)
# DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(steam-engine steam-engine.cpp)
# DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(stockpiles stockpiles.cpp)
# DFHACK_PLUGIN(stocks stocks.cpp)
# DFHACK_PLUGIN(strangemood strangemood.cpp)
# DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
DFHACK_PLUGIN(resume resume.cpp)
DFHACK_PLUGIN(reveal reveal.cpp)
DFHACK_PLUGIN(search search.cpp)
DFHACK_PLUGIN(seedwatch seedwatch.cpp)
DFHACK_PLUGIN(showmood showmood.cpp)
DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(steam-engine steam-engine.cpp)
DFHACK_PLUGIN(stockflow stockflow.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(stockpiles stockpiles.cpp)
DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(strangemood strangemood.cpp)
DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
# DFHACK_PLUGIN(treefarm treefarm.cpp)
# DFHACK_PLUGIN(tubefill tubefill.cpp)
# DFHACK_PLUGIN(tweak tweak.cpp)
# DFHACK_PLUGIN(weather weather.cpp)
# DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua)
# DFHACK_PLUGIN(workNow workNow.cpp)
# DFHACK_PLUGIN(zone zone.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(tubefill tubefill.cpp)
DFHACK_PLUGIN(tweak tweak.cpp)
DFHACK_PLUGIN(weather weather.cpp)
DFHACK_PLUGIN(workflow workflow.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(workNow workNow.cpp)
DFHACK_PLUGIN(zone zone.cpp LINK_LIBRARIES lua)
endif()
# 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;
}
if (shape == tiletype_shape::TREE)
return false;
if (material == tiletype_material::FIRE ||
material == tiletype_material::POOL ||
material == tiletype_material::BROOK ||

@ -38,11 +38,11 @@ using namespace std;
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/language_name.h"
#include "df/world.h"
#include "df/world_raws.h"
#include "df/death_info.h"
#include "df/incident.h"
using std::vector;
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
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)
{
@ -260,7 +260,7 @@ command_result cursecheck (color_ostream &out, vector <string> & parameters)
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;
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++)
out.print("\t[REACTION_CLASS:%s]\n", mat->reaction_class[i]->c_str());
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)
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
&& (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";

@ -73,7 +73,9 @@ cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilitie
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;
/*

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

@ -106,7 +106,7 @@ public:
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
{
bool is_top = false;
if (screen->embark_pos_min.y == 0)
if (screen->location.embark_pos_min.y == 0)
is_top = true;
std::set<df::interface_key> keys;
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
* Local area resizes up and to the right, unless it's already touching the edge
*/
int x1 = screen->embark_pos_min.x,
x2 = screen->embark_pos_max.x,
y1 = screen->embark_pos_min.y,
y2 = screen->embark_pos_max.y,
int x1 = screen->location.embark_pos_min.x,
x2 = screen->location.embark_pos_max.x,
y1 = screen->location.embark_pos_min.y,
y2 = screen->location.embark_pos_max.y,
width = x2 - x1 + dx,
height = y2 - y1 + dy;
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);
screen->embark_pos_min.x = x1;
screen->embark_pos_max.x = x2;
screen->embark_pos_min.y = y1;
screen->embark_pos_max.y = y2;
screen->location.embark_pos_min.x = x1;
screen->location.embark_pos_max.x = x2;
screen->location.embark_pos_min.y = y1;
screen->location.embark_pos_max.y = y2;
update_embark_sidebar(screen);
}
@ -183,10 +183,10 @@ int sticky_pos[] = {0, 0, 3, 3};
bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen)
{
sticky_pos[0] = screen->embark_pos_min.x;
sticky_pos[1] = screen->embark_pos_max.x;
sticky_pos[2] = screen->embark_pos_min.y;
sticky_pos[3] = screen->embark_pos_max.y;
sticky_pos[0] = screen->location.embark_pos_min.x;
sticky_pos[1] = screen->location.embark_pos_max.x;
sticky_pos[2] = screen->location.embark_pos_min.y;
sticky_pos[3] = screen->location.embark_pos_max.y;
}
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
return;
}
screen->embark_pos_min.x = sticky_pos[0];
screen->embark_pos_max.x = sticky_pos[1];
screen->embark_pos_min.y = sticky_pos[2];
screen->embark_pos_max.y = sticky_pos[3];
screen->location.embark_pos_min.x = sticky_pos[0];
screen->location.embark_pos_max.x = sticky_pos[1];
screen->location.embark_pos_min.y = sticky_pos[2];
screen->location.embark_pos_max.y = sticky_pos[3];
update_embark_sidebar(screen);
}

@ -8,13 +8,17 @@
#include "DataDefs.h"
#include "df/world.h"
#include "df/unit.h"
#include "df/unit_action.h"
#include "df/map_block.h"
using std::string;
using std::vector;
using namespace DFHack;
using namespace df::enums;
using df::global::world;
using df::global::debug_turbospeed;
// dfhack interface
DFHACK_PLUGIN("fastdwarf");
@ -26,8 +30,8 @@ static bool enable_teledwarf = false;
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
if (df::global::debug_turbospeed)
*df::global::debug_turbospeed = false;
if (debug_turbospeed)
*debug_turbospeed = false;
return CR_OK;
}
@ -51,22 +55,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (!Units::isCitizen(unit))
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
{
// 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
if ((unit->relations.draggee_id != -1) || (unit->relations.dragger_id != -1))
break;
@ -79,6 +69,11 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (unit->counters.unconscious > 0)
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
auto old_occ = Maps::getTileOccupancy(unit->pos);
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->path.path.clear();
} 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;
}
@ -131,21 +182,21 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
if (parameters[0] == "0")
{
enable_fastdwarf = false;
if (df::global::debug_turbospeed)
*df::global::debug_turbospeed = false;
if (debug_turbospeed)
*debug_turbospeed = false;
}
else if (parameters[0] == "1")
{
enable_fastdwarf = true;
if (df::global::debug_turbospeed)
*df::global::debug_turbospeed = false;
if (debug_turbospeed)
*debug_turbospeed = false;
}
else if (parameters[0] == "2")
{
if (df::global::debug_turbospeed)
if (debug_turbospeed)
{
enable_fastdwarf = false;
*df::global::debug_turbospeed = true;
*debug_turbospeed = true;
}
else
{
@ -160,7 +211,7 @@ static command_result fastdwarf (color_ostream &out, vector <string> & parameter
active = enable_fastdwarf || enable_teledwarf;
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);
return CR_OK;

@ -33,9 +33,9 @@ static command_result feature(color_ostream &out, vector <string> &parameters)
{
if (parameters.size() != 1)
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;
feature_init->getName(&name);
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)
return CR_WRONG_USAGE;
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");
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))
{
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)
return CR_WRONG_USAGE;
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");
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))
{
out.print("Selected feature is already hidden!\n");

@ -1,7 +1,5 @@
// (un)designate matching plants for gathering/cutting
#include "uicommon.h"
#include "Core.h"
#include "Console.h"
#include "Export.h"
@ -14,20 +12,8 @@
#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;
@ -37,601 +23,6 @@ using namespace DFHack;
using namespace df::enums;
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)
{
@ -655,17 +46,6 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
exclude = true;
else if(parameters[i] == "-a")
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
plantNames.insert(parameters[i]);
}
@ -721,45 +101,44 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
}
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;
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];
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)))
if (exclude)
continue;
if (cur->designation[x][y].bits.hidden)
}
else
{
if (!exclude)
continue;
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;
}
}
df::tiletype_shape shape = tileShape(cur->tiletype[x][y]);
df::tiletype_material material = tileMaterial(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 || !(material == tiletype_material::TREE)))
continue;
if (cur->designation[x][y].bits.hidden)
continue;
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)
cur->flags.bits.designated = true;
@ -769,48 +148,7 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
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;
}
DFHACK_PLUGIN("getplants");
DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCommand> &commands)
{
@ -826,28 +164,12 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCom
" -x - Apply selected action to all plants except those specified\n"
" -a - Select every type of plant (obeys -t/-s)\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"
" autochop - Opens the automated chopping control screen\n"
"If no plant IDs are specified, all valid plant IDs will be listed.\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;
}
}

@ -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];
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].bits.update = 1;
world->map.z_count_block++;
world->map.z_count++;
delete[] world->map.z_level_flags;
world->map.z_level_flags = flags;
delete[] world->map_extras.z_level_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_FURNITURE, job_skill::NONE, "Fu"},
{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
{12, 4, profession::ARCHITECT, unit_labor::ARCHITECT, job_skill::DESIGNBUILDING, "Ar"},
{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::COORDINATION, "Cr"},
{15, 8, profession::NONE, unit_labor::NONE, job_skill::BALANCE, "Ba"},
{15, 8, profession::NONE, unit_labor::NONE, job_skill::CLIMBING, "Cl"},
// Social
{16, 3, profession::NONE, unit_labor::NONE, job_skill::PERSUASION, "Pe"},
{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))
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;
}
}
@ -768,7 +769,7 @@ void viewscreen_unitlaborsst::feed(set<df::interface_key> *events)
if ((gps->mouse_x >= col_offsets[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;
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;
switch (click_header)

@ -20,10 +20,11 @@ using std::string;
using namespace DFHack;
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");
/* Immolate/Extirpate no longer work in 0.40
enum do_what
{
do_immolate,
@ -59,12 +60,13 @@ static bool getoptions( vector <string> & parameters, bool & shrubs, bool & tree
return true;
}
/**
* Book of Immolations, chapter 1, verse 35:
* 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.
* Armok was pleased and great temples were built by the dwarves, for they shared his hatred for trees and plants.
*/
//
// Book of Immolations, chapter 1, verse 35:
// 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.
// 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)
{
CoreSuspender suspend;
@ -90,37 +92,30 @@ static command_result immolations (color_ostream &out, do_what what, bool shrubs
destroyed ++;
}
}
out.print("Praise Armok!\n");
out.print("Praise Armok! %i plants destroyed.\n", destroyed);
}
else
{
int32_t x,y,z;
if(Gui::getCursorCoords(x,y,z))
{
auto block = Maps::getTileBlock(x,y,z);
vector<df::plant *> *alltrees = block ? &block->plants : NULL;
if(alltrees)
bool didit = false;
for(size_t i = 0; i < world->plants.all.size(); i++)
{
bool didit = false;
for(size_t i = 0 ; i < alltrees->size(); i++)
{
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)
df::plant *tree = world->plants.all[i];
if(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
{
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
{
@ -157,16 +152,16 @@ command_result df_immolate (color_ostream &out, vector <string> & parameters, do
return CR_OK;
}
*/
command_result df_grow (color_ostream &out, vector <string> & parameters)
{
for(size_t i = 0; i < parameters.size();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"
"With active cursor, work on the targetted one only.\n";
"With active cursor, work on the targetted one only.\n");
return CR_OK;
}
}
@ -180,30 +175,26 @@ command_result df_grow (color_ostream &out, vector <string> & parameters)
}
MapExtras::MapCache map;
int32_t x,y,z;
int grown = 0;
if(Gui::getCursorCoords(x,y,z))
{
auto block = Maps::getTileBlock(x,y,z);
vector<df::plant *> *alltrees = block ? &block->plants : NULL;
if(alltrees)
for(size_t i = 0; i < world->plants.all.size(); i++)
{
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(tree->pos.x == x && tree->pos.y == y && tree->pos.z == z)
if(tileShape(map.tiletypeAt(DFCoord(x,y,z))) == tiletype_shape::SAPLING &&
tileSpecial(map.tiletypeAt(DFCoord(x,y,z))) != tiletype_special::DEAD)
{
if(tileShape(map.tiletypeAt(DFCoord(x,y,z))) == tiletype_shape::SAPLING &&
tileSpecial(map.tiletypeAt(DFCoord(x,y,z))) != tiletype_special::DEAD)
{
tree->grow_counter = sapling_to_tree_threshold;
}
break;
tree->grow_counter = sapling_to_tree_threshold;
grown++;
}
break;
}
}
}
else
{
int grown = 0;
for(size_t i = 0 ; i < world->plants.all.size(); 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)
{
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;
}
@ -222,10 +218,10 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
{
if ((parameters.size() != 1) || (parameters[0] == "help" || parameters[0] == "?"))
{
out << "Usage:\n"
out.print("Usage:\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"
"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;
}
@ -244,7 +240,8 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
return CR_FAILURE;
}
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");
return CR_FAILURE;
@ -296,10 +293,6 @@ command_result df_createplant (color_ostream &out, vector <string> & parameters)
plant->pos.y = y;
plant->pos.z = z;
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);
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 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)
map->tiletype[tx][ty] = tiletype::Shrub;
else
@ -326,6 +319,7 @@ command_result df_plant (color_ostream &out, vector <string> & parameters)
parameters.erase(parameters.begin());
return df_grow(out, parameters);
} else
/*
if (parameters[0] == "immolate") {
parameters.erase(parameters.begin());
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());
return df_immolate(out, parameters, do_extirpate);
} else
*/
if (parameters[0] == "create") {
parameters.erase(parameters.begin());
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,
"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 immolate help - Set plants on fire.\n"
"plant extirpate help - Kill plants.\n"
// "plant immolate help - Set plants on fire.\n"
// "plant extirpate help - Kill plants.\n"
"plant create help - Create a new plant.\n"));
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;
coord2d cur_region = screen->region_pos;
coord2d cur_region = screen->location.region_pos;
auto cur_details = get_details(data, cur_region);
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]]++;
}*/
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;
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
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;
if(plants)
{
for (PlantList::const_iterator it = plants->begin(); it != plants->end(); it++)
{
const df::plant & plant = *(*it);
if (plant.pos.z != z)
continue;
df::coord2d loc(plant.pos.x, plant.pos.y);
loc = loc % 16;
if (showHidden || !b->DesignationAt(loc).bits.hidden)

@ -22,7 +22,7 @@
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/language_name.h"
#include "df/building_stockpilest.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::RAMP:
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::SHRUB:
case tiletype_shape::BOULDER:
@ -442,6 +444,12 @@ command_result revflood(color_ostream &out, vector<string> & params)
above = sides = true;
break;
}
if (tileMaterial(tt) == tiletype_material::PLANT || tileMaterial(tt) == tiletype_material::MUSHROOM)
{
if(from_below)
unhide = 0;
above = sides = true;
}
if(unhide)
{
des.bits.hidden = false;

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

@ -42,7 +42,7 @@
#include "df/creature_raw.h"
#include "df/caste_raw.h"
#include "df/caste_raw_flags.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/game_mode.h"
#include "df/unit_misc_trait.h"
#include "df/job.h"
@ -900,7 +900,10 @@ static bool isTreeTile(df::coord 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)

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

@ -25,10 +25,10 @@
#include "df/historical_entity.h"
#include "df/historical_figure.h"
#include "df/historical_figure_info.h"
#include "df/assumed_identity.h"
#include "df/identity.h"
#include "df/language_name.h"
#include "df/death_info.h"
#include "df/criminal_case.h"
#include "df/incident.h"
#include "df/crime.h"
#include "df/unit_inventory_item.h"
#include "df/viewscreen_dwarfmodest.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);
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);
*/
struct hive_crash_hook : df::building_hivest {
typedef df::building_hivest interpose_base;
@ -1209,13 +1211,13 @@ static command_result tweak(color_ostream &out, vector <string> &parameters)
if (!unit)
return CR_FAILURE;
auto death = df::death_info::find(unit->counters.death_id);
auto death = df::incident::find(unit->counters.death_id);
if (death)
{
death->flags.bits.discovered = true;
auto crime = df::criminal_case::find(death->crime_id);
auto crime = df::crime::find(death->crime_id);
if (crime)
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);
}
/*
else if (cmd == "military-training")
{
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_sp_hook, process), parameters);
enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters);
}
}*/
else if (cmd == "hive-crash")
{
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('reactions_vector',globals,'world','raws','reactions')
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('fake_identities_vector',globals,'world','assumed_identities','all')
vector('fake_identities_vector',globals,'world','identities','all')
offset('fortress_entity',globals,'ui','main','fortress_entity')
vector('historical_entities_vector',globals,'world','entities','all')
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('reputation',df.historical_figure_info,'reputation')
offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity')
offset('fake_name',df.assumed_identity,'name')
offset('fake_birth_year',df.assumed_identity,'birth_year')
offset('fake_birth_time',df.assumed_identity,'birth_second')
offset('fake_name',df.identity,'name')
offset('fake_birth_year',df.identity,'birth_year')
offset('fake_birth_time',df.identity,'birth_second')
header('weapon_offsets')
offset('name_plural',df.itemdef_weaponst,'name_plural')

@ -4,6 +4,17 @@ local utils = require 'utils'
local ms = require 'memscan'
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 os_type = dfhack.getOSType()
@ -119,12 +130,14 @@ local function zoomed_searcher(startn, end_or_sz)
end
end
local finder_searches = {}
local function exec_finder(finder, names)
if type(names) ~= 'table' then
names = { names }
end
local search = force_scan['all']
for _,v in ipairs(names) do
table.insert(finder_searches, v)
if force_scan[v] or not is_known(v) then
search = true
end
@ -400,6 +413,12 @@ local function find_gview()
return
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')
end
@ -488,7 +507,7 @@ local function is_valid_world(world)
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.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
dfhack.printerr('Vector layout check failed.')
return false
@ -1409,7 +1428,7 @@ local function find_process_jobs()
Searching for process_jobs. Please do as instructed below:]],
'int8_t',
{ 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' }
)
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_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()

@ -417,16 +417,16 @@ function Identity:init(args)
end
end
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]
if not self.death_info.flags.discovered then
self.incident = df.global.world.incidents.all[u.counters.death_id]
if not self.incident.flags.discovered then
self.missing = true
end
end
-- slaughtered?
if self.death_event then
self.death_date = Time{year = self.death_event.year, ticks = self.death_event.seconds}
elseif self.death_info then
self.death_date = Time{year = self.death_info.event_year, ticks = self.death_info.event_time}
elseif self.incident then
self.death_date = Time{year = self.incident.event_year, ticks = self.incident.event_time}
end
-- 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
@ -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 = DEATH_TYPES[i.death_event.death_cause]..PERIOD
pen = pens.MAGENTA
elseif i.death_info then
elseif i.incident then
--str = "The #{u.race_tg.name[0]}"
--str << " #{u.name}" if u.name.has_name
--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 << " 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
elseif i.unit.flags2.slaughter and i.unit.flags2.killed then
str = ' was slaughtered.'