@ -36,6 +36,7 @@
# include "df/item_weaponst.h"
# include "df/inorganic_raw.h"
# include "df/job.h"
# include "df/job_list_link.h"
# include "df/job_skill.h"
# include "df/job_type.h"
# include "df/map_block.h"
@ -67,28 +68,31 @@ using namespace std;
using namespace DFHack ;
using namespace df : : enums ;
command_result diggingInvaders Func ( color_ostream & out , std : : vector < std : : string > & parameters ) ;
command_result diggingInvaders Command ( color_ostream & out , std : : vector < std : : string > & parameters ) ;
void watchForJobComplete ( color_ostream & out , void * ptr ) ;
void initiateDigging ( color_ostream & out , void * ptr ) ;
int32_t manageInvasion ( color_ostream & out ) ;
void newInvasionHandler ( color_ostream & out , void * ptr ) ;
//int32_t manageInvasion(color_ostream& out);
DFHACK_PLUGIN ( " diggingInvaders " ) ;
//TODO: when world unloads
static int32_t lastInvasionJob = - 1 ;
static int32_t lastInvasionDigger = - 1 ;
static EventManager : : EventHandler jobCompleteHandler ( watchForJobComplete , 5 ) ;
//static EventManager::EventHandler jobCompleteHandler(watchForJobComplete, 5);
static bool enabled = false ;
static unordered_set < int32_t > diggingRaces ;
static bool activeDigging = false ;
static unordered_set < string > diggingRaces ;
static unordered_set < int32_t > invaderJobs ;
static df : : coord lastDebugEdgeCostPoint ;
DFhackCExport command_result plugin_init ( color_ostream & out , std : : vector < PluginCommand > & commands )
{
EventManager : : EventHandler invasionHandler( initiateDigging , 1000 ) ;
EventManager : : registerListener ( EventManager : : EventType : : INVASION , invasionH andler, plugin_self ) ;
EventManager : : EventHandler handler( newInvasionHandler , 1000 ) ;
EventManager : : registerListener ( EventManager : : EventType : : INVASION , h andler, plugin_self ) ;
commands . push_back ( PluginCommand (
" diggingInvaders " , " Makes invaders dig to your dwarves. " ,
diggingInvaders Func , false , /* true means that the command can't be used from non-interactive user interface */
diggingInvaders Command , false , /* true means that the command can't be used from non-interactive user interface */
" diggingInvaders enable \n enables the plugin \n "
" diggingInvaders disable \n disables the plugin \n "
" diggingInvaders add GOBLIN \n registers the race GOBLIN as a digging invader \n "
@ -96,6 +100,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" diggingInvaders \n Makes invaders try to dig now. \n "
) ) ;
* df : : global : : debug_showambush = true ;
return CR_OK ;
}
@ -106,7 +111,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
DFhackCExport command_result plugin_onstatechange ( color_ostream & out , state_change_event event )
{
EventManager : : EventHandler invasionHandler ( initiateDigging , 1000 ) ;
EventManager : : EventHandler invasionHandler ( newInvasionHandler , 1000 ) ;
switch ( event ) {
case DFHack : : SC_WORLD_LOADED :
//TODO: check game mode
@ -151,9 +156,15 @@ public:
} ;
//bool important(df::coord pos, map<df::coord, set<Edge> >& edges, df::coord prev, set<df::coord>& importantPoints, set<Edge>& importantEdges);
int32_t findAndAssignInvasionJob ( color_ostream & out ) ;
void findAndAssignInvasionJob ( color_ostream & out , void * ) ;
void initiateDigging ( color_ostream & out , void * ptr ) {
void newInvasionHandler ( color_ostream & out , void * ptr ) {
if ( activeDigging )
return ;
activeDigging = true ;
EventManager : : EventHandler handler ( findAndAssignInvasionJob , 1 ) ;
EventManager : : registerTick ( handler , 1 , plugin_self ) ;
#if 0
//called when there's a new invasion
//TODO: check if invaders can dig
if ( manageInvasion ( out ) = = - 2 )
@ -164,10 +175,12 @@ void initiateDigging(color_ostream& out, void* ptr) {
tick = tick % 1000 ;
tick = 1000 - tick ;
EventManager : : EventHandler handle ( initiateDigging , 1000 ) ;
EventManager : : EventHandler handle ( newInvasionHandler , 1000 ) ;
EventManager : : registerTick ( handle , tick , plugin_self ) ;
# endif
}
#if 0
void watchForJobComplete ( color_ostream & out , void * ptr ) {
/*
df : : job * job = ( df : : job * ) ptr ;
@ -180,7 +193,9 @@ void watchForJobComplete(color_ostream& out, void* ptr) {
manageInvasion ( out ) ;
}
# endif
#if 0
int32_t manageInvasion ( color_ostream & out ) {
//EventManager::unregisterAll(plugin_self);
if ( ! enabled ) {
@ -233,9 +248,9 @@ int32_t manageInvasion(color_ostream& out) {
* df : : global : : pause_state = true ;
return 0 ; //did something
}
# endif
static df : : coord lastDebugEdgeCostPoint ;
command_result diggingInvadersFunc ( color_ostream & out , std : : vector < std : : string > & parameters ) {
command_result diggingInvadersCommand ( color_ostream & out , std : : vector < std : : string > & parameters ) {
for ( size_t a = 0 ; a < parameters . size ( ) ; a + + ) {
if ( parameters [ a ] = = " enable " ) {
enabled = true ;
@ -245,7 +260,12 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector<std::string>&
if ( a + 1 > = parameters . size ( ) )
return CR_WRONG_USAGE ;
string race = parameters [ a + 1 ] ;
bool foundIt = false ;
if ( parameters [ a ] = = " add " ) {
diggingRaces . insert ( race ) ;
} else {
diggingRaces . erase ( race ) ;
}
/*bool foundIt = false;
for ( size_t b = 0 ; b < df : : global : : world - > raws . creatures . all . size ( ) ; b + + ) {
df : : creature_raw * raw = df : : global : : world - > raws . creatures . all [ b ] ;
if ( race = = raw - > creature_id ) {
@ -262,7 +282,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector<std::string>&
if ( ! foundIt ) {
out . print ( " Couldn't find \" %s \" \n " , race . c_str ( ) ) ;
return CR_WRONG_USAGE ;
}
} */
a + + ;
} else if ( parameters [ a ] = = " setCost " ) {
if ( a + 2 > = parameters . size ( ) )
@ -297,92 +317,151 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector<std::string>&
}
}
if ( parameters . size ( ) = = 0 )
manageInvasion ( out ) ;
if ( parameters . size ( ) = = 0 ) {
//manageInvasion(out);
newInvasionHandler ( out , ( void * ) 0 ) ;
}
return CR_OK ;
}
int32_t findAndAssignInvasionJob ( color_ostream & out ) {
//returns the worker id of the job created
//CoreSuspender suspend;
/////////////////////////////////////////////////////////////////////////////////////////
//dijkstra globals
vector < int32_t > invaders ;
unordered_set < df : : coord , PointHash > invaderPts ;
unordered_set < df : : coord , PointHash > localPts ;
unordered_map < df : : coord , df : : coord , PointHash > parentMap ;
unordered_map < df : : coord , int64_t , PointHash > costMap ;
PointComp comp ( & costMap ) ;
set < df : : coord , PointComp > fringe ( comp ) ;
EventManager : : EventHandler findJobTickHandler ( findAndAssignInvasionJob , 1 ) ;
const int32_t edgesPerFrame = 1000 ;
int32_t localPtsFound = 0 ;
unordered_set < df : : coord , PointHash > closedSet ;
unordered_map < df : : coord , int32_t , PointHash > workNeeded ; //non-walking work needed to get there
bool foundTarget = false ;
int32_t edgeCount = 0 ;
void clearDijkstra ( ) {
invaders . clear ( ) ;
invaderPts . clear ( ) ;
localPts . clear ( ) ;
parentMap . clear ( ) ;
costMap . clear ( ) ;
comp = PointComp ( & costMap ) ;
fringe = set < df : : coord , PointComp > ( comp ) ;
localPtsFound = edgeCount = 0 ;
foundTarget = false ;
closedSet . clear ( ) ;
workNeeded . clear ( ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////
void findAndAssignInvasionJob ( color_ostream & out , void * tickTime ) {
CoreSuspender suspend ;
//returns the worker id of the job created //used to
//out.print("%s, %d: %d\n", __FILE__, __LINE__, (int32_t)tickTime);
unordered_set < df : : coord , PointHash > invaderPts ;
unordered_set < df : : coord , PointHash > localPts ;
unordered_map < df : : coord , df : : coord , PointHash > parentMap ;
unordered_map < df : : coord , int64_t , PointHash > costMap ;
if ( ! activeDigging ) {
clearDijkstra ( ) ;
return ;
}
EventManager : : unregister ( EventManager : : EventType : : TICK , findJobTickHandler , plugin_self ) ;
EventManager : : registerTick ( findJobTickHandler , 1 , plugin_self ) ;
PointComp comp ( & costMap ) ;
set < df : : coord , PointComp > fringe ( comp ) ;
uint32_t xMax , yMax , zMax ;
Maps : : getSize ( xMax , yMax , zMax ) ;
xMax * = 16 ;
yMax * = 16 ;
MapExtras : : MapCache cache ;
vector < df : : unit * > invaders ;
unordered_set < uint16_t > invaderConnectivity ;
unordered_set < uint16_t > localConnectivity ;
//find all locals and invaders
for ( size_t a = 0 ; a < df : : global : : world - > units . all . size ( ) ; a + + ) {
df : : unit * unit = df : : global : : world - > units . all [ a ] ;
if ( unit - > flags1 . bits . dead )
continue ;
if ( Units : : isCitizen ( unit ) ) {
if ( localPts . find ( unit - > pos ) ! = localPts . end ( ) )
continue ;
localPts . insert ( unit - > pos ) ;
df : : map_block * block = Maps : : getTileBlock ( unit - > pos ) ;
localConnectivity . insert ( block - > walkable [ unit - > pos . x & 0xF ] [ unit - > pos . y & 0xF ] ) ;
} else if ( unit - > flags1 . bits . active_invader ) {
if ( diggingRaces . find ( unit - > race ) = = diggingRaces . end ( ) )
continue ;
if ( invaderPts . find ( unit - > pos ) ! = invaderPts . end ( ) )
if ( fringe . empty ( ) ) {
df : : unit * lastDigger = df : : unit : : find ( lastInvasionDigger ) ;
if ( lastDigger & & lastDigger - > job . current_job & & lastDigger - > job . current_job - > id = = lastInvasionJob ) {
return ;
}
//out.print("%s,%d: lastDigger = %d, last job = %d, last digger's job = %d\n", __FILE__, __LINE__, lastInvasionDigger, lastInvasionJob, !lastDigger ? -1 : (!lastDigger->job.current_job ? -1 : lastDigger->job.current_job->id));
lastInvasionDigger = lastInvasionJob = - 1 ;
clearDijkstra ( ) ;
unordered_set < uint16_t > invaderConnectivity ;
unordered_set < uint16_t > localConnectivity ;
//find all locals and invaders
for ( size_t a = 0 ; a < df : : global : : world - > units . all . size ( ) ; a + + ) {
df : : unit * unit = df : : global : : world - > units . all [ a ] ;
if ( unit - > flags1 . bits . dead )
continue ;
df : : map_block * block = Maps : : getTileBlock ( unit - > pos ) ;
invaderConnectivity . insert ( block - > walkable [ unit - > pos . x & 0xF ] [ unit - > pos . y & 0xF ] ) ;
if ( invaderPts . size ( ) > 0 )
if ( Units : : isCitizen ( unit ) ) {
if ( localPts . find ( unit - > pos ) ! = localPts . end ( ) )
continue ;
localPts . insert ( unit - > pos ) ;
df : : map_block * block = Maps : : getTileBlock ( unit - > pos ) ;
localConnectivity . insert ( block - > walkable [ unit - > pos . x & 0xF ] [ unit - > pos . y & 0xF ] ) ;
} else if ( unit - > flags1 . bits . active_invader ) {
df : : creature_raw * raw = df : : creature_raw : : find ( unit - > race ) ;
if ( raw = = NULL ) {
out . print ( " %s,%d: WTF? Couldn't find creature raw. \n " , __FILE__ , __LINE__ ) ;
continue ;
}
if ( diggingRaces . find ( raw - > creature_id ) = = diggingRaces . end ( ) )
continue ;
if ( invaderPts . find ( unit - > pos ) ! = invaderPts . end ( ) )
continue ;
//must be able to wield a pick: this is overly pessimistic
if ( unit - > status2 . limbs_grasp_count < unit - > status2 . limbs_grasp_max )
continue ;
df : : map_block * block = Maps : : getTileBlock ( unit - > pos ) ;
invaderConnectivity . insert ( block - > walkable [ unit - > pos . x & 0xF ] [ unit - > pos . y & 0xF ] ) ;
if ( invaderPts . size ( ) > 0 )
continue ;
invaderPts . insert ( unit - > pos ) ;
costMap [ unit - > pos ] = 0 ;
fringe . insert ( unit - > pos ) ;
invaders . push_back ( unit - > id ) ;
} else {
continue ;
invaderPts . insert ( unit - > pos ) ;
costMap [ unit - > pos ] = 0 ;
fringe . insert ( unit - > pos ) ;
invaders . push_back ( unit ) ;
} else {
continue ;
}
}
}
if ( invaders . empty ( ) | | localPts . empty ( ) ) {
return - 1 ;
}
if ( invaders . empty ( ) | | localPts . empty ( ) ) {
activeDigging = false ;
return ;
}
//if local connectivity is not disjoint from invader connectivity, no digging required
bool overlap = false ;
for ( auto a = localConnectivity . begin ( ) ; a ! = localConnectivity . end ( ) ; a + + ) {
uint16_t conn = * a ;
if ( invaderConnectivity . find ( conn ) = = invaderConnectivity . end ( ) )
continue ;
overlap = true ;
break ;
//if local connectivity is not disjoint from invader connectivity, no digging required
bool overlap = false ;
for ( auto a = localConnectivity . begin ( ) ; a ! = localConnectivity . end ( ) ; a + + ) {
uint16_t conn = * a ;
if ( invaderConnectivity . find ( conn ) = = invaderConnectivity . end ( ) )
continue ;
overlap = true ;
break ;
}
if ( overlap ) {
//still keep checking next frame: might kill a few outsiders then dig down
return ;
}
}
if ( overlap ) {
return - 1 ;
df : : unit * firstInvader = df : : unit : : find ( invaders [ 0 ] ) ;
if ( firstInvader = = NULL ) {
fringe . clear ( ) ;
return ;
}
df : : unit * firstInvader = invaders [ 0 ] ;
//TODO: check that firstInvader is an appropriate digger
//out << firstInvader->id << endl;
//out << firstInvader->pos.x << ", " << firstInvader->pos.y << ", " << firstInvader->pos.z << endl;
//out << __LINE__ << endl;
int32_t localPtsFound = 0 ;
unordered_set< df : : coord , PointHash > closedSet ;
unordered_map< df : : coord , int32_t , PointHash > workNeeded ; //non-walking work needed to get there
bool foundTarget = false ;
int32_t edgeCount = 0 ;
uint32_t xMax , yMax , zMax ;
Maps: : getSize ( xMax , yMax , zMax ) ;
xMax * = 16 ;
yMax * = 16 ;
MapExtras : : MapCache cache ;
clock_t t0 = clock ( ) ;
clock_t totalEdgeTime = 0 ;
int32_t edgesExpanded = 0 ;
while ( ! fringe . empty ( ) ) {
if ( edgesExpanded + + > = edgesPerFrame ) {
return ;
}
df : : coord pt = * ( fringe . begin ( ) ) ;
fringe . erase ( fringe . begin ( ) ) ;
//out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d, pt = %d,%d,%d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size(), pt.x,pt.y,pt.z);
@ -400,7 +479,7 @@ int32_t findAndAssignInvasionJob(color_ostream& out) {
}
if ( workNeeded . find ( pt ) = = workNeeded . end ( ) | | workNeeded [ pt ] = = 0 ) {
//there are still dwarves to kill that don't require digging to get to
return - 1 ;
return ;
}
}
@ -434,27 +513,34 @@ int32_t findAndAssignInvasionJob(color_ostream& out) {
delete myEdges ;
}
clock_t time = clock ( ) - t0 ;
out . print ( " time = %d, totalEdgeTime = %d, total points = %d, total edges = %d, time per point = %.3f, time per edge = %.3f, clocks/sec = %d \n " , time , totalEdgeTime , closedSet . size ( ) , edgeCount , ( float ) time / closedSet . size ( ) , ( float ) time / edgeCount , CLOCKS_PER_SEC ) ;
//out.print("tickTime = %d, time = %d, totalEdgeTime = %d, total points = %d, total edges = %d, time per point = %.3f, time per edge = %.3f, clocks/sec = %d\n", (int32_t)tickTime, time, totalEdgeTime, closedSet.size(), edgeCount, (float)time / closedSet.size(), (float)time / edgeCount, CLOCKS_PER_SEC);
fringe . clear ( ) ;
if ( ! foundTarget )
return - 1 ;
return ;
unordered_set < df : : coord , PointHash > requiresZNeg ;
unordered_set < df : : coord , PointHash > requiresZPos ;
//find important edges
Edge firstImportantEdge ( df : : coord ( ) , df : : coord ( ) , - 1 ) ;
df : : coord closest ;
int64_t closestCostEstimate = 0 ;
int64_t closestCostActual = 0 ;
for ( auto i = localPts . begin ( ) ; i ! = localPts . end ( ) ; i + + ) {
df : : coord pt = * i ;
if ( costMap . find ( pt ) = = costMap . end ( ) )
continue ;
if ( parentMap . find ( pt ) = = parentMap . end ( ) )
continue ;
closest = pt ;
closestCostEstimate = costMap [ closest ] ;
//if ( workNeeded[pt] == 0 )
// continue;
while ( parentMap . find ( pt ) ! = parentMap . end ( ) ) {
//out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z);
df : : coord parent = parentMap [ pt ] ;
closestCostActual + = getEdgeCost ( out , parent , pt ) ;
if ( Maps : : canStepBetween ( parent , pt ) ) {
} else {
@ -477,7 +563,13 @@ int32_t findAndAssignInvasionJob(color_ostream& out) {
}
break ;
}
if ( firstImportantEdge . p1 = = df : : coord ( ) )
return ;
if ( closestCostActual ! = closestCostEstimate ) {
out . print ( " %s,%d: closest = (%d,%d,%d), estimate = %lld != actual = %lld \n " , __FILE__ , __LINE__ , closest . x , closest . y , closest . z , closestCostEstimate , closestCostActual ) ;
return ;
}
#if 0
unordered_set < df : : coord , PointHash > toDelete ;
for ( auto a = requiresZNeg . begin ( ) ; a ! = requiresZNeg . end ( ) ; a + + ) {
@ -502,7 +594,25 @@ int32_t findAndAssignInvasionJob(color_ostream& out) {
toDelete . clear ( ) ;
# endif
return assignJob ( out , firstImportantEdge , parentMap , costMap , invaders , requiresZNeg , requiresZPos , cache , diggingRaces ) ;
assignJob ( out , firstImportantEdge , parentMap , costMap , invaders , requiresZNeg , requiresZPos , cache ) ;
lastInvasionDigger = firstInvader - > id ;
lastInvasionJob = firstInvader - > job . current_job ? firstInvader - > job . current_job - > id : - 1 ;
invaderJobs . erase ( lastInvasionJob ) ;
for ( df : : job_list_link * link = & df : : global : : world - > job_list ; link ! = NULL ; link = link - > next ) {
if ( link - > item = = NULL )
continue ;
df : : job * job = link - > item ;
if ( invaderJobs . find ( job - > id ) = = invaderJobs . end ( ) ) {
continue ;
}
//cancel it
job - > flags . bits . item_lost = 1 ;
out . print ( " %s,%d: cancelling job %d. \n " , __FILE__ , __LINE__ , job - > id ) ;
//invaderJobs.remove(job->id);
}
invaderJobs . erase ( lastInvasionJob ) ;
return ;
}
df : : coord getRoot ( df : : coord point , map < df : : coord , df : : coord > & rootMap ) {