Add "tweak cage-butcher" and some extra Building module functions

* Buildings::markedForRemoval()
* Buildings::getCageOccupants()

Closes #906
develop
lethosor 2017-06-25 15:21:45 -04:00
parent db375ae83b
commit 10e13c532a
8 changed files with 189 additions and 5 deletions

@ -198,6 +198,7 @@ tweak embark-profile-name
# Misc. UI tweaks # Misc. UI tweaks
tweak block-labors # Prevents labors that can't be used from being toggled tweak block-labors # Prevents labors that can't be used from being toggled
tweak cage-butcher
tweak civ-view-agreement tweak civ-view-agreement
tweak eggs-fertile tweak eggs-fertile
tweak fps-min tweak fps-min

@ -1486,6 +1486,12 @@ General
Returns a list of items stored on the given stockpile. Returns a list of items stored on the given stockpile.
Ignores empty bins, barrels, and wheelbarrows assigned as storage and transport for that 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
~~~~~~~~~ ~~~~~~~~~
Low-level building creation functions: Low-level building creation functions:
@ -1531,6 +1537,11 @@ Low-level building creation functions:
Destroys the building, or queues a deconstruction job. Destroys the building, or queues a deconstruction job.
Returns *true* if the building was destroyed and deallocated immediately. 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 High-level
~~~~~~~~~~ ~~~~~~~~~~
More high-level functions are implemented in lua and can be loaded by More high-level functions are implemented in lua and can be loaded by

@ -275,6 +275,7 @@ Subcommands that persist until disabled or DF quits:
the contents separately from the container. This forcefully skips child the contents separately from the container. This forcefully skips child
reagents. reagents.
:block-labors: Prevents labors that can't be used from being toggled :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 :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`). :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`). :craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`).

@ -85,6 +85,7 @@ distribution.
#include "df/dfhack_material_category.h" #include "df/dfhack_material_category.h"
#include "df/job_material_category.h" #include "df/job_material_category.h"
#include "df/burrow.h" #include "df/burrow.h"
#include "df/building_cagest.h"
#include "df/building_civzonest.h" #include "df/building_civzonest.h"
#include "df/region_map_entry.h" #include "df/region_map_entry.h"
#include "df/flow_info.h" #include "df/flow_info.h"
@ -1985,6 +1986,7 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, isPenPasture), WRAPM(Buildings, isPenPasture),
WRAPM(Buildings, isPitPond), WRAPM(Buildings, isPitPond),
WRAPM(Buildings, isActive), WRAPM(Buildings, isActive),
WRAPM(Buildings, markedForRemoval),
{ NULL, NULL } { NULL, NULL }
}; };
@ -2066,13 +2068,22 @@ static int buildings_getStockpileContents(lua_State *state)
return 1; return 1;
} }
static int buildings_getCageOccupants(lua_State *state)
{
std::vector<df::unit*> units;
Buildings::getCageOccupants(Lua::CheckDFObject<df::building_cagest>(state, 1), units);
Lua::PushVector(state, units);
return 1;
}
static const luaL_Reg dfhack_buildings_funcs[] = { static const luaL_Reg dfhack_buildings_funcs[] = {
{ "findAtTile", buildings_findAtTile }, { "findAtTile", buildings_findAtTile },
{ "findCivzonesAt", buildings_findCivzonesAt }, { "findCivzonesAt", buildings_findCivzonesAt },
{ "getCorrectSize", buildings_getCorrectSize }, { "getCorrectSize", buildings_getCorrectSize },
{ "setSize", &Lua::CallWithCatchWrapper<buildings_setSize> }, { "setSize", &Lua::CallWithCatchWrapper<buildings_setSize> },
{ "getStockpileContents", buildings_getStockpileContents}, { "getStockpileContents", buildings_getStockpileContents },
{ "findPenPitAt", buildings_findPenPitAt}, { "findPenPitAt", buildings_findPenPitAt },
{ "getCageOccupants", &Lua::CallWithCatchWrapper<buildings_getCageOccupants> },
{ NULL, NULL } { NULL, NULL }
}; };

@ -43,10 +43,12 @@ distribution.
namespace df namespace df
{ {
struct job_item; struct building_cagest;
struct item;
struct building_extents;
struct building_civzonest; struct building_civzonest;
struct building_extents;
struct item;
struct job_item;
struct unit;
} }
namespace DFHack namespace DFHack
@ -188,6 +190,11 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector<df::job_i
*/ */
DFHACK_EXPORT bool deconstruct(df::building *bld); DFHACK_EXPORT bool deconstruct(df::building *bld);
/**
* Determines whether this building is marked for removal.
*/
DFHACK_EXPORT bool markedForRemoval(df::building *bld);
void updateBuildings(color_ostream& out, void* ptr); void updateBuildings(color_ostream& out, void* ptr);
void clearBuildings(color_ostream& out); void clearBuildings(color_ostream& out);
@ -249,5 +256,10 @@ DFHACK_EXPORT bool isHospital(df::building * building);
DFHACK_EXPORT bool isAnimalTraining(df::building * building); DFHACK_EXPORT bool isAnimalTraining(df::building * building);
DFHACK_EXPORT df::building* findPenPitAt(df::coord coord); DFHACK_EXPORT df::building* findPenPitAt(df::coord coord);
/**
* Returns the units currently in the given cage
*/
DFHACK_EXPORT bool getCageOccupants(df::building_cagest *cage, std::vector<df::unit*> &units);
} }
} }

