Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reenable forceequip #4979

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ if(BUILD_SUPPORTED)
#dfhack_plugin(fixveins fixveins.cpp)
dfhack_plugin(flows flows.cpp)
#dfhack_plugin(follow follow.cpp)
#dfhack_plugin(forceequip forceequip.cpp)
dfhack_plugin(forceequip forceequip.cpp)
#dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp)
dfhack_plugin(getplants getplants.cpp)
dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua)
Expand Down
69 changes: 34 additions & 35 deletions plugins/forceequip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@
#include <string>
#include <algorithm>
#include <set>
using namespace std;

#include "Core.h"
#include "Debug.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Maps.h"
#include "modules/Gui.h"
#include "modules/Items.h"
#include "modules/Materials.h"
#include "modules/MapCache.h"
#include "DataDefs.h"
#include "df/item.h"
#include "df/itemdef.h"
Expand All @@ -42,12 +41,17 @@ using namespace std;
using namespace DFHack;
using namespace df::enums;

using MapExtras::Block;
using MapExtras::MapCache;
using std::string;
using std::vector;
using std::endl;

DFHACK_PLUGIN("forceequip");
REQUIRE_GLOBAL(world);

namespace DFHack {
DBG_DECLARE(forceequip, log, DebugCategory::LINFO);
}

const int const_GloveRightHandedness = 1;
const int const_GloveLeftHandedness = 2;

Expand All @@ -68,18 +72,18 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}

