@ -6,6 +6,7 @@
# include "PluginManager.h"
# include "TileTypes.h"
# include "LuaTools.h"
# include "Debug.h"
# include "modules/Buildings.h"
# include "modules/Gui.h"
@ -14,6 +15,8 @@
# include "modules/Random.h"
# include "modules/Units.h"
# include "modules/World.h"
# include "modules/EventManager.h"
# include "modules/Job.h"
# include <df/historical_entity.h>
# include <df/map_block.h>
@ -26,12 +29,129 @@
# include <df/world.h>
# include <df/world_site.h>
# include <cinttypes>
# include <unordered_set>
# include <unordered_map>
DFHACK_PLUGIN ( " dig-now " ) ;
REQUIRE_GLOBAL ( plotinfo ) ;
REQUIRE_GLOBAL ( world ) ;
// Debugging
namespace DFHack {
DBG_DECLARE ( dignow , general , DebugCategory : : LINFO ) ;
DBG_DECLARE ( dignow , channels , DebugCategory : : LINFO ) ;
}
# define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16
# define COORDARGS(id) id.x, id.y, id.z
using namespace DFHack ;
struct designation {
df : : coord pos ;
df : : tile_designation type ;
df : : tile_occupancy occupancy ;
designation ( ) = default ;
designation ( const df : : coord & c , const df : : tile_designation & td , const df : : tile_occupancy & to ) : pos ( c ) , type ( td ) , occupancy ( to ) { }
bool operator = = ( const designation & rhs ) const {
return pos = = rhs . pos ;
}
bool operator ! = ( const designation & rhs ) const {
return ! ( rhs = = * this ) ;
}
} ;
namespace std {
template < >
struct hash < designation > {
std : : size_t operator ( ) ( const designation & c ) const {
std : : hash < df : : coord > hash_coord ;
return hash_coord ( c . pos ) ;
}
} ;
}
class DesignationJobs {
private :
std : : unordered_map < df : : coord , designation > designations ;
std : : unordered_map < df : : coord , df : : job * > jobs ;
public :
void load ( MapExtras : : MapCache & map ) {
designations . clear ( ) ;
df : : job_list_link * node = df : : global : : world - > jobs . list . next ;
while ( node ) {
df : : job * job = node - > item ;
if ( ! job | | ! Maps : : isValidTilePos ( job - > pos ) )
continue ;
node = node - > next ;
df : : tile_designation td = map . designationAt ( job - > pos ) ;
df : : tile_occupancy to = map . occupancyAt ( job - > pos ) ;
const auto ctd = td . whole ;
const auto cto = to . whole ;
switch ( job - > job_type ) {
case job_type : : Dig :
td . bits . dig = tile_dig_designation : : Default ;
break ;
case job_type : : DigChannel :
td . bits . dig = tile_dig_designation : : Channel ;
break ;
case job_type : : CarveRamp :
td . bits . dig = tile_dig_designation : : Ramp ;
break ;
case job_type : : CarveUpwardStaircase :
td . bits . dig = tile_dig_designation : : UpStair ;
break ;
case job_type : : CarveDownwardStaircase :
td . bits . dig = tile_dig_designation : : DownStair ;
break ;
case job_type : : CarveUpDownStaircase :
td . bits . dig = tile_dig_designation : : UpDownStair ;
break ;
case job_type : : DetailWall :
case job_type : : DetailFloor : {
df : : tiletype tt = map . tiletypeAt ( job - > pos ) ;
if ( tileSpecial ( tt ) ! = df : : tiletype_special : : SMOOTH ) {
td . bits . smooth = 1 ;
}
break ;
}
case job_type : : CarveTrack :
to . bits . carve_track_north = ( job - > item_category . whole > > 18 ) & 1 ;
to . bits . carve_track_south = ( job - > item_category . whole > > 19 ) & 1 ;
to . bits . carve_track_west = ( job - > item_category . whole > > 20 ) & 1 ;
to . bits . carve_track_east = ( job - > item_category . whole > > 21 ) & 1 ;
break ;
default :
break ;
}
if ( ctd ! = td . whole | | cto ! = to . whole ) {
// we found a designation job
designations . emplace ( job - > pos , designation ( job - > pos , td , to ) ) ;
jobs . emplace ( job - > pos , job ) ;
}
}
}
void remove ( const df : : coord & pos ) {
if ( jobs . count ( pos ) ) {
Job : : removeJob ( jobs [ pos ] ) ;
jobs . erase ( pos ) ;
}
}
designation get ( const df : : coord & pos ) {
if ( designations . count ( pos ) ) {
return designations [ pos ] ;
}
return { } ;
}
bool count ( const df : : coord & pos ) {
return jobs . count ( pos ) ;
}
} ;
struct boulder_percent_options {
// percent chance ([0..100]) for creating a boulder for the given rock type
uint32_t layer ;
@ -320,8 +440,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map,
std : : vector < dug_tile_info > & dug_tiles ) {
df : : tiletype tt = map . tiletypeAt ( pos ) ;
if ( ! is_diggable ( map , pos , tt ) )
if ( ! is_diggable ( map , pos , tt ) ) {
DEBUG ( general ) . print ( " dig_tile: not diggable \n " ) ;
return false ;
}
/** The algorithm process seems to be:
* for each tile
* check for a designation
* if a designation exists send it to dig_tile
*
* dig_tile ( below ) then digs the layer below the channel designated tile
* thereby changing it and causing its designation to be lost
* */
df : : tiletype target_type = df : : tiletype : : Void ;
switch ( designation ) {
@ -341,19 +472,22 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map,
DFCoord pos_below ( pos . x , pos . y , pos . z - 1 ) ;
if ( can_dig_channel ( tt ) & & map . ensureBlockAt ( pos_below )
& & is_diggable ( map , pos_below , map . tiletypeAt ( pos_below ) ) ) {
TRACE ( channels ) . print ( " dig_tile: channeling at ( " COORD " ) [can_dig_channel: true] \n " , COORDARGS ( pos_below ) ) ;
target_type = df : : tiletype : : OpenSpace ;
DFCoord pos_above ( pos . x , pos . y , pos . z + 1 ) ;
if ( map . ensureBlockAt ( pos_above ) )
if ( map . ensureBlockAt ( pos_above ) ) {
remove_ramp_top ( map , pos_above ) ;
df : : tile_dig_designation td_below =
map . designationAt ( pos_below ) . bits . dig ;
if ( dig_tile ( out , map , pos_below ,
df : : tile_dig_designation : : Ramp , dug_tiles ) ) {
}
df : : tile_dig_designation td_below = map . designationAt ( pos_below ) . bits . dig ;
if ( dig_tile ( out , map , pos_below , df : : tile_dig_designation : : Ramp , dug_tiles ) ) {
clean_ramps ( map , pos_below ) ;
if ( td_below = = df : : tile_dig_designation : : Default )
if ( td_below = = df : : tile_dig_designation : : Default ) {
dig_tile ( out , map , pos_below , td_below , dug_tiles ) ;
}
return true ;
}
} else {
DEBUG ( channels ) . print ( " dig_tile: failed to channel at ( " COORD " ) [can_dig_channel: false] \n " , COORDARGS ( pos_below ) ) ;
}
break ;
}
@ -407,7 +541,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map,
if ( target_type = = df : : tiletype : : Void | | target_type = = tt )
return false ;
dug_tiles . push_back ( dug_tile_info ( map , pos ) ) ;
dug_tiles . emplace_back ( map , pos ) ;
TRACE ( general ) . print ( " dig_tile: digging the designation tile at ( " COORD " ) \n " , COORDARGS ( pos ) ) ;
dig_type ( map , pos , target_type ) ;
// let light filter down to newly exposed tiles
@ -594,9 +729,12 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &dug_coords,
item_coords_t & item_coords , const dig_now_options & options ) {
MapExtras : : MapCache map ;
Random : : MersenneRNG rng ;
DesignationJobs jobs ;
jobs . load ( map ) ;
rng . init ( ) ;
std : : unordered_set < designation > buffer ;
// go down levels instead of up so stacked ramps behave as expected
for ( int16_t z = options . end . z ; z > = options . start . z ; - - z ) {
for ( int16_t y = options . start . y ; y < = options . end . y ; + + y ) {
@ -609,46 +747,68 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &dug_coords,
DFCoord pos ( x , y , z ) ;
df : : tile_designation td = map . designationAt ( pos ) ;
df : : tile_occupancy to = map . occupancyAt ( pos ) ;
if ( td . bits . dig ! = df : : tile_dig_designation : : No & &
! to . bits . dig_marked ) {
std : : vector < dug_tile_info > dug_tiles ;
if ( dig_tile ( out , map , pos , td . bits . dig , dug_tiles ) ) {
for ( auto info : dug_tiles ) {
td = map . designationAt ( info . pos ) ;
td . bits . dig = df : : tile_dig_designation : : No ;
map . setDesignationAt ( info . pos , td ) ;
dug_coords . push_back ( info . pos ) ;
refresh_adjacent_smooth_walls ( map , info . pos ) ;
if ( info . imat < 0 )
continue ;
if ( produces_item ( options . boulder_percents ,
map , rng , info ) ) {
auto k = std : : make_pair ( info . itype , info . imat ) ;
item_coords [ k ] . push_back ( info . pos ) ;
}
}
}
} else if ( td . bits . smooth = = 1 ) {
if ( smooth_tile ( out , map , pos ) ) {
td = map . designationAt ( pos ) ;
td . bits . smooth = 0 ;
map . setDesignationAt ( pos , td ) ;
}
} else if ( to . bits . carve_track_north = = 1
| | to . bits . carve_track_east = = 1
| | to . bits . carve_track_south = = 1
| | to . bits . carve_track_west = = 1 ) {
if ( carve_tile ( map , pos , to ) ) {
to = map . occupancyAt ( pos ) ;
to . bits . carve_track_north = 0 ;
to . bits . carve_track_east = 0 ;
to . bits . carve_track_south = 0 ;
to . bits . carve_track_west = 0 ;
map . setOccupancyAt ( pos , to ) ;
if ( jobs . count ( pos ) ) {
buffer . emplace ( jobs . get ( pos ) ) ;
jobs . remove ( pos ) ;
// if it does get removed, then we're gonna buffer the jobs info then remove the job
} else if ( ( td . bits . dig ! = df : : tile_dig_designation : : No & & ! to . bits . dig_marked )
| | td . bits . smooth = = 1
| | to . bits . carve_track_north = = 1
| | to . bits . carve_track_east = = 1
| | to . bits . carve_track_south = = 1
| | to . bits . carve_track_west = = 1 ) {
// we're only buffering designations, so that processing doesn't affect what we're buffering
buffer . emplace ( pos , td , to ) ;
}
}
}
}
// process designations
for ( auto & d : buffer ) {
auto pos = d . pos ;
auto td = d . type ;
auto to = d . occupancy ;
if ( td . bits . dig ! = df : : tile_dig_designation : : No & & ! to . bits . dig_marked ) {
std : : vector < dug_tile_info > dug_tiles ;
if ( dig_tile ( out , map , pos , td . bits . dig , dug_tiles ) ) {
for ( auto info : dug_tiles ) {
td = map . designationAt ( info . pos ) ;
td . bits . dig = df : : tile_dig_designation : : No ;
map . setDesignationAt ( info . pos , td ) ;
dug_coords . push_back ( info . pos ) ;
refresh_adjacent_smooth_walls ( map , info . pos ) ;
if ( info . imat < 0 )
continue ;
if ( produces_item ( options . boulder_percents ,
map , rng , info ) ) {
auto k = std : : make_pair ( info . itype , info . imat ) ;
item_coords [ k ] . push_back ( info . pos ) ;
}
}
}
} else if ( td . bits . smooth = = 1 ) {
if ( smooth_tile ( out , map , pos ) ) {
td = map . designationAt ( pos ) ;
td . bits . smooth = 0 ;
map . setDesignationAt ( pos , td ) ;
}
} else if ( to . bits . carve_track_north = = 1
| | to . bits . carve_track_east = = 1
| | to . bits . carve_track_south = = 1
| | to . bits . carve_track_west = = 1 ) {
if ( carve_tile ( map , pos , to ) ) {
to = map . occupancyAt ( pos ) ;
to . bits . carve_track_north = 0 ;
to . bits . carve_track_east = 0 ;
to . bits . carve_track_south = 0 ;
to . bits . carve_track_west = 0 ;
map . setOccupancyAt ( pos , to ) ;
}
}
}