@ -26,26 +26,20 @@
# include <queue>
using std : : vector ;
using std : : string ;
using std : : map ;
using std : : set ;
using std : : queue ;
using std : : endl ;
using namespace DFHack ;
using namespace df : : enums ;
using df : : global : : world ;
using df : : global : : ui ;
static command_result autofarm ( color_ostream & out , vector < st ring> & parameters ) ;
static command_result autofarm ( color_ostream & out , std : : vector < st d: : st ring> & parameters ) ;
DFHACK_PLUGIN ( " autofarm " ) ;
DFHACK_PLUGIN_IS_ENABLED ( enabled ) ;
const char * tagline = " Automatically handle crop selection in farm plots based on current plant stocks. " ;
const char * usage = (
const char * tagline = " Automatically handle crop selection in farm plots based on current plant stocks. " ;
const char * usage = (
" ``enable autofarm``: Enables the plugin \n "
" ``autofarm runonce``: Updates farm plots (one-time only) \n "
" ``autofarm status``: Prints status information \n "
@ -55,10 +49,10 @@ const char *usage = (
class AutoFarm {
private :
map< int , int > thresholds ;
std: : map< int , int > thresholds ;
int defaultThreshold = 50 ;
map< int , int > lastCounts ;
std: : map< int , int > lastCounts ;
public :
void initialize ( )
@ -106,9 +100,9 @@ public:
}
private :
map< int , set < df : : biome_type > > plantable_plants ;
std: : map< int , std : : set < df : : biome_type > > plantable_plants ;
const map< df : : plant_raw_flags , df : : biome_type > biomeFlagMap = {
const std: : map< df : : plant_raw_flags , df : : biome_type > biomeFlagMap = {
{ df : : plant_raw_flags : : BIOME_MOUNTAIN , df : : biome_type : : MOUNTAIN } ,
{ df : : plant_raw_flags : : BIOME_GLACIER , df : : biome_type : : GLACIER } ,
{ df : : plant_raw_flags : : BIOME_TUNDRA , df : : biome_type : : TUNDRA } ,
@ -168,41 +162,37 @@ public:
{
plantable_plants . clear ( ) ;
map< int , int > counts ;
std: : map< int , int > counts ;
df : : item_flags bad_flags ;
bad_flags . whole = 0 ;
# define F(x) bad_flags.bits.x = true;
F ( dump ) ; F ( forbid ) ; F ( garbage_collect ) ;
F ( hostile ) ; F ( on_fire ) ; F ( rotten ) ; F ( trader ) ;
F ( in_building ) ; F ( construction ) ; F ( artifact ) ;
const uint32_t bad_flags {
# define F(x) (df::item_flags::Mask::mask_##x)
F ( dump ) | F ( forbid ) | F ( garbage_collect ) |
F ( hostile ) | F ( on_fire ) | F ( rotten ) | F ( trader ) |
F ( in_building ) | F ( construction ) | F ( artifact )
# undef F
} ;
for ( auto ii : world - > items . other [ df : : items_other_id : : SEEDS ] )
for ( auto & ii : world - > items . other [ df : : items_other_id : : SEEDS ] )
{
auto i = virtual_cast < df : : item_seedsst > ( ii ) ;
if ( i & & ( i - > flags . whole & bad_flags .whole ) = = 0 )
if ( i & & ( i - > flags . whole & bad_flags ) = = 0 )
counts [ i - > mat_index ] + = i - > stack_size ;
}
for ( auto & ci : counts )
for ( auto & ci : counts )
{
df : : plant_raw * plant = world - > raws . plants . all [ ci . first ] ;
if ( is_plantable ( plant ) )
for ( auto & flagmap : biomeFlagMap )
for ( auto & flagmap : biomeFlagMap )
if ( plant - > flags . is_set ( flagmap . first ) )
plantable_plants [ plant - > index ] . insert ( flagmap . second ) ;
}
}
st ring get_plant_name ( int plant_id )
st d: : st ring get_plant_name ( int plant_id )
{
df : : plant_raw * raw = df : : plant_raw : : find ( plant_id ) ;
if ( raw )
return raw - > name ;
else
return " NONE " ;
df : : plant_raw * raw = df : : plant_raw : : find ( plant_id ) ;
return raw ? raw - > name : " NONE " ;
}
void set_farm ( color_ostream & out , int new_plant_id , df : : building_farmplotst * farm , int season )
@ -213,11 +203,11 @@ public:
farm - > plant_id [ season ] = new_plant_id ;
out < < " autofarm: changing farm # " < < farm - > id < <
" from " < < get_plant_name ( old_plant_id ) < <
" to " < < get_plant_name ( new_plant_id ) < < endl ;
" to " < < get_plant_name ( new_plant_id ) < < ' \n ' ;
}
}
void set_farms ( color_ostream & out , set < int > plants , vector < df : : building_farmplotst * > farms )
void set_farms ( color_ostream & out , const std : : set < int > & plants , const std : : vector < df : : building_farmplotst * > & farms )
{
// this algorithm attempts to change as few farms as possible, while ensuring that
// the number of farms planting each eligible plant is "as equal as possible"
@ -238,16 +228,13 @@ public:
int min = farms . size ( ) / plants . size ( ) ; // the number of farms that should plant each eligible plant, rounded down
int extra = farms . size ( ) - min * plants . size ( ) ; // the remainder that cannot be evenly divided
map < int , int > counters ;
counters . empty ( ) ;
queue < df : : building_farmplotst * > toChange ;
toChange . empty ( ) ;
std : : map < int , int > counters ;
std : : queue < df : : building_farmplotst * > toChange ;
for ( auto farm : farms )
{
int o = farm - > plant_id [ season ] ;
if ( plants . count ( o ) = = 0 | | counters [ o ] > min | | ( counters [ o ] = = min & & extra = = 0 ) )
if ( plants . count ( o ) = = 0 | | counters [ o ] > min | | ( counters [ o ] = = min & & extra = = 0 ) )
toChange . push ( farm ) ; // this farm is an excess instance for the plant it is currently planting
else
{
@ -281,44 +268,33 @@ public:
lastCounts . clear ( ) ;
df : : item_flags bad_flags ;
bad_flags . whole = 0 ;
# define F(x) bad_flags.bits.x = true;
F ( dump ) ; F ( forbid ) ; F ( garbage_collect ) ;
F ( hostile ) ; F ( on_fire ) ; F ( rotten ) ; F ( trader ) ;
F ( in_building ) ; F ( construction ) ; F ( artifact ) ;
# undef F
const uint32_t bad_flags {
# define F(x) (df::item_flags::Mask::mask_##x)
F ( dump ) | F ( forbid ) | F ( garbage_collect ) |
F ( hostile ) | F ( on_fire ) | F ( rotten ) | F ( trader ) |
F ( in_building ) | F ( construction ) | F ( artifact )
# undef F
} ;
// have to scan both items[PLANT] and items[PLANT_GROWTH] because agricultural products can be either
for ( auto ii : world - > items . other [ df : : items_other_id : : PLANT ] )
{
auto i = virtual_cast < df : : item_plantst > ( ii ) ;
if ( i & &
( i - > flags . whole & bad_flags . whole ) = = 0 & &
plantable_plants . count ( i - > mat_index ) > 0 )
auto count = [ & , this ] ( df : : item * i ) {
auto mat = i - > getMaterialIndex ( ) ;
if ( ( i - > flags . whole & bad_flags ) = = 0 & &
plantable_plants . count ( mat ) > 0 )
{
lastCounts [ i - > mat_index ] + = i - > stack_size ;
}
}
for ( auto ii : world - > items . other [ df : : items_other_id : : PLANT_GROWTH ] )
{
auto i = virtual_cast < df : : item_plant_growthst > ( ii ) ;
if ( i & &
( i - > flags . whole & bad_flags . whole ) = = 0 & &
plantable_plants . count ( i - > mat_index ) > 0 )
{
lastCounts [ i - > mat_index ] + = i - > stack_size ;
}
lastCounts [ mat ] + = i - > getStackSize ( ) ;
}
} ;
for ( auto i : world - > items . other [ df : : items_other_id : : PLANT ] )
count ( i ) ;
for ( auto i : world - > items . other [ df : : items_other_id : : PLANT_GROWTH ] )
count ( i ) ;
map < df : : biome_type , set < int > > plants ;
plants . clear ( ) ;
std : : map < df : : biome_type , std : : set < int > > plants ;
for ( auto & plantable : plantable_plants )
for ( auto & plantable : plantable_plants )
{
df : : plant_raw * plant = world - > raws . plants . all [ plantable . first ] ;
if ( lastCounts [ plant - > index ] < getThreshold ( plant - > index ) )
@ -328,10 +304,9 @@ public:
}
}
map < df : : biome_type , vector < df : : building_farmplotst * > > farms ;
farms . clear ( ) ;
std : : map < df : : biome_type , std : : vector < df : : building_farmplotst * > > farms ;
for ( auto bb : world - > buildings . other [ df : : buildings_other_id : : FARM_PLOT ] )
for ( auto & bb : world - > buildings . other [ df : : buildings_other_id : : FARM_PLOT ] )
{
auto farm = virtual_cast < df : : building_farmplotst > ( bb ) ;
if ( farm - > flags . bits . exists )
@ -347,7 +322,7 @@ public:
}
}
for ( auto & ff : farms )
for ( auto & ff : farms )
{
set_farms ( out , plants [ ff . first ] , ff . second ) ;
}
@ -355,28 +330,28 @@ public:
void status ( color_ostream & out )
{
out < < ( enabled ? " Running. " : " Stopped. " ) < < endl ;
for ( auto & lc : lastCounts )
out < < ( enabled ? " Running. " : " Stopped. " ) < < ' \n ' ;
for ( auto & lc : lastCounts )
{
auto plant = world - > raws . plants . all [ lc . first ] ;
out < < plant - > id < < " limit " < < getThreshold ( lc . first ) < < " current " < < lc . second < < endl ;
out < < plant - > id < < " limit " < < getThreshold ( lc . first ) < < " current " < < lc . second < < ' \n ' ;
}
for ( auto & th : thresholds )
for ( auto & th : thresholds )
{
if ( lastCounts [ th . first ] > 0 )
continue ;
auto plant = world - > raws . plants . all [ th . first ] ;
out < < plant - > id < < " limit " < < getThreshold ( th . first ) < < " current 0 " < < endl ;
out < < plant - > id < < " limit " < < getThreshold ( th . first ) < < " current 0 " < < ' \n ' ;
}
out < < " Default: " < < defaultThreshold < < endl ;
out < < " Default: " < < defaultThreshold < < ' \n ' ;
}
} ;
static std : : unique_ptr < AutoFarm > autofarmInstance ;
DFhackCExport command_result plugin_init ( color_ostream & out , std : : vector < PluginCommand > & commands )
DFhackCExport command_result plugin_init ( color_ostream & out , std : : vector < PluginCommand > & commands )
{
if ( world & & ui ) {
commands . push_back (
@ -389,14 +364,14 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( color_ostream & out )
DFhackCExport command_result plugin_shutdown ( color_ostream & out )
{
autofarmInstance . release ( ) ;
return CR_OK ;
}
DFhackCExport command_result plugin_onupdate ( color_ostream & out )
DFhackCExport command_result plugin_onupdate ( color_ostream & out )
{
if ( ! autofarmInstance )
return CR_OK ;
@ -418,19 +393,19 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
return CR_OK ;
}
DFhackCExport command_result plugin_enable ( color_ostream & out , bool enable )
DFhackCExport command_result plugin_enable ( color_ostream & out , bool enable )
{
enabled = enable ;
return CR_OK ;
}
static command_result setThresholds ( color_ostream & out , vector< st ring> & parameters )
static command_result setThresholds ( color_ostream & out , std: : vector< st d: : st ring> & parameters )
{
int val = atoi ( parameters [ 1 ] . c_str ( ) ) ;
for ( size_t i = 2 ; i < parameters . size ( ) ; i + + )
{
st ring id = parameters [ i ] ;
transform( id . begin ( ) , id . end ( ) , id . begin ( ) , : : toupper ) ;
st d: : st ring id = parameters [ i ] ;
std: : transform( id . begin ( ) , id . end ( ) , id . begin ( ) , : : toupper ) ;
bool ok = false ;
for ( auto plant : world - > raws . plants . all )
@ -444,14 +419,14 @@ static command_result setThresholds(color_ostream& out, vector<string> & paramet
}
if ( ! ok )
{
out < < " Cannot find plant with id " < < id < < endl ;
out < < " Cannot find plant with id " < < id < < ' \n ' ;
return CR_WRONG_USAGE ;
}
}
return CR_OK ;
}
static command_result autofarm ( color_ostream & out , vector < st ring> & parameters )
static command_result autofarm ( color_ostream & out , std : : vector < st d: : st ring> & parameters )
{
CoreSuspender suspend ;