2011-08-03 20:04:46 -06:00
// Quick Dumper : Moves items marked as "dump" to cursor
// FIXME: local item cache in map blocks needs to be fixed after teleporting items
# include <iostream>
# include <iomanip>
# include <sstream>
# include <climits>
# include <vector>
2012-01-15 13:54:14 -07:00
# include <string>
# include <algorithm>
2011-08-03 20:04:46 -06:00
# include <set>
using namespace std ;
2011-12-31 04:48:42 -07:00
# include "Core.h"
2012-01-15 13:54:14 -07:00
# 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"
2011-08-03 20:04:46 -06:00
2012-01-15 13:54:14 -07:00
# include "DataDefs.h"
# include "df/item.h"
# include "df/world.h"
# include "df/general_ref.h"
2012-01-14 10:13:30 -07:00
2011-08-03 20:04:46 -06:00
using namespace DFHack ;
2012-01-21 17:31:15 -07:00
using namespace df : : enums ;
2011-08-03 20:04:46 -06:00
using MapExtras : : Block ;
using MapExtras : : MapCache ;
2012-01-16 21:12:58 -07:00
using df : : global : : world ;
2011-08-03 20:04:46 -06:00
2012-02-21 10:19:17 -07:00
DFHACK_PLUGIN ( " autodump " ) ;
2012-03-10 04:55:42 -07:00
command_result df_autodump ( color_ostream & out , vector < string > & parameters ) ;
command_result df_autodump_destroy_here ( color_ostream & out , vector < string > & parameters ) ;
command_result df_autodump_destroy_item ( color_ostream & out , vector < string > & parameters ) ;
2011-08-03 20:04:46 -06:00
2012-03-10 04:55:42 -07:00
DFhackCExport command_result plugin_init ( color_ostream & out , vector < PluginCommand > & commands )
2011-08-03 20:04:46 -06:00
{
2012-01-28 05:03:56 -07:00
commands . push_back ( PluginCommand (
" autodump " , " Teleport items marked for dumping to the cursor. " ,
df_autodump , false ,
" This utility lets you quickly move all items designated to be dumped. \n "
" Items are instantly moved to the cursor position, the dump flag is unset, \n "
" and the forbid flag is set, as if it had been dumped normally. \n "
" Be aware that any active dump item tasks still point at the item. \n "
" Options: \n "
" destroy - instead of dumping, destroy the items instantly. \n "
" destroy-here - only affect the tile under cursor. \n "
2012-01-28 06:40:09 -07:00
" visible - only process items that are not hidden. \n "
" hidden - only process hidden items. \n "
" forbidden - only process forbidden items (default: only unforbidden). \n "
2012-01-28 05:03:56 -07:00
) ) ;
2012-01-14 10:13:30 -07:00
commands . push_back ( PluginCommand (
" autodump-destroy-here " , " Destroy items marked for dumping under cursor. " ,
2012-03-03 06:38:24 -07:00
df_autodump_destroy_here , Gui : : cursor_hotkey ,
2012-01-14 10:13:30 -07:00
" Identical to autodump destroy-here, but intended for use as keybinding. \n "
) ) ;
commands . push_back ( PluginCommand (
" autodump-destroy-item " , " Destroy the selected item. " ,
2012-03-03 06:38:24 -07:00
df_autodump_destroy_item , Gui : : any_item_hotkey ,
2012-01-14 10:13:30 -07:00
" Destroy the selected item. The item may be selected \n "
" in the 'k' list, or inside a container. If called \n "
" again before the game is resumed, cancels destroy. \n "
) ) ;
2011-08-03 20:04:46 -06:00
return CR_OK ;
}
2012-03-10 04:55:42 -07:00
DFhackCExport command_result plugin_shutdown ( color_ostream & out )
2011-08-03 20:04:46 -06:00
{
return CR_OK ;
}
2012-01-31 09:55:38 -07:00
typedef map < DFCoord , uint32_t > coordmap ;
2011-08-03 20:04:46 -06:00
2012-03-10 04:55:42 -07:00
static command_result autodump_main ( color_ostream & out , vector < string > & parameters )
2011-08-03 20:04:46 -06:00
{
// Command line options
bool destroy = false ;
2012-01-14 10:13:30 -07:00
bool here = false ;
2012-01-28 06:40:09 -07:00
bool need_visible = false ;
bool need_hidden = false ;
bool need_forbidden = false ;
2012-01-31 09:55:38 -07:00
for ( size_t i = 0 ; i < parameters . size ( ) ; i + + )
2011-08-03 20:04:46 -06:00
{
2012-01-29 07:17:06 -07:00
string & p = parameters [ i ] ;
2011-08-03 20:04:46 -06:00
if ( p = = " destroy " )
destroy = true ;
2012-01-14 10:13:30 -07:00
else if ( p = = " destroy-here " )
destroy = here = true ;
2012-01-28 06:40:09 -07:00
else if ( p = = " visible " )
need_visible = true ;
else if ( p = = " hidden " )
need_hidden = true ;
else if ( p = = " forbidden " )
need_forbidden = true ;
2012-01-28 05:03:56 -07:00
else
return CR_WRONG_USAGE ;
2011-08-03 20:04:46 -06:00
}
2012-01-15 13:59:47 -07:00
2012-01-28 06:40:09 -07:00
if ( need_visible & & need_hidden )
{
2012-03-10 04:55:42 -07:00
out . printerr ( " An item can't be both hidden and visible. \n " ) ;
2012-01-28 06:40:09 -07:00
return CR_WRONG_USAGE ;
}
2012-03-10 04:55:42 -07:00
//DFHack::VersionInfo *mem = Core::getInstance().vinfo;
2012-01-19 20:44:17 -07:00
if ( ! Maps : : IsValid ( ) )
2011-08-03 20:04:46 -06:00
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Map is not available! \n " ) ;
2011-08-03 20:04:46 -06:00
return CR_FAILURE ;
}
2012-01-31 09:55:38 -07:00
size_t numItems = world - > items . all . size ( ) ;
2012-01-19 20:44:17 -07:00
MapCache MC ;
2011-08-03 20:04:46 -06:00
int i = 0 ;
int dumped_total = 0 ;
int cx , cy , cz ;
DFCoord pos_cursor ;
2012-01-14 10:13:30 -07:00
if ( ! destroy | | here )
2011-08-03 20:04:46 -06:00
{
2012-03-03 06:38:24 -07:00
if ( ! Gui : : getCursorCoords ( cx , cy , cz ) )
2011-08-03 20:04:46 -06:00
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Cursor position not found. Please enabled the cursor. \n " ) ;
2011-08-03 20:04:46 -06:00
return CR_FAILURE ;
}
pos_cursor = DFCoord ( cx , cy , cz ) ;
2012-01-14 10:13:30 -07:00
}
if ( ! destroy )
{
2011-08-03 20:04:46 -06:00
{
Block * b = MC . BlockAt ( pos_cursor / 16 ) ;
if ( ! b )
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Cursor is in an invalid/uninitialized area. Place it over a floor. \n " ) ;
2011-08-03 20:04:46 -06:00
return CR_FAILURE ;
}
2012-02-13 15:56:33 -07:00
df : : tiletype ttype = MC . tiletypeAt ( pos_cursor ) ;
2011-08-03 20:04:46 -06:00
if ( ! DFHack : : isFloorTerrain ( ttype ) )
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Cursor should be placed over a floor. \n " ) ;
2011-08-03 20:04:46 -06:00
return CR_FAILURE ;
}
}
}
2012-04-10 10:19:41 -06:00
2012-05-15 04:32:09 -06:00
bool inventoryDumpingSkipped = false ;
2011-08-03 20:04:46 -06:00
// proceed with the dumpification operation
2012-01-31 09:55:38 -07:00
for ( size_t i = 0 ; i < numItems ; i + + )
2011-08-03 20:04:46 -06:00
{
2012-01-16 21:12:58 -07:00
df : : item * itm = world - > items . all [ i ] ;
2012-01-19 03:30:22 -07:00
DFCoord pos_item ( itm - > pos . x , itm - > pos . y , itm - > pos . z ) ;
2011-08-03 20:04:46 -06:00
// only dump the stuff marked for dumping and laying on the ground
2012-01-16 19:16:16 -07:00
if ( ! itm - > flags . bits . dump
2012-05-15 04:32:09 -06:00
// || !itm->flags.bits.on_ground
2012-01-16 19:16:16 -07:00
| | itm - > flags . bits . construction
| | itm - > flags . bits . in_building
| | itm - > flags . bits . in_chest
2012-05-15 04:32:09 -06:00
// || itm->flags.bits.in_inventory
2012-01-16 19:16:16 -07:00
| | itm - > flags . bits . artifact1
2011-08-03 20:04:46 -06:00
)
continue ;
2012-01-28 06:40:09 -07:00
if ( need_visible & & itm - > flags . bits . hidden )
continue ;
if ( need_hidden & & ! itm - > flags . bits . hidden )
continue ;
if ( need_forbidden & & ! itm - > flags . bits . forbid )
continue ;
if ( ! need_forbidden & & itm - > flags . bits . forbid )
continue ;
2012-05-15 04:32:09 -06:00
if ( itm - > flags . bits . in_inventory & & Gui : : getSelectedUnit ( out , true ) )
{
// Due to GUI caching/redraw rules, Dwarf Fortress tends to crash if we make any changes to a unit's inventory
// while the player is looking at the inventory menu. Therefore, we'll simply skip such items until they
// change something (e.g. switch from "v" to "k" mode).
inventoryDumpingSkipped = true ;
continue ;
}
2012-01-28 06:40:09 -07:00
2011-08-03 20:04:46 -06:00
if ( ! destroy ) // move to cursor
{
// Change flags to indicate the dump was completed, as if by super-dwarfs
2012-01-16 19:16:16 -07:00
itm - > flags . bits . dump = false ;
itm - > flags . bits . forbid = true ;
2011-08-03 20:04:46 -06:00
// Don't move items if they're already at the cursor
2012-04-10 10:19:41 -06:00
if ( pos_cursor ! = pos_item )
2012-04-12 08:37:27 -06:00
Items : : moveToGround ( MC , itm , pos_cursor ) ;
2011-08-03 20:04:46 -06:00
}
else // destroy
{
2012-01-14 10:13:30 -07:00
if ( here & & pos_item ! = pos_cursor )
continue ;
2012-03-30 00:44:52 -06:00
itm - > flags . bits . garbage_collect = true ;
2012-01-14 10:13:30 -07:00
// Cosmetic changes: make them disappear from view instantly
2012-01-16 19:16:16 -07:00
itm - > flags . bits . forbid = true ;
itm - > flags . bits . hidden = true ;
2011-08-03 20:04:46 -06:00
}
2012-04-10 10:19:41 -06:00
2011-08-03 20:04:46 -06:00
dumped_total + + ;
}
2012-04-10 10:19:41 -06:00
// write map changes back to DF.
if ( ! destroy )
2011-08-03 20:04:46 -06:00
MC . WriteAll ( ) ;
2012-04-10 10:19:41 -06:00
2012-05-15 04:32:09 -06:00
if ( inventoryDumpingSkipped ) { out . printerr ( " Some inventory items could not be autodumped because the unit/inventory screen is currently active. Please close the unit screen and repeat the operation. \n " ) ; }
2012-03-10 04:55:42 -07:00
out . print ( " Done. %d items %s. \n " , dumped_total , destroy ? " marked for destruction " : " quickdumped " ) ;
2011-08-03 20:04:46 -06:00
return CR_OK ;
2012-01-14 10:13:30 -07:00
}
2012-03-10 04:55:42 -07:00
command_result df_autodump ( color_ostream & out , vector < string > & parameters )
2012-01-14 10:13:30 -07:00
{
2012-03-10 04:55:42 -07:00
CoreSuspender suspend ;
2012-01-14 10:13:30 -07:00
2012-03-10 04:55:42 -07:00
return autodump_main ( out , parameters ) ;
2012-01-14 10:13:30 -07:00
}
2012-03-10 04:55:42 -07:00
command_result df_autodump_destroy_here ( color_ostream & out , vector < string > & parameters )
2012-01-14 10:13:30 -07:00
{
// HOTKEY COMMAND; CORE ALREADY SUSPENDED
if ( ! parameters . empty ( ) )
return CR_WRONG_USAGE ;
vector < string > args ;
args . push_back ( " destroy-here " ) ;
2012-03-10 04:55:42 -07:00
return autodump_main ( out , args ) ;
2012-01-14 10:13:30 -07:00
}
2012-01-31 09:55:38 -07:00
static map < int , df : : item_flags > pending_destroy ;
2012-01-14 10:13:30 -07:00
static int last_frame = 0 ;
2012-03-10 04:55:42 -07:00
command_result df_autodump_destroy_item ( color_ostream & out , vector < string > & parameters )
2012-01-14 10:13:30 -07:00
{
// HOTKEY COMMAND; CORE ALREADY SUSPENDED
if ( ! parameters . empty ( ) )
return CR_WRONG_USAGE ;
2012-03-10 04:55:42 -07:00
df : : item * item = Gui : : getSelectedItem ( out ) ;
2012-01-14 10:13:30 -07:00
if ( ! item )
return CR_FAILURE ;
// Allow undoing the destroy
2012-01-21 17:31:15 -07:00
if ( world - > frame_counter ! = last_frame )
2012-01-14 10:13:30 -07:00
{
2012-01-21 17:31:15 -07:00
last_frame = world - > frame_counter ;
2012-01-14 10:13:30 -07:00
pending_destroy . clear ( ) ;
}
if ( pending_destroy . count ( item - > id ) )
{
df : : item_flags old_flags = pending_destroy [ item - > id ] ;
pending_destroy . erase ( item - > id ) ;
2012-03-30 00:44:52 -06:00
item - > flags . bits . garbage_collect = false ;
2012-01-14 10:13:30 -07:00
item - > flags . bits . hidden = old_flags . bits . hidden ;
item - > flags . bits . dump = old_flags . bits . dump ;
item - > flags . bits . forbid = old_flags . bits . forbid ;
return CR_OK ;
}
// Check the item is good to destroy
2012-03-30 00:44:52 -06:00
if ( item - > flags . bits . garbage_collect )
2012-01-14 10:13:30 -07:00
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Item is already marked for destroy. \n " ) ;
2012-01-14 10:13:30 -07:00
return CR_FAILURE ;
}
if ( item - > flags . bits . construction | |
item - > flags . bits . in_building | |
item - > flags . bits . artifact1 )
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Choosing not to destroy buildings, constructions and artifacts. \n " ) ;
2012-01-14 10:13:30 -07:00
return CR_FAILURE ;
}
2012-01-31 09:55:38 -07:00
for ( size_t i = 0 ; i < item - > itemrefs . size ( ) ; i + + )
2012-01-14 10:13:30 -07:00
{
df : : general_ref * ref = item - > itemrefs [ i ] ;
2012-01-21 17:31:15 -07:00
if ( ref - > getType ( ) = = general_ref_type : : UNIT_HOLDER )
2012-01-14 10:13:30 -07:00
{
2012-03-10 04:55:42 -07:00
out . printerr ( " Choosing not to destroy items in unit inventory. \n " ) ;
2012-01-14 10:13:30 -07:00
return CR_FAILURE ;
}
}
// Set the flags
pending_destroy [ item - > id ] = item - > flags ;
2012-03-30 00:44:52 -06:00
item - > flags . bits . garbage_collect = true ;
2012-01-14 10:13:30 -07:00
item - > flags . bits . hidden = true ;
item - > flags . bits . dump = true ;
item - > flags . bits . forbid = true ;
return CR_OK ;
}