From 10e13c532a6f96125be67e56a576809e29940613 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 25 Jun 2017 15:21:45 -0400 Subject: [PATCH] Add "tweak cage-butcher" and some extra Building module functions * Buildings::markedForRemoval() * Buildings::getCageOccupants() Closes #906 --- dfhack.init-example | 1 + docs/Lua API.rst | 11 ++++ docs/Plugins.rst | 1 + library/LuaApi.cpp | 15 ++++- library/include/modules/Buildings.h | 18 +++++- library/modules/Buildings.cpp | 42 +++++++++++++ plugins/tweak/tweak.cpp | 8 +++ plugins/tweak/tweaks/cage-butcher.h | 98 +++++++++++++++++++++++++++++ 8 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 plugins/tweak/tweaks/cage-butcher.h diff --git a/dfhack.init-example b/dfhack.init-example index 13fd6191a..87462975c 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -198,6 +198,7 @@ tweak embark-profile-name # Misc. UI tweaks tweak block-labors # Prevents labors that can't be used from being toggled +tweak cage-butcher tweak civ-view-agreement tweak eggs-fertile tweak fps-min diff --git a/docs/Lua API.rst b/docs/Lua API.rst index fb09f96fd..52470225b 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -1486,6 +1486,12 @@ General Returns a list of items stored on the given stockpile. Ignores empty bins, barrels, and wheelbarrows assigned as storage and transport for that stockpile. +* ``dfhack.buildings.getCageOccupants(cage)`` + + Returns a list of units in the given built cage. Note that this is different + from the list of units assigned to the cage, which can be accessed with + ``cage.assigned_units``. + Low-level ~~~~~~~~~ Low-level building creation functions: @@ -1531,6 +1537,11 @@ Low-level building creation functions: Destroys the building, or queues a deconstruction job. Returns *true* if the building was destroyed and deallocated immediately. +* ``dfhack.buildings.markedForRemoval(building)`` + + Returns *true* if the building is marked for removal (with :kbd:`x`), *false* + otherwise. + High-level ~~~~~~~~~~ More high-level functions are implemented in lua and can be loaded by diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 95d827f71..a1fd57e85 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -275,6 +275,7 @@ Subcommands that persist until disabled or DF quits: the contents separately from the container. This forcefully skips child reagents. :block-labors: Prevents labors that can't be used from being toggled +:cage-butcher: Adds an option to butcher units when viewing cages with :kbd:`q` :civ-view-agreement: Fixes overlapping text on the "view agreement" screen :condition-material: Fixes a crash in the work order contition material list (:bug:`9905`). :craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`). diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index ca958cfdc..4acba0809 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -85,6 +85,7 @@ distribution. #include "df/dfhack_material_category.h" #include "df/job_material_category.h" #include "df/burrow.h" +#include "df/building_cagest.h" #include "df/building_civzonest.h" #include "df/region_map_entry.h" #include "df/flow_info.h" @@ -1985,6 +1986,7 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { WRAPM(Buildings, isPenPasture), WRAPM(Buildings, isPitPond), WRAPM(Buildings, isActive), + WRAPM(Buildings, markedForRemoval), { NULL, NULL } }; @@ -2066,13 +2068,22 @@ static int buildings_getStockpileContents(lua_State *state) return 1; } +static int buildings_getCageOccupants(lua_State *state) +{ + std::vector units; + Buildings::getCageOccupants(Lua::CheckDFObject(state, 1), units); + Lua::PushVector(state, units); + return 1; +} + static const luaL_Reg dfhack_buildings_funcs[] = { { "findAtTile", buildings_findAtTile }, { "findCivzonesAt", buildings_findCivzonesAt }, { "getCorrectSize", buildings_getCorrectSize }, { "setSize", &Lua::CallWithCatchWrapper }, - { "getStockpileContents", buildings_getStockpileContents}, - { "findPenPitAt", buildings_findPenPitAt}, + { "getStockpileContents", buildings_getStockpileContents }, + { "findPenPitAt", buildings_findPenPitAt }, + { "getCageOccupants", &Lua::CallWithCatchWrapper }, { NULL, NULL } }; diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 16df7b212..4519b8164 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -43,10 +43,12 @@ distribution. namespace df { - struct job_item; - struct item; - struct building_extents; + struct building_cagest; struct building_civzonest; + struct building_extents; + struct item; + struct job_item; + struct unit; } namespace DFHack @@ -188,6 +190,11 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector &units); } } diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 55f0869a4..759e3acd4 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -54,6 +54,7 @@ using namespace DFHack; #include "df/building_bars_floorst.h" #include "df/building_bars_verticalst.h" #include "df/building_bridgest.h" +#include "df/building_cagest.h" #include "df/building_civzonest.h" #include "df/building_coffinst.h" #include "df/building_def.h" @@ -72,7 +73,9 @@ using namespace DFHack; #include "df/buildings_other_id.h" #include "df/d_init.h" #include "df/general_ref_building_holderst.h" +#include "df/general_ref_contains_unitst.h" #include "df/item.h" +#include "df/item_cagest.h" #include "df/job.h" #include "df/job_item.h" #include "df/map_block.h" @@ -1171,6 +1174,19 @@ bool Buildings::deconstruct(df::building *bld) return true; } +bool Buildings::markedForRemoval(df::building *bld) +{ + CHECK_NULL_POINTER(bld); + for (df::job *job : bld->jobs) + { + if (job && job->job_type == df::job_type::DestroyBuilding) + { + return true; + } + } + return false; +} + static unordered_map corner1; static unordered_map corner2; @@ -1350,3 +1366,29 @@ StockpileIterator& StockpileIterator::operator++() { return *this; } +bool Buildings::getCageOccupants(df::building_cagest *cage, vector &units) +{ + CHECK_NULL_POINTER(cage); + if (!world) + return false; + if (cage->contained_items.empty()) + return false; + + auto *cage_item = virtual_cast(cage->contained_items[0]->item); + if (!cage_item) + return false; + + units.clear(); + for (df::general_ref *gref : cage_item->general_refs) + { + auto ref = virtual_cast(gref); + if (ref) + { + df::unit *unit = df::unit::find(ref->unit_id); + if (unit) + units.push_back(unit); + } + } + + return true; +} diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index a9ef091c7..1336e3b81 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -80,6 +80,7 @@ #include "tweaks/adamantine-cloth-wear.h" #include "tweaks/advmode-contained.h" #include "tweaks/block-labors.h" +#include "tweaks/cage-butcher.h" #include "tweaks/civ-agreement-ui.h" #include "tweaks/condition-material.h" #include "tweaks/craft-age-wear.h" @@ -117,6 +118,8 @@ REQUIRE_GLOBAL(enabler); REQUIRE_GLOBAL(ui); REQUIRE_GLOBAL(ui_area_map_width); REQUIRE_GLOBAL(ui_build_selector); +REQUIRE_GLOBAL(ui_building_in_assign); +REQUIRE_GLOBAL(ui_building_in_resize); REQUIRE_GLOBAL(ui_building_item_cursor); REQUIRE_GLOBAL(ui_menu_width); REQUIRE_GLOBAL(ui_look_cursor); @@ -183,6 +186,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector main.mode != df::ui_sidebar_mode::QueryBuilding) + return nullptr; + + auto cage = virtual_cast(Gui::getAnyBuilding(this)); + if (!cage) + return nullptr; + if (cage->getBuildStage() < cage->getMaxBuildStage()) + return nullptr; + if (cage->flags.bits.justice) + return nullptr; + if (Buildings::markedForRemoval(cage)) + return nullptr; + + return cage; + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + using namespace df::enums::interface_key; + INTERPOSE_NEXT(render)(); + + auto cage = get_cage(); + if (!cage) + return; + + std::vector units; + if (!Buildings::getCageOccupants(cage, units)) + return; + + auto dims = Gui::getDwarfmodeViewDims(); + for (int y = 4, i = (*ui_building_item_cursor/11)*11; + y <= 14 && i < units.size(); + ++y, ++i) + { + df::unit *unit = vector_get(units, i); + if (unit && unit->flags2.bits.slaughter) + { + int x = dims.menu_x2 - 2; + OutputString(COLOR_LIGHTMAGENTA, x, y, "Bu"); + } + } + + int x = dims.menu_x1 + 1, y = dims.y2; + OutputHotkeyString(x, y, "Butcher ", CUSTOM_B, false, 0, COLOR_WHITE, COLOR_LIGHTRED); + OutputHotkeyString(x, y, "all", CUSTOM_SHIFT_B, false, 0, COLOR_WHITE, COLOR_LIGHTRED); + } + DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) + { + using namespace df::enums::interface_key; + auto cage = get_cage(); + if (cage) + { + std::vector units; + if (Buildings::getCageOccupants(cage, units)) + { + df::unit *unit = vector_get(units, *ui_building_item_cursor); + if (unit) + { + if (input->count(CUSTOM_B)) + { + unit->flags2.bits.slaughter = !unit->flags2.bits.slaughter; + } + } + if (input->count(CUSTOM_SHIFT_B)) + { + bool state = unit ? !unit->flags2.bits.slaughter : true; + for (auto u : units) + u->flags2.bits.slaughter = state; + } + } + } + INTERPOSE_NEXT(feed)(input); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(cage_butcher_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(cage_butcher_hook, render);