2012-05-15 04:03:29 -06:00
// forceequip plugin
// Moves local items from the ground into a unit's inventory
# include <iostream>
# include <iomanip>
# include <sstream>
# include <climits>
# include <vector>
# include <string>
# include <algorithm>
# include <set>
using namespace std ;
# include "Core.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"
# include "df/world.h"
# include "df/general_ref.h"
# include "df/unit.h"
# include "df/body_part_raw.h"
# include "MiscUtils.h"
# include "df/unit_inventory_item.h"
2012-05-30 07:02:53 -06:00
# include "df/body_part_raw_flags.h"
2012-05-15 04:03:29 -06:00
# include "df/creature_raw.h"
# include "df/caste_raw.h"
# include "df/body_detail_plan.h"
# include "df/body_template.h"
# include "df/body_part_template.h"
# include "df/unit_soul.h"
# include "df/unit_skill.h"
# include "df/general_ref.h"
# include "df/caste_raw.h"
# include "DFHack.h"
using namespace DFHack ;
using namespace df : : enums ;
using MapExtras : : Block ;
using MapExtras : : MapCache ;
2014-12-06 16:47:35 -07:00
DFHACK_PLUGIN ( " forceequip " ) ;
REQUIRE_GLOBAL ( world ) ;
2012-05-15 04:03:29 -06:00
2012-05-20 11:57:45 -06:00
const int const_GloveRightHandedness = 1 ;
const int const_GloveLeftHandedness = 2 ;
2012-05-15 04:03:29 -06:00
command_result df_forceequip ( color_ostream & out , vector < string > & parameters ) ;
const string forceequip_help =
" ForceEquip moves local items into a unit's inventory. It is typically \n "
2012-11-16 14:33:36 -07:00
" used to equip specific clothing/armor items onto a dwarf, but can also \n "
" be used to put armor onto a war animal or to add unusual items (such \n "
" as crowns) to any unit. \n "
" This plugin can process multiple items in a single call, but will only \n "
" work with a single unit (the first one it finds under the cursor). \n "
" In order to minimize confusion, it is recommended that you use \n "
" forceequip only when you have a unit standing alone atop a pile of \n "
" gear that you would like it to wear. Items which are stored in bins \n "
" or other containers (e.g. chests, armor racks) may also work, but \n "
" piling items on the floor (via a garbage dump activity zone, of the \n "
" DFHack autodump command) is the most reliable way to do it. \n "
" The plugin will ignore any items that are forbidden. Hence, you \n "
" can setup a large pile of surplus gear, walk a unit onto it (or \n "
" pasture an animal on it), unforbid a few items and run forceequip. \n "
" The (forbidden) majority of your gear will remain in-place, ready \n "
" for the next passerby. "
2012-05-15 04:03:29 -06:00
" \n "
2012-11-16 14:33:36 -07:00
" As mentioned above, this plugin can be used to equip items onto \n "
" units (such as animals) which cannot normally equip gear. There's \n "
" an important caveat here - such creatures will automatically drop \n "
" inappropriate gear almost immediately (within 10 game ticks). \n "
" If you want them to retain their equipment, you must forbid it \n "
" AFTER using forceequip to get it into their inventory. \n "
" This technique can also be used to clothe dwarven infants, but \n "
" only if you're able to separate them from their mothers. \n "
" \n "
" By default, the forceequip plugin will attempt to avoid \n "
" conflicts and outright cheating. For instance, it will skip \n "
" any item which is flagged for use in a job, and will not \n "
" equip more than one piece of clothing/armor onto any given \n "
" body part. These restrictions can be overridden via command \n "
" switches (see examples below) but doing so puts you at greater \n "
" risk of unexpected consequences. For instance, a dwarf who \n "
" is wearing three breastplates will not be able to move very \n "
" quickly. \n "
" \n "
" Items equipped by this plugin DO NOT become owned by the \n "
" recipient. Adult dwarves are free to adjust their own \n "
" wardrobe, and may promptly decide to doff your gear in \n "
" favour of their owned items. Animals, as described above, \n "
" will tend to discard ALL clothing immediately unless it is \n "
" manually forbidden. Armor items seem to be an exception; \n "
" an animal will tend to retain an equipped suit of mail \n "
" even if you neglect to Forbid it. \n "
" \n "
" Please note that armored animals are quite vulnerable to ranged \n "
" attacks. Unlike dwarves, animals cannot block, dodge, or deflect \n "
" arrows, and they are slowed by the weight of their armor. \n "
" \n "
" This plugin currently does not support weapons. \n "
" \n "
" Options: \n "
" here, h - process the unit and item(s) under the cursor. \n "
" - This option is enabled by default since the plugin \n "
" - does not currently support remote equpping. \n "
2012-05-15 04:03:29 -06:00
" ignore, i - bypasses the usual item eligibility checks (such as \n "
2012-11-16 14:33:36 -07:00
" - \" Never equip gear belonging to another dwarf \" and \n "
" - \" Nobody is allowed to equip a Hive \" .) \n "
" multi, m - bypasses the 1-item-per-bodypart limit, allowing \n "
" - the unit to receive an unlimited amount of gear. \n "
" - Can be used legitimately (e.g. mitten + gauntlet) \n "
" - or for cheating (e.g. twelve breastplates). \n "
" m2, m3, m4 - alters the 1-item-per-bodypart limit, allowing \n "
" - each part to receive 2, 3, or 4 pieces of gear. \n "
" selected, s - rather than processing all items piled at a unit's \n "
" - feet, process only the one item currently selected. \n "
" bodypart, bp - must be followed by a bodypart code (e.g. LH). \n "
" - Instructs the plugin to equip all available items \n "
" - onto this body part only. Typically used in \n "
" - conjunction with the f switch (to over-armor \n "
" - a particular bodypart) or the i switch (to equip \n "
" - an unusual item onto a specific slot). \n "
2012-05-15 04:03:29 -06:00
" verbose, v - provides detailed narration and error messages. \n "
2012-11-16 14:33:36 -07:00
" - Can be helpful in resolving failures; not needed \n "
" - for casual use. \n "
" \n "
2012-05-15 04:03:29 -06:00
" Examples: \n "
2012-11-16 14:33:36 -07:00
" forceequip \n "
" attempts to equip all of the items under the cursor onto the unit \n "
" under the cursor. Uses only clothing/armor items; ignores all \n "
" other types. Equips a maximum of 1 item onto each bodypart, \n "
" and equips only \" appropriate \" items in each slot (e.g. glove \n "
" --> hand). Bypasses any item which might cause a conflict, \n "
" such as a Boot belonging to a different dwarf. \n "
" forceequip bp LH \n "
" attempts to equip all local items onto the left hand of the local \n "
" unit. If the hand is already equipped then nothing will happen, \n "
" and if it is not equipped then only one appropriate item (e.g. \n "
" a single mitten or gauntlet) will be equipped. This command can \n "
" be useful if you don't want to selectively forbid individual items \n "
" and simply want the unit to equip, say, a Helmet while leaving \n "
" the rest of the pile alone. \n "
" forceequip m bp LH \n "
" as above, but will equip ALL appropriate items onto the unit's \n "
" left hand. After running this command, it might end up wearing \n "
" a dozen left-handed mittens. Use with caution, and remember \n "
" that dwarves will tend to drop supernumary items ASAP. \n "
" forceequip m \n "
" as above, but will equip ALL appropriate items onto any \n "
" appropriate bodypart. Tends to put several boots onto the right \n "
" foot while leaving the left foot bare. \n "
" forceequip m2 \n "
" as above, but will equip up to two appropriate items onto each \n "
" bodypart. Helps to balance footwear, but doesn't ensure proper \n "
" placement (e.g. left foot gets two socks, right foot gets two \n "
" shoes). For best results, use \" selected bp LH \" and \n "
" \" selected bp RH \" instead. \n "
" forceequip i \n "
" performs the standard \" equip appropriate items onto appropriate \n "
" bodyparts \" logic, but also includes items that would normally \n "
" be considered ineligible (such as a sock which is owned by \n "
" a different dwarf). \n "
" forceequip bp NECK \n "
" attempts to equip any appropriate gear onto the Neck of the \n "
" local unit. Since the plugin believes that no items are actually \n "
" appropriate for the Neck slot, this command does nothing. \n "
" forceequip i bp NECK \n "
" attempts to equip items from the local pile onto the Neck \n "
" of the local unit. Ignores appropriateness restrictions. \n "
" If there's a millstone or an albatross carcass sitting on \n "
" the same square as the targeted unit, then there's a good \n "
" chance that it will end up around his neck. For precise \n "
" control, remember that you can selectively forbid some of \n "
" the items that are piled on the ground. \n "
" forceequip i m bp NECK \n "
" as above, but equips an unlimited number of items onto the \n "
" targeted bodypart. Effectively, all unforbidden items \n "
" (including helms, millstones, boulders, etc) will be \n "
" moved from the local pile and placed in the dwarf's \n "
" inventory (specifically, on his neck). When used with \n "
" a large pile of goods, this will leave the dwarf heavily \n "
" encumbered and very slow to move. \n "
" forceequip s \n "
" requires that a single item be selected using the k menu. \n "
" This item must occupy the same square as the target unit, \n "
" and must be unforbidden. Attempts to equip this single \n "
" item onto an appropriate slot in the unit's inventory. \n "
" Can serve as a quicker alternative to the selective- \n "
" unforbidding approach described above. \n "
" forceequip s m i bp HD \n "
" equips the selected item onto the unit's head. Ignores \n "
" all possible restrictions and conflicts. If you know \n "
" exactly what you want to equip, and exactly where you \n "
" want it to go, then this is the most straightforward \n "
" and reliable option. \n "
" forceequip v bp QQQ \n "
" guaranteed to fail (and accomplish nothing) because \n "
" there are no bodyparts called QQQ. However, since the \n "
" verbose switch is used, the resulting error messages \n "
" will list every bodypart that the unit DOES possess. \n "
" May be useful if you're unfamiliar with the BP codes \n "
" used by Dwarf Fortress, or if you're experimenting \n "
" with an exotic creature. \n "
" \n "
;
2012-05-15 04:03:29 -06:00
DFhackCExport command_result plugin_init ( color_ostream & out , vector < PluginCommand > & commands )
{
commands . push_back ( PluginCommand (
2015-11-09 20:37:45 -07:00
" forceequip " , " Move items from the ground into a unit's inventory " ,
2012-05-15 04:03:29 -06:00
df_forceequip , false ,
forceequip_help . c_str ( )
) ) ;
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( color_ostream & out )
{
return CR_OK ;
}
2012-05-20 11:57:45 -06:00
static bool moveToInventory ( MapExtras : : MapCache & mc , 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 ) ;
2012-11-16 14:33:36 -07:00
if ( ignoreRestrictions )
2012-05-20 11:57:45 -06:00
{
// If the ignoreRestrictions cmdline switch was specified, then skip all of the normal preventative rules
if ( verbose ) { Core : : 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 ) ; }
return false ;
}
else if ( item - > getType ( ) ! = df : : enums : : item_type : : GLOVES & &
2012-11-16 14:33:36 -07:00
item - > getType ( ) ! = df : : enums : : item_type : : HELM & &
item - > getType ( ) ! = df : : enums : : item_type : : ARMOR & &
2012-05-20 11:57:45 -06:00
item - > getType ( ) ! = df : : enums : : item_type : : PANTS & &
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 ) ; }
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 ) ; }
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 ) ; }
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 ) ; }
return false ;
}
// ASSERT: anti-requisite conditions have been satisfied (or disregarded)
// Step 2: Try to find a bodypart which is eligible to receive equipment AND which is appropriate for the specified item
df : : body_part_raw * confirmedBodyPart = NULL ;
2018-04-06 00:18:15 -06:00
size_t bpIndex ;
2012-05-20 11:57:45 -06:00
for ( bpIndex = 0 ; bpIndex < unit - > body . body_plan - > body_parts . size ( ) ; bpIndex + + )
{
df : : body_part_raw * currPart = unit - > body . body_plan - > body_parts [ bpIndex ] ;
// Short-circuit the search process if a BP was specified in the function call
// Note: this causes a bit of inefficient busy-looping, but the search space is tiny (<100) and we NEED to get the correct bpIndex value in order to perform inventory manipulations
if ( ! targetBodyPart )
{
// The function call did not specify any particular body part; proceed with normal iteration and evaluation of BP eligibility
}
else if ( currPart = = targetBodyPart )
{
// A specific body part was included in the function call, and we've found it; proceed with the normal BP evaluation (suitability, emptiness, etc)
}
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
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : printerr ( " Found bodypart %s; not a match; continuing search. \n " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
continue ;
}
2012-11-16 14:33:36 -07:00
else
2012-05-20 11:57:45 -06:00
{
// 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 " ) ; }
return false ;
}
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Inspecting bodypart %s. \n " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
// Inspect the current bodypart
2012-05-30 07:02:53 -06:00
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 ) ) ) )
2012-05-20 11:57:45 -06:00
{
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Hand found (%s)... " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
2012-05-30 07:02:53 -06:00
else if ( item - > getType ( ) = = df : : enums : : item_type : : HELM & & currPart - > flags . is_set ( df : : body_part_raw_flags : : HEAD ) )
2012-05-20 11:57:45 -06:00
{
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Head found (%s)... " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
2012-05-30 07:02:53 -06:00
else if ( item - > getType ( ) = = df : : enums : : item_type : : ARMOR & & currPart - > flags . is_set ( df : : body_part_raw_flags : : UPPERBODY ) )
2012-05-20 11:57:45 -06:00
{
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Upper body found (%s)... " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
2012-05-30 07:02:53 -06:00
else if ( item - > getType ( ) = = df : : enums : : item_type : : PANTS & & currPart - > flags . is_set ( df : : body_part_raw_flags : : LOWERBODY ) )
2012-05-20 11:57:45 -06:00
{
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Lower body found (%s)... " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
2012-05-30 07:02:53 -06:00
else if ( item - > getType ( ) = = df : : enums : : item_type : : SHOES & & currPart - > flags . is_set ( df : : body_part_raw_flags : : STANCE ) )
2012-05-20 11:57:45 -06:00
{
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Foot found (%s)... " , currPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
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.
2012-05-26 04:49:27 -06:00
if ( verbose ) { Core : : print ( " Non-standard bodypart found (%s)... " , targetBodyPart - > token . c_str ( ) ) ; }
2012-05-20 11:57:45 -06:00
}
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 " ) ; }
return false ;
}
else
{
if ( verbose ) { Core : : print ( " Skipping ineligible bodypart. \n " ) ; }
// This body part is not eligible for the equipment in question; skip it
continue ;
}
// ASSERT: The current body part is able to support the specified equipment (or the test has been overridden). Check whether it is currently empty/available.
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... " ) ; }
confirmedBodyPart = currPart ;
break ;
}
else
{
confirmedBodyPart = currPart ; // Assume that the bodypart is valid; we'll invalidate it if we detect too many collisions while looping
int collisions = 0 ;
2018-04-06 00:18:15 -06:00
for ( df : : unit_inventory_item * currInvItem : unit - > inventory )
2012-05-20 11:57:45 -06:00
{
2018-04-06 00:18:15 -06:00
if ( currInvItem - > body_part_id = = int32_t ( bpIndex ) )
2012-05-20 11:57:45 -06:00
{
// Collision detected; have we reached the limit?
if ( + + collisions > = multiEquipLimit )
{
2012-11-16 14:33:36 -07:00
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 ) ; }
2012-05-20 11:57:45 -06:00
confirmedBodyPart = NULL ;
break ;
}
}
}
2012-11-16 14:33:36 -07:00
if ( confirmedBodyPart )
2012-05-20 11:57:45 -06:00
{
// Match found; no need to examine any other BPs
if ( verbose ) { Core : : print ( " eligibility confirmed... " ) ; }
break ;
}
else if ( ! targetBodyPart )
{
// This body part is not eligible to receive the specified equipment; return to the loop and check the next BP
continue ;
}
else
{
// A specific body part was designated in the function call, but it was found to be ineligible.
// Don't return to the BP loop; just fall-through to the failure-reporting code a few lines below.
break ;
}
}
}
if ( ! confirmedBodyPart ) {
// No matching body parts found; report failure
if ( verbose ) { Core : : printerr ( " \n The 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 ( verbose ) { Core : : printerr ( " \n Equipping 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 " ) ; }
return true ;
}
2012-05-15 04:03:29 -06:00
command_result df_forceequip ( color_ostream & out , vector < string > & parameters )
{
2012-11-16 14:33:36 -07:00
// The "here" option is hardcoded to true, because the plugin currently doesn't support
// equip-at-a-distance (e.g. grab items within 10 squares of the targeted unit)
2012-05-15 04:03:29 -06:00
bool here = true ;
2018-04-06 00:18:15 -06:00
( void ) here ;
2012-11-16 14:33:36 -07:00
// For balance (anti-cheating) reasons, the plugin applies a limit on the number of
// item that can be equipped on any bodypart. This limit defaults to 1 but can be
// overridden with cmdline switches.
2012-05-15 04:03:29 -06:00
int multiEquipLimit = 1 ;
2012-11-16 14:33:36 -07:00
// The plugin applies several pre-checks in order to reduce the risk of conflict
// and unintended side-effects. Most of these checks can be disabled via cmdline
bool ignore = false ;
// By default, the plugin uses all gear piled on the selected square. Optionally,
// it can target only a single item (selected on the k menu) instead
bool selected = false ;
// Most of the plugin's text output is suppressed by default. It can be enabled
// to provide insight into errors, and/or for debugging purposes.
bool verbose = false ;
// By default, the plugin will mate each item to an appropriate bodypart. This
// behaviour can be skipped if the user specifies a particular BP in the cmdline input.
2012-05-15 04:03:29 -06:00
std : : string targetBodyPartCode ;
2012-11-16 14:33:36 -07:00
// Parse the input
2012-05-15 04:03:29 -06:00
for ( size_t i = 0 ; i < parameters . size ( ) ; i + + )
{
string & p = parameters [ i ] ;
2012-11-16 14:33:36 -07:00
2012-05-15 04:03:29 -06:00
if ( p = = " help " | | p = = " ? " | | p = = " h " | | p = = " /? " | | p = = " info " | | p = = " man " )
{
out < < forceequip_help < < endl ;
return CR_OK ;
}
else if ( p = = " here " | | p = = " h " )
{
here = true ;
}
2012-11-16 14:33:36 -07:00
else if ( p = = " ignore " | | p = = " i " )
{
ignore = true ;
}
2012-05-15 04:03:29 -06:00
else if ( p = = " multi " | | p = = " m " )
{
2012-11-16 14:33:36 -07:00
multiEquipLimit = INT_MAX ;
}
else if ( p = = " m2 " )
{
multiEquipLimit = 2 ;
}
else if ( p = = " m3 " )
{
multiEquipLimit = 3 ;
}
else if ( p = = " m4 " )
{
multiEquipLimit = 4 ;
}
else if ( p = = " selected " | | p = = " s " )
{
selected = true ;
}
else if ( p = = " verbose " | | p = = " v " )
{
verbose = true ;
}
2012-05-15 04:03:29 -06:00
else if ( p = = " bodypart " | | p = = " bp " )
{
// 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 " ) ;
return CR_FAILURE ;
}
targetBodyPartCode = parameters [ i + 1 ] ;
i + + ;
}
else
{
out < < p < < " : Unknown command! Type \" forceequip help \" for assistance. " < < endl ;
return CR_FAILURE ;
}
}
2012-11-16 14:33:36 -07:00
// Ensure that the map information is available (e.g. a game is actually in-progress)
2012-05-15 04:03:29 -06:00
if ( ! Maps : : IsValid ( ) )
{
out . printerr ( " Map is not available! \n " ) ;
return CR_FAILURE ;
}
2012-11-16 14:33:36 -07:00
// Lookup the cursor position
int cx , cy , cz ;
DFCoord pos_cursor ;
// needs a cursor
if ( ! Gui : : getCursorCoords ( cx , cy , cz ) )
{
out . printerr ( " Cursor position not found. Please enable the cursor. \n " ) ;
return CR_FAILURE ;
}
pos_cursor = DFCoord ( cx , cy , cz ) ;
// Iterate over all units, process the first one whose pos == pos_cursor
2018-04-06 00:18:15 -06:00
df : : unit * targetUnit = nullptr ;
2012-11-16 14:33:36 -07:00
size_t numUnits = world - > units . all . size ( ) ;
for ( size_t i = 0 ; i < numUnits ; i + + )
{
targetUnit = world - > units . all [ i ] ; // tentatively assume that we have a match; then verify
DFCoord pos_unit ( targetUnit - > pos . x , targetUnit - > pos . y , targetUnit - > pos . z ) ;
if ( pos_unit = = pos_cursor )
break ;
2018-04-06 00:18:15 -06:00
targetUnit = nullptr ;
}
if ( ! targetUnit )
{
out . printerr ( " No unit found at cursor! \n " ) ;
return CR_FAILURE ;
2012-11-16 14:33:36 -07:00
}
// Assert: unit found.
// If a specific bodypart was included in the command arguments, then search for it now
df : : body_part_raw * targetBodyPart = NULL ;
if ( targetBodyPartCode . size ( ) > 0 ) {
2018-04-06 00:18:15 -06:00
for ( size_t bpIndex = 0 ; bpIndex < targetUnit - > body . body_plan - > body_parts . size ( ) ; bpIndex + + )
2012-11-16 14:33:36 -07:00
{
// Tentatively assume that the part is a match
targetBodyPart = targetUnit - > body . body_plan - > body_parts . at ( bpIndex ) ;
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 ( ) ) ; }
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 ( ) ) ; }
targetBodyPart = NULL ;
}
}
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 ( ) ) ;
return CR_FAILURE ;
}
}
// Search for item(s)
MapCache mc ;
// iterate over all items, process those where pos == pos_cursor
int itemsEquipped = 0 ;
int itemsFound = 0 ;
int numItems = world - > items . all . size ( ) ; // Normally, we iterate through EVERY ITEM in the world. This is expensive, but currently necessary.
if ( selected ) { numItems = 1 ; } // If the user wants to process only the selected item, then the loop is trivialized (only one pass is needed).
for ( int i = 0 ; i < numItems ; i + + )
{
df : : item * currentItem ;
// Search behaviour depends on whether the operation is driven by cursor location or UI selection
if ( selected )
{
// The "search" is trivial - the selection must always cover either one or zero items
currentItem = Gui : : getSelectedItem ( out ) ;
if ( ! currentItem ) { return CR_FAILURE ; }
}
else
{
// Lookup the current item in the world-space
currentItem = world - > items . all [ i ] ;
// Test the item's position
DFCoord pos_item ( currentItem - > pos . x , currentItem - > pos . y , currentItem - > pos . z ) ;
if ( pos_item ! = pos_cursor )
{
// The item is in the wrong place; skip it
// Note: we do not emit any notification, even with the "verbose" switch, because the search space is enormous and we'd invariably flood the UI with useless text
continue ;
}
// Bypass any forbidden items
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 " ) ; }
}
}
// Test the item; check whether we have any grounds to disqualify/reject it
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 " ) ; }
}
else
{
itemsFound + + ; // Track the number of items found under the cursor (for feedback purposes)
if ( moveToInventory ( mc , currentItem , targetUnit , targetBodyPart , ignore , multiEquipLimit , verbose ) )
{
// // 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());
itemsEquipped + + ; // Track the number of items successfully processed (for feedback purposes)
}
}
}
if ( itemsFound = = 0 ) {
out . printerr ( " 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 ) ; }
// 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".
// The slight delay in recalculation is probably not worth worrying about.
// Work complete; report success
return CR_OK ;
2012-05-15 04:03:29 -06:00
}