From 015f760d7f18fdd8c0b0e72a2161371179f7e37f Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 4 Oct 2024 10:32:15 -0500 Subject: [PATCH 1/4] reenable forceequip --- plugins/CMakeLists.txt | 2 +- plugins/forceequip.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index f0b35ea9ef..e49f505b23 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -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) diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 4c09fef701..771828d41b 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -237,7 +237,7 @@ static bool moveToInventory(MapExtras::MapCache &mc, df::item *item, df::unit *u 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"); } return false; From 08b3a5bb7cfa6d10eee79022af31caf4088c992c Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 4 Oct 2024 11:38:15 -0500 Subject: [PATCH 2/4] `forceequip` : cleanup remove `using namespace std` remove references to MapCache and related --- plugins/forceequip.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 771828d41b..41f9cb0950 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -8,7 +8,6 @@ #include #include #include -using namespace std; #include "Core.h" #include "Console.h" @@ -18,7 +17,6 @@ using namespace std; #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" @@ -42,8 +40,9 @@ 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); @@ -68,7 +67,7 @@ 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); @@ -401,8 +400,6 @@ command_result df_forceequip(color_ostream &out, vector & parameters) } // Search for item(s) - MapCache mc; - // iterate over all items, process those where pos == pos_cursor int itemsEquipped = 0; int itemsFound = 0; @@ -449,7 +446,7 @@ command_result df_forceequip(color_ostream &out, vector & 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(); @@ -468,9 +465,6 @@ command_result df_forceequip(color_ostream &out, vector & 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". From 383ffe20687a8e4c66e4fc4023d2c67cf70b058e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 4 Oct 2024 11:45:21 -0500 Subject: [PATCH 3/4] Update forceequip.cpp switched to using debug facility instead of `Core::print`/`Core::printerr` may need further tweaking --- plugins/forceequip.cpp | 51 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index 41f9cb0950..baf2bfc742 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -10,6 +10,7 @@ #include #include "Core.h" +#include "Debug.h" #include "Console.h" #include "Export.h" #include "PluginManager.h" @@ -47,6 +48,10 @@ 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; @@ -74,11 +79,11 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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 && @@ -88,22 +93,22 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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; } @@ -130,55 +135,55 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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; } @@ -188,7 +193,7 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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; } @@ -203,7 +208,7 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * // 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; } @@ -213,7 +218,7 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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) @@ -232,17 +237,17 @@ static bool moveToInventory(df::item *item, df::unit *unit, df::body_part_raw * 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(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; } From 7087acdc01c14962a1882813d44d0d2f23355e60 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 13 Nov 2024 22:59:43 -0600 Subject: [PATCH 4/4] forceequip: docs and cleanup update docs update remaining messaging to use debug logger --- docs/changelog.txt | 1 + docs/plugins/forceequip.rst | 2 +- plugins/forceequip.cpp | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index cda9b7c87c..6260ff890f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ Template for new versions: # Future ## New Tools +- `forceequip`: (reinstated) Forcibly move items into a unit's inventory ## New Features - `tweak`: ``realistic-melting``: change melting return for inorganic armor parts, shields, weapons, trap components and tools to stop smelters from creating metal, bring melt return for adamantine in line with other metals to ~95% of forging cost. wear reduces melt return by 10% per level diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 18661195a1..26ab378eec 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -3,7 +3,7 @@ forceequip .. dfhack-tool:: :summary: Move items into a unit's inventory. - :tags: unavailable + :tags: adventure fort animals items military units This tool is typically used to equip specific clothing/armor items onto a dwarf, but can also be used to put armor onto a war animal or to add unusual items diff --git a/plugins/forceequip.cpp b/plugins/forceequip.cpp index baf2bfc742..9a0086ae11 100644 --- a/plugins/forceequip.cpp +++ b/plugins/forceequip.cpp @@ -321,7 +321,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) // must be followed by bodypart code (e.g. NECK) if(i == parameters.size()-1 || parameters[i+1].size() == 0) { - out.printerr("The bp switch must be followed by a bodypart code!\n"); + WARN(log).print("The bp switch must be followed by a bodypart code!\n"); return CR_FAILURE; } targetBodyPartCode = parameters[i+1]; @@ -337,7 +337,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) // Ensure that the map information is available (e.g. a game is actually in-progress) if (!Maps::IsValid()) { - out.printerr("Map is not available!\n"); + WARN(log).print("Map is not available!\n"); return CR_FAILURE; } @@ -348,7 +348,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) // needs a cursor if (!Gui::getCursorCoords(cx,cy,cz)) { - out.printerr("Cursor position not found. Please enable the cursor.\n"); + WARN(log).print("Cursor position not found. Please enable the cursor.\n"); return CR_FAILURE; } pos_cursor = DFCoord(cx,cy,cz); @@ -369,7 +369,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) if (!targetUnit) { - out.printerr("No unit found at cursor!\n"); + WARN(log).print("No unit found at cursor!\n"); return CR_FAILURE; } @@ -385,13 +385,13 @@ command_result df_forceequip(color_ostream &out, vector & parameters) if (targetBodyPart->token.compare(targetBodyPartCode) == 0) { // It is indeed a match; exit the loop (while leaving the variable populated) - if (verbose) { out.print("Matching bodypart (%s) found.\n", targetBodyPart->token.c_str()); } + if (verbose) { INFO(log).print("Matching bodypart (%s) found.\n", targetBodyPart->token.c_str()); } break; } else { // Not a match; nullify the variable (it will get re-populated on the next pass through the loop) - if (verbose) { out.printerr("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->token.c_str(), targetBodyPartCode.c_str()); } + if (verbose) { WARN(log).print("Bodypart \"%s\" does not match \"%s\".\n", targetBodyPart->token.c_str(), targetBodyPartCode.c_str()); } targetBodyPart = NULL; } } @@ -399,7 +399,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) if (!targetBodyPart) { // Loop iteration is complete but no match was found. - out.printerr("The unit does not possess a bodypart of type \"%s\". Please check the spelling or choose a different unit.\n", targetBodyPartCode.c_str()); + WARN(log).print("The unit does not possess a bodypart of type \"%s\". Please check the spelling or choose a different unit.\n", targetBodyPartCode.c_str()); return CR_FAILURE; } } @@ -437,7 +437,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) else if (currentItem->flags.bits.forbid == 1) { // The item is forbidden; skip it - if (verbose) { out.printerr("Forbidden item encountered; skipping to next item.\n"); } + if (verbose) { WARN(log).print("Forbidden item encountered; skipping to next item.\n"); } } } @@ -446,7 +446,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) if (currentItem->flags.bits.in_inventory == 1) { // The item is in a unit's inventory; skip it - if (verbose) { out.printerr("Inventory item encountered; skipping to next item.\n"); } + if (verbose) { WARN(log).print("Inventory item encountered; skipping to next item.\n"); } } else { @@ -455,7 +455,7 @@ command_result df_forceequip(color_ostream &out, vector & parameters) { // // TODO TEMP EXPERIMENTAL - try to alter the item size in order to conform to its wearer // currentItem->getRace(); -// out.print("Critter size: %d| %d | Armor size: %d", world->raws.creatures.all[targetUnit->race]->caste[targetUnit->caste]->body_size_1, world->raws.creatures.all[targetUnit->race]->caste[targetUnit->caste]->body_size_2, currentItem->getTotalDimension()); +// INFO(log).print("Critter size: %d| %d | Armor size: %d", world->raws.creatures.all[targetUnit->race]->caste[targetUnit->caste]->body_size_1, world->raws.creatures.all[targetUnit->race]->caste[targetUnit->caste]->body_size_2, currentItem->getTotalDimension()); itemsEquipped++; // Track the number of items successfully processed (for feedback purposes) } @@ -463,13 +463,13 @@ command_result df_forceequip(color_ostream &out, vector & parameters) } if (itemsFound == 0) { - out.printerr("No usable items found at the cursor position. Please choose a different location and try again.\n"); + WARN(log).print("No usable items found at the cursor position. Please choose a different location and try again.\n"); return CR_OK; } - 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); } + if (itemsEquipped == 0 && !verbose) { WARN(log).print("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) { INFO(log).print("%d items equipped.\n", itemsEquipped); } // 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".