diff --git a/CMakeLists.txt b/CMakeLists.txt index df5943a31..04c9ab215 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/Compile.html b/Compile.html index e031fd87a..4471278cb 100644 --- a/Compile.html +++ b/Compile.html @@ -382,10 +382,10 @@ ul.auto-toc {

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
@@ -553,14 +553,14 @@ make install
 You will need some sort of Windows port of git, or a GUI. Some examples:

-

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
@@ -577,13 +577,7 @@ to your binary search PATH so the tool can be later run from anywhere.

You'll need a copy of Microsoft Visual C++ 2010. The Express version is sufficient. 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:

- +

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.

@@ -666,7 +660,7 @@ I'll make you fix it ;)

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.

@@ -678,13 +672,13 @@ to look at machine code without getting crazy :)

Good windows tools include:

Good linux tools:

Using publicly known information and analyzing the game's data is preferred.

diff --git a/Compile.rst b/Compile.rst index 63686d663..eb15b2c8b 100644 --- a/Compile.rst +++ b/Compile.rst @@ -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. diff --git a/library/Core.cpp b/library/Core.cpp index 175610700..01964139c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -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 #include @@ -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(screen) || + strict_virtual_cast(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 diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index aebe8e5c1..711e0e38a 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -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" diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 17f5258e1..f1887c9a7 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -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(out->mutable_death_info_flags()); + describe_bitfield(out->mutable_death_info_flags()); ENUM(profession); diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 5380d82aa..6462b166c 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -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); diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index ce7d2f1e5..00a80cc48 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -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) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 3dfe89822..86736d646 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -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 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 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 constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); multimap 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 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 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 copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end()); unordered_map 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 copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end()); std::vector& 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 copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); std::vector& 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 copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); std::vector& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index bb9ce6676..fedb95954 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -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; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index c0017fb9b..253e09468 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -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; diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 6fa06d941..89eae64ed 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -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) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 1fd4b80a1..20b5a913c 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -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) || diff --git a/library/modules/Translation.cpp b/library/modules/Translation.cpp index 53ec3bfba..48ec646eb 100644 --- a/library/modules/Translation.cpp +++ b/library/modules/Translation.cpp @@ -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); } } diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 72c61b3d0..9d2ebf2dd 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -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); diff --git a/library/xml b/library/xml index 0d6d3ae7e..fc3c5ce33 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0d6d3ae7ea6f2836b72472739f696a94ec2a3f01 +Subproject commit fc3c5ce3329edcdca5dadb4c1f8b03b0c98f0472 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 143d5dce3..823b9aa75 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -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 diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp new file mode 100644 index 000000000..5dab8f69b --- /dev/null +++ b/plugins/autochop.cpp @@ -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 + +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 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 &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(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 *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 v = burrows_column.getSelectedElems(); + for_each_(v, [] (df::burrow *b) { watchedBurrows.add(b->id); }); + } + +private: + ListColumn 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 tmp; + tmp.insert(key); + INTERPOSE_NEXT(feed)(&tmp); + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *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 & 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 &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; +} diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp index b8021aaf2..69df2d279 100644 --- a/plugins/automaterial.cpp +++ b/plugins/automaterial.cpp @@ -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 || diff --git a/plugins/cursecheck.cpp b/plugins/cursecheck.cpp index d4005d3d0..f7c6d2fd9 100644 --- a/plugins/cursecheck.cpp +++ b/plugins/cursecheck.cpp @@ -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 & 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) { diff --git a/plugins/devel/dumpmats.cpp b/plugins/devel/dumpmats.cpp index 0af1fce50..5aabf807a 100644 --- a/plugins/devel/dumpmats.cpp +++ b/plugins/devel/dumpmats.cpp @@ -231,7 +231,7 @@ command_result df_dumpmats (color_ostream &out, vector ¶meters) 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()); diff --git a/plugins/dig.cpp b/plugins/dig.cpp index bbfc15058..082e6a04a 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -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"; diff --git a/plugins/diggingInvaders/edgeCost.cpp b/plugins/diggingInvaders/edgeCost.cpp index 785c55922..66f2810e2 100644 --- a/plugins/diggingInvaders/edgeCost.cpp +++ b/plugins/diggingInvaders/edgeCost.cpp @@ -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; /* diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index be271750a..4f23e5199 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -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; diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 6c888ee7e..a93625038 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -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 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); } diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 587280385..b03fda561 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -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 & 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 & 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; diff --git a/plugins/feature.cpp b/plugins/feature.cpp index e2ee9d4f1..cbfd7b5e0 100644 --- a/plugins/feature.cpp +++ b/plugins/feature.cpp @@ -33,9 +33,9 @@ static command_result feature(color_ostream &out, vector ¶meters) { 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 ¶meters) 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 ¶meters) 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"); diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 5a5337f45..f4771b58b 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -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 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 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 &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(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 *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 v = burrows_column.getSelectedElems(); - for_each_(v, [] (df::burrow *b) { watchedBurrows.add(b->id); }); - } - -private: - ListColumn 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 tmp; - tmp.insert(key); - INTERPOSE_NEXT(feed)(&tmp); - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *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 & parameters) { @@ -655,17 +46,6 @@ command_result df_getplants (color_ostream &out, vector & 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 & 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 & 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 &commands) { @@ -826,28 +164,12 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector 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; } } diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 5d36dcd5b..98d3181e7 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -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 *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 *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) diff --git a/plugins/plants.cpp b/plugins/plants.cpp index 6eec4c8c1..9509e07f8 100644 --- a/plugins/plants.cpp +++ b/plugins/plants.cpp @@ -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 & 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 *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 & parameters, do return CR_OK; } - +*/ command_result df_grow (color_ostream &out, vector & 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 & 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 *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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 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 & 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 *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) diff --git a/plugins/rename.cpp b/plugins/rename.cpp index fdebad1d3..383066b5e 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -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" diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 9bd63e47e..1cafc3ca6 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -430,7 +430,9 @@ command_result revflood(color_ostream &out, vector & 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 & 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; diff --git a/plugins/search.cpp b/plugins/search.cpp index 72001275c..7bb5cbd62 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -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 *is_vermin, is_vermin_s; - std::vector *pet_info, pet_info_s; std::vector *is_tame, is_tame_s; std::vector *is_adopting, is_adopting_s; }; diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 124f464cc..9a4ee2054 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -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) diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 95ae109a8..e4aad61e2 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -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); } diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 8154b473e..f6ecee913 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -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 ¶meters) 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 ¶meters) { 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); diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua index 90776f1e2..a5a067c08 100644 --- a/scripts/devel/export-dt-ini.lua +++ b/scripts/devel/export-dt-ini.lua @@ -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') diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua index 1ce7a4425..2e1d5cf9c 100644 --- a/scripts/devel/find-offsets.lua +++ b/scripts/devel/find-offsets.lua @@ -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, ival)) + end +end + searcher:reset() diff --git a/scripts/gui/unit-info-viewer.lua b/scripts/gui/unit-info-viewer.lua index 029db28d5..aa8cb3a20 100644 --- a/scripts/gui/unit-info-viewer.lua +++ b/scripts/gui/unit-info-viewer.lua @@ -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.'