static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *unit, df::body_part_raw * targetBodyPart, bool ignoreRestrictions, int multiEquipLimit, bool verbose)
static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * targetBodyPart, bool ignoreRestrictions, int multiEquipLimit, bool verbose)
{
// Step 1: Check for anti-requisite conditions
df::unit * itemOwner = Items::getOwner(item);
if (ignoreRestrictions)
{
// If the ignoreRestrictions cmdline switch was specified, then skip all of the normal preventative rules
if (verbose) { Core::print("Skipping integrity checks...\n"); }
if (verbose) { DEBUG(log).print("Skipping integrity checks...\n"); }
}
else if(!item->isClothing() && !item->isArmorNotClothing())
{
if (verbose) { Core::printerr("Item %d is not clothing or armor; it cannot be equipped. Please choose a different item (or use the Ignore option if you really want to equip an inappropriate item).\n", item->id); }
if (verbose) { WARN(log).print("Item %d is not clothing or armor; it cannot be equipped. Please choose a different item (or use the Ignore option if you really want to equip an inappropriate item).\n", item->id); }
return false;
}
else if (item->getType() != df::enums::item_type::GLOVES &&
Expand All @@ -89,22 +93,22 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
item->getType() != df::enums::item_type::SHOES &&
!targetBodyPart)
{
if (verbose) { Core::printerr("Item %d is of an unrecognized type; it cannot be equipped (because the module wouldn't know where to put it).\n", item->id); }
if (verbose) { WARN(log).print("Item %d is of an unrecognized type; it cannot be equipped (because the module wouldn't know where to put it).\n", item->id); }
return false;
}
else if (itemOwner && itemOwner->id != unit->id)
{
if (verbose) { Core::printerr("Item %d is owned by someone else. Equipping it on this unit is not recommended. Please use DFHack's Confiscate plugin, choose a different item, or use the Ignore option to proceed in spite of this warning.\n", item->id); }
if (verbose) { WARN(log).print("Item %d is owned by someone else. Equipping it on this unit is not recommended. Please use DFHack's Confiscate plugin, choose a different item, or use the Ignore option to proceed in spite of this warning.\n", item->id); }
return false;
}
else if (item->flags.bits.in_inventory)
{
if (verbose) { Core::printerr("Item %d is already in a unit's inventory. Direct inventory transfers are not recommended; please move the item to the ground first (or use the Ignore option).\n", item->id); }
if (verbose) { WARN(log).print("Item %d is already in a unit's inventory. Direct inventory transfers are not recommended; please move the item to the ground first (or use the Ignore option).\n", item->id); }
return false;
}
else if (item->flags.bits.in_job)
{
if (verbose) { Core::printerr("Item %d is reserved for use in a queued job. Equipping it is not recommended, as this might interfere with the completion of vital jobs. Use the Ignore option to ignore this warning.\n", item->id); }
if (verbose) { WARN(log).print("Item %d is reserved for use in a queued job. Equipping it is not recommended, as this might interfere with the completion of vital jobs. Use the Ignore option to ignore this warning.\n", item->id); }
return false;
}

Expand All @@ -131,55 +135,55 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
else if (bpIndex < unit->body.body_plan->body_parts.size())
{
// The current body part is not the one that was specified in the function call, but we can keep searching
if (verbose) { Core::printerr("Found bodypart %s; not a match; continuing search.\n", currPart->token.c_str()); }
if (verbose) { WARN(log).print("Found bodypart %s; not a match; continuing search.\n", currPart->token.c_str()); }
continue;
}
else
{
// The specified body part has not been found, and we've reached the end of the list. Report failure.
if (verbose) { Core::printerr("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n",targetBodyPart->token.c_str()); }
if (verbose) { WARN(log).print("The specified body part (%s) does not belong to the chosen unit. Please double-check to ensure that your spelling is correct, and that you have not chosen a dismembered bodypart.\n",targetBodyPart->token.c_str()); }
return false;
}

if (verbose) { Core::print("Inspecting bodypart %s.\n", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Inspecting bodypart %s.\n", currPart->token.c_str()); }

// Inspect the current bodypart
if (item->getType() == df::enums::item_type::GLOVES && currPart->flags.is_set(df::body_part_raw_flags::GRASP) &&
((item->getGloveHandedness() == const_GloveLeftHandedness && currPart->flags.is_set(df::body_part_raw_flags::LEFT)) ||
(item->getGloveHandedness() == const_GloveRightHandedness && currPart->flags.is_set(df::body_part_raw_flags::RIGHT))))
{
if (verbose) { Core::print("Hand found (%s)...", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Hand found (%s)...", currPart->token.c_str()); }
}
else if (item->getType() == df::enums::item_type::HELM && currPart->flags.is_set(df::body_part_raw_flags::HEAD))
{
if (verbose) { Core::print("Head found (%s)...", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Head found (%s)...", currPart->token.c_str()); }
}
else if (item->getType() == df::enums::item_type::ARMOR && currPart->flags.is_set(df::body_part_raw_flags::UPPERBODY))
{
if (verbose) { Core::print("Upper body found (%s)...", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Upper body found (%s)...", currPart->token.c_str()); }
}
else if (item->getType() == df::enums::item_type::PANTS && currPart->flags.is_set(df::body_part_raw_flags::LOWERBODY))
{
if (verbose) { Core::print("Lower body found (%s)...", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Lower body found (%s)...", currPart->token.c_str()); }
}
else if (item->getType() == df::enums::item_type::SHOES && currPart->flags.is_set(df::body_part_raw_flags::STANCE))
{
if (verbose) { Core::print("Foot found (%s)...", currPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Foot found (%s)...", currPart->token.c_str()); }
}
else if (targetBodyPart && ignoreRestrictions)
{
// The BP in question would normally be considered ineligible for equipment. But since it was deliberately specified by the user, we'll proceed anyways.
if (verbose) { Core::print("Non-standard bodypart found (%s)...", targetBodyPart->token.c_str()); }
if (verbose) { DEBUG(log).print("Non-standard bodypart found (%s)...", targetBodyPart->token.c_str()); }
}
else if (targetBodyPart)
{
// The BP in question is not eligible for equipment and the ignore flag was not specified. Report failure.
if (verbose) { Core::printerr("Non-standard bodypart found, but it is ineligible for standard equipment. Use the Ignore flag to override this warning.\n"); }
if (verbose) { WARN(log).print("Non-standard bodypart found, but it is ineligible for standard equipment. Use the Ignore flag to override this warning.\n"); }
return false;
}
else
{
if (verbose) { Core::print("Skipping ineligible bodypart.\n"); }
if (verbose) { DEBUG(log).print("Skipping ineligible bodypart.\n"); }
// This body part is not eligible for the equipment in question; skip it
continue;
}
Expand All @@ -189,7 +193,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
if (multiEquipLimit == INT_MAX)
{
// Note: this loop/check is skipped if the MultiEquip option is specified; we'll simply add the item to the bodyPart even if it's already holding a dozen gloves, shoes, and millstones (or whatever)
if (verbose) { Core::print(" inventory checking skipped..."); }
if (verbose) { DEBUG(log).print(" inventory checking skipped..."); }
confirmedBodyPart = currPart;
break;
}
Expand All @@ -204,7 +208,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
// Collision detected; have we reached the limit?
if (++collisions >= multiEquipLimit)
{
if (verbose) { Core::printerr(" but it already carries %d piece(s) of equipment. Either remove the existing equipment or use the Multi option.\n", multiEquipLimit); }
if (verbose) { WARN(log).print(" but it already carries %d piece(s) of equipment. Either remove the existing equipment or use the Multi option.\n", multiEquipLimit); }
confirmedBodyPart = NULL;
break;
}
Expand All @@ -214,7 +218,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u
if (confirmedBodyPart)
{
// Match found; no need to examine any other BPs
if (verbose) { Core::print(" eligibility confirmed..."); }
if (verbose) { DEBUG(log).print(" eligibility confirmed..."); }
break;
}
else if (!targetBodyPart)
Expand All @@ -233,17 +237,17 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u

if (!confirmedBodyPart) {
// No matching body parts found; report failure
if (verbose) { Core::printerr("\nThe item could not be equipped because the relevant body part(s) of the unit are missing or already occupied. Try again with the Multi option if you're like to over-equip a body part, or choose a different unit-item combination (e.g. stop trying to put shoes on a trout).\n" ); }
if (verbose) { WARN(log).print("\nThe item could not be equipped because the relevant body part(s) of the unit are missing or already occupied. Try again with the Multi option if you're like to over-equip a body part, or choose a different unit-item combination (e.g. stop trying to put shoes on a trout).\n" ); }
return false;
}

if (!Items::moveToInventory(mc, item, unit, df::unit_inventory_item::Worn, bpIndex))
if (!Items::moveToInventory(item, unit, df::unit_inventory_item::Worn, bpIndex))
{
if (verbose) { Core::printerr("\nEquipping failed - failed to retrieve item from its current location/container/inventory. Please move it to the ground and try again.\n"); }
if (verbose) { WARN(log).print("\nEquipping failed - failed to retrieve item from its current location/container/inventory. Please move it to the ground and try again.\n"); }
return false;
}

if (verbose) { Core::print(" Success!\n"); }
if (verbose) { DEBUG(log).print(" Success!\n"); }
return true;
}

Expand Down Expand Up @@ -401,8 +405,6 @@ command_result df_forceequip(color_ostream &out, vector <string> & parameters)
}

// Search for item(s)
MapCache mc;

// iterate over all items, process those where pos == pos_cursor
int itemsEquipped = 0;
int itemsFound = 0;
Expand Down Expand Up @@ -449,7 +451,7 @@ command_result df_forceequip(color_ostream &out, vector <string> & parameters)
else
{
itemsFound ++; // Track the number of items found under the cursor (for feedback purposes)
if (moveToInventory(mc, currentItem, targetUnit, targetBodyPart, ignore, multiEquipLimit, verbose))
if (moveToInventory(currentItem, targetUnit, targetBodyPart, ignore, multiEquipLimit, verbose))
{
// // TODO TEMP EXPERIMENTAL - try to alter the item size in order to conform to its wearer
// currentItem->getRace();
Expand All @@ -468,9 +470,6 @@ command_result df_forceequip(color_ostream &out, vector <string> & parameters)

if (itemsEquipped == 0 && !verbose) { out.printerr("Some items were found but no equipment changes could be made. Use the /verbose switch to display the reasons for failure.\n"); }
if (itemsEquipped > 0) { out.print("%d items equipped.\n", itemsEquipped); }
// At this point, some changes may have been made (such as detaching items from their original position), regardless of whether any equipment changes succeeded.
// Therefore, we must update the map.
mc.WriteAll();

// Note: we might expect to recalculate the unit's weight at this point, in order to account for the
// added items. In fact, this recalculation occurs automatically during each dwarf's "turn".
Expand Down