@ -54,6 +54,7 @@ using namespace DFHack;
#include "df/building_bars_floorst.h" #include "df/building_bars_floorst.h"
#include "df/building_bars_verticalst.h" #include "df/building_bars_verticalst.h"
#include "df/building_bridgest.h" #include "df/building_bridgest.h"
#include "df/building_cagest.h"
#include "df/building_civzonest.h" #include "df/building_civzonest.h"
#include "df/building_coffinst.h" #include "df/building_coffinst.h"
#include "df/building_def.h" #include "df/building_def.h"
@ -72,7 +73,9 @@ using namespace DFHack;
#include "df/buildings_other_id.h" #include "df/buildings_other_id.h"
#include "df/d_init.h" #include "df/d_init.h"
#include "df/general_ref_building_holderst.h" #include "df/general_ref_building_holderst.h"
#include "df/general_ref_contains_unitst.h"
#include "df/item.h" #include "df/item.h"
#include "df/item_cagest.h"
#include "df/job.h" #include "df/job.h"
#include "df/job_item.h" #include "df/job_item.h"
#include "df/map_block.h" #include "df/map_block.h"
@ -1171,6 +1174,19 @@ bool Buildings::deconstruct(df::building *bld)
return true; 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<int32_t, df::coord> corner1; static unordered_map<int32_t, df::coord> corner1;
static unordered_map<int32_t, df::coord> corner2; static unordered_map<int32_t, df::coord> corner2;
@ -1350,3 +1366,29 @@ StockpileIterator& StockpileIterator::operator++() {
return *this; return *this;
} }
bool Buildings::getCageOccupants(df::building_cagest *cage, vector<df::unit*> &units)
{
CHECK_NULL_POINTER(cage);
if (!world)
return false;
if (cage->contained_items.empty())
return false;
auto *cage_item = virtual_cast<df::item_cagest>(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<df::general_ref_contains_unitst>(gref);
if (ref)
{
df::unit *unit = df::unit::find(ref->unit_id);
if (unit)
units.push_back(unit);
}
}
return true;
}

@ -80,6 +80,7 @@
#include "tweaks/adamantine-cloth-wear.h" #include "tweaks/adamantine-cloth-wear.h"
#include "tweaks/advmode-contained.h" #include "tweaks/advmode-contained.h"
#include "tweaks/block-labors.h" #include "tweaks/block-labors.h"
#include "tweaks/cage-butcher.h"
#include "tweaks/civ-agreement-ui.h" #include "tweaks/civ-agreement-ui.h"
#include "tweaks/condition-material.h" #include "tweaks/condition-material.h"
#include "tweaks/craft-age-wear.h" #include "tweaks/craft-age-wear.h"
@ -117,6 +118,8 @@ REQUIRE_GLOBAL(enabler);
REQUIRE_GLOBAL(ui); REQUIRE_GLOBAL(ui);
REQUIRE_GLOBAL(ui_area_map_width); REQUIRE_GLOBAL(ui_area_map_width);
REQUIRE_GLOBAL(ui_build_selector); 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_building_item_cursor);
REQUIRE_GLOBAL(ui_menu_width); REQUIRE_GLOBAL(ui_menu_width);
REQUIRE_GLOBAL(ui_look_cursor); REQUIRE_GLOBAL(ui_look_cursor);
@ -183,6 +186,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" from the container. This forcefully skips child reagents.\n" " from the container. This forcefully skips child reagents.\n"
" tweak block-labors [disable]\n" " tweak block-labors [disable]\n"
" Prevents labors that can't be used from being toggled.\n" " Prevents labors that can't be used from being toggled.\n"
" tweak cage-butcher [disable]\n"
" Adds an option to butcher units when viewing cages.\n"
" tweak civ-view-agreement\n" " tweak civ-view-agreement\n"
" Fixes overlapping text on the \"view agreement\" screen\n" " Fixes overlapping text on the \"view agreement\" screen\n"
" tweak condition-material\n" " tweak condition-material\n"
@ -250,6 +255,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
TWEAK_HOOK("block-labors", block_labors_hook, feed); TWEAK_HOOK("block-labors", block_labors_hook, feed);
TWEAK_HOOK("block-labors", block_labors_hook, render); TWEAK_HOOK("block-labors", block_labors_hook, render);
TWEAK_HOOK("cage-butcher", cage_butcher_hook, feed);
TWEAK_HOOK("cage-butcher", cage_butcher_hook, render);
TWEAK_HOOK("civ-view-agreement", civ_agreement_view_hook, render); TWEAK_HOOK("civ-view-agreement", civ_agreement_view_hook, render);
TWEAK_HOOK("condition-material", condition_material_hook, feed); TWEAK_HOOK("condition-material", condition_material_hook, feed);

@ -0,0 +1,98 @@
#include "modules/Gui.h"
#include "modules/Screen.h"
#include "df/building_cagest.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/ui_sidebar_mode.h"
#include "df/unit.h"
using namespace DFHack;
using df::global::ui;
using df::global::ui_building_in_assign;
using df::global::ui_building_in_resize;
using df::global::ui_building_item_cursor;
struct cage_butcher_hook : df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
inline df::building_cagest *get_cage()
{
if (*ui_building_in_assign || *ui_building_in_resize)
return nullptr;
if (ui->main.mode != df::ui_sidebar_mode::QueryBuilding)
return nullptr;
auto cage = virtual_cast<df::building_cagest>(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<df::unit*> 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<df::interface_key> *input))
{
using namespace df::enums::interface_key;
auto cage = get_cage();
if (cage)
{
std::vector<df::unit*> 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);