2022-11-06 01:12:35 -06:00
# include <channel-manager.h>
2022-11-06 16:59:30 -07:00
# include <tile-cache.h>
2022-11-06 01:12:35 -06:00
# include <inlines.h>
2022-11-10 16:59:48 -07:00
# include <modules/EventManager.h> //hash function for df::coord
2022-11-06 01:12:35 -06:00
# include <df/block_square_event_designation_priorityst.h>
2023-02-08 13:03:40 -07:00
# define NUMARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
# define d_assert(condition, ...) \
static_assert ( NUMARGS ( __VA_ARGS__ ) > = 1 , " d_assert(condition, format, ...) requires at least up to format as arguments " ) ; \
if ( ! condition ) { \
DFHack : : Core : : getInstance ( ) . getConsole ( ) . printerr ( __VA_ARGS__ ) ; \
assert ( 0 ) ; \
}
2022-12-08 12:21:17 -07:00
df : : unit * find_dwarf ( const df : : coord & map_pos ) {
2023-02-08 13:03:40 -07:00
2022-12-08 12:21:17 -07:00
df : : unit * nearest = nullptr ;
uint32_t distance ;
for ( auto unit : df : : global : : world - > units . active ) {
if ( ! nearest ) {
nearest = unit ;
distance = calc_distance ( unit - > pos , map_pos ) ;
} else if ( unit - > status . labors [ df : : unit_labor : : MINE ] ) {
uint32_t d = calc_distance ( unit - > pos , map_pos ) ;
if ( d < distance ) {
nearest = unit ;
distance = d ;
} else if ( Maps : : canWalkBetween ( unit - > pos , map_pos ) ) {
return unit ;
}
}
}
return nearest ;
}
2022-11-06 01:12:35 -06:00
// sets mark flags as necessary, for all designations
2022-11-06 12:53:46 -07:00
void ChannelManager : : manage_groups ( ) {
INFO ( manager ) . print ( " manage_groups() \n " ) ;
2022-11-06 01:12:35 -06:00
// make sure we've got a fort map to analyze
if ( World : : isFortressMode ( ) & & Maps : : IsValid ( ) ) {
// iterate the groups we built/updated
for ( const auto & group : groups ) {
manage_group ( group , true , has_any_groups_above ( groups , group ) ) ;
}
}
}
void ChannelManager : : manage_group ( const df : : coord & map_pos , bool set_marker_mode , bool marker_mode ) {
INFO ( manager ) . print ( " manage_group( " COORD " ) \n " , COORDARGS ( map_pos ) ) ;
if ( ! groups . count ( map_pos ) ) {
groups . scan_one ( map_pos ) ;
}
auto iter = groups . find ( map_pos ) ;
if ( iter ! = groups . end ( ) ) {
manage_group ( * iter , set_marker_mode , marker_mode ) ;
}
INFO ( manager ) . print ( " manage_group() is done \n " ) ;
}
2022-12-08 15:59:09 -07:00
void ChannelManager : : manage_group ( const Group & group , bool set_marker_mode , bool marker_mode ) const {
2022-11-06 01:12:35 -06:00
INFO ( manager ) . print ( " manage_group() \n " ) ;
if ( ! set_marker_mode ) {
2022-12-08 12:21:17 -07:00
marker_mode = has_any_groups_above ( groups , group ) ;
2022-11-06 01:12:35 -06:00
}
2022-12-08 12:37:28 -07:00
// cavein prevention
bool cavein_possible = false ;
uint8_t least_access = 100 ;
2023-02-08 13:03:40 -07:00
2022-12-08 12:37:28 -07:00
std : : unordered_map < df : : coord , uint8_t > cavein_candidates ;
if ( ! marker_mode ) {
/* To prevent cave-ins we're looking at accessibility of tiles with open space below them
* If it has space below , it has somewhere to fall
* Accessibility tells us how close to a cave - in a tile is , low values are at risk of cave - ins
* To count access , we find a random miner dwarf and count how many tile neighbours they can path to
* */
// find a dwarf to path from
df : : coord miner_pos = find_dwarf ( * group . begin ( ) ) - > pos ;
// Analyze designations
for ( const auto & pos : group ) {
df : : coord below ( pos ) ;
below . z - - ;
const auto below_ttype = * Maps : : getTileType ( below ) ;
// we can skip designations already queued for insta-digging
if ( CSP : : dignow_queue . count ( pos ) ) continue ;
if ( DFHack : : isOpenTerrain ( below_ttype ) | | DFHack : : isFloorTerrain ( below_ttype ) ) {
// the tile below is not solid earth
// we're counting accessibility then dealing with 0 access
DEBUG ( manager ) . print ( " analysis: cave-in condition found \n " ) ;
INFO ( manager ) . print ( " (%d) checking accessibility of ( " COORD " ) from ( " COORD " ) \n " , cavein_possible , COORDARGS ( pos ) , COORDARGS ( miner_pos ) ) ;
auto access = count_accessibility ( miner_pos , pos ) ;
if ( access = = 0 ) {
TRACE ( groups ) . print ( " has 0 access \n " ) ;
if ( config . insta_dig ) {
manage_one ( pos , true , false ) ;
dig_now ( DFHack : : Core : : getInstance ( ) . getConsole ( ) , pos ) ;
} else {
// todo: engage dig management, swap channel designations for dig
}
} else {
WARN ( manager ) . print ( " has %d access \n " , access ) ;
cavein_possible = config . riskaverse ;
cavein_candidates . emplace ( pos , access ) ;
2023-06-25 18:44:06 -06:00
least_access = std : : min ( access , least_access ) ;
2022-12-08 12:37:28 -07:00
}
} else if ( config . insta_dig & & isEntombed ( miner_pos , pos ) ) {
manage_one ( pos , true , false ) ;
dig_now ( DFHack : : Core : : getInstance ( ) . getConsole ( ) , pos ) ;
}
}
DEBUG ( manager ) . print ( " cavein possible(%d) \n "
2022-12-08 15:59:09 -07:00
" %zu candidates \n "
" least access %d \n " , cavein_possible , cavein_candidates . size ( ) , least_access ) ;
2022-12-08 12:37:28 -07:00
}
// managing designations
if ( ! group . empty ( ) ) {
DEBUG ( manager ) . print ( " managing group #%d \n " , groups . debugGIndex ( * group . begin ( ) ) ) ;
}
for ( auto & pos : group ) {
// if no cave-in is possible [or we don't check for], we'll just execute normally and move on
if ( ! cavein_possible ) {
TRACE ( manager ) . print ( " cave-in evaluated false \n " ) ;
2023-02-08 13:03:40 -07:00
d_assert ( manage_one ( pos , true , marker_mode ) , " manage_one() is failing under !cavein " ) ;
2022-12-08 12:37:28 -07:00
continue ;
}
// cavein is only possible if marker_mode is false
2022-12-08 15:59:09 -07:00
// we want to dig the cavein candidates first, the least accessible ones specifically
2022-12-08 12:37:28 -07:00
const static uint8_t MAX = 84 ; //arbitrary number that indicates the value has changed
2022-12-08 15:59:09 -07:00
const static uint8_t OFFSET = 2 ; //value has been tweaked to avoid cave-ins whilst activating as many designations as possible
2022-12-08 12:37:28 -07:00
if ( CSP : : dignow_queue . count ( pos ) | | ( cavein_candidates . count ( pos ) & &
2022-12-08 15:59:09 -07:00
least_access < MAX & & cavein_candidates [ pos ] < = least_access + OFFSET ) ) {
2022-12-08 12:37:28 -07:00
2022-12-08 15:59:09 -07:00
TRACE ( manager ) . print ( " cave-in evaluated true and either of dignow or (%d <= %d) \n " , cavein_candidates [ pos ] , least_access + OFFSET ) ;
2022-12-08 12:37:28 -07:00
df : : coord local ( pos ) ;
local . x % = 16 ;
local . y % = 16 ;
auto block = Maps : : ensureTileBlock ( pos ) ;
2022-12-08 15:59:09 -07:00
// if we don't find the priority in block_events, it probably means bad things
2022-12-08 12:37:28 -07:00
for ( df : : block_square_event * event : block - > block_events ) {
if ( auto evT = virtual_cast < df : : block_square_event_designation_priorityst > ( event ) ) {
// we want to let the user keep some designations free of being managed
2023-06-25 18:44:06 -06:00
auto b = std : : max ( 0 , cavein_candidates [ pos ] - least_access ) ;
2022-12-08 12:37:28 -07:00
auto v = 1000 + ( b * 1700 ) ;
DEBUG ( manager ) . print ( " ( " COORD " ) 1000+1000(%d) -> %d {least-access: %d} \n " , COORDARGS ( pos ) , b , v , least_access ) ;
evT - > priority [ Coord ( local ) ] = v ;
}
}
2023-02-08 13:03:40 -07:00
d_assert ( manage_one ( pos , true , false ) , " manage_one() is failing for cavein " ) ;
2022-12-08 12:37:28 -07:00
continue ;
}
2022-12-08 15:59:09 -07:00
// cavein possible, but we failed to meet the criteria for activation
2022-12-08 12:37:28 -07:00
if ( cavein_candidates . count ( pos ) ) {
2023-02-08 13:03:40 -07:00
DEBUG ( manager ) . print ( " cave-in evaluated true and the cavein candidate's accessibility check was made as (%d <= %d) \n " , cavein_candidates [ pos ] , least_access + OFFSET ) ;
2022-12-08 12:37:28 -07:00
} else {
2023-02-08 13:03:40 -07:00
DEBUG ( manager ) . print ( " cave-in evaluated true and the position was not a candidate, nor was it set for dignow \n " ) ;
2022-12-08 12:37:28 -07:00
}
2023-02-08 13:03:40 -07:00
d_assert ( manage_one ( pos , true , true ) , " manage_one() is failing to set a cave-in causing designation to marker mode " ) ;
2022-11-06 01:12:35 -06:00
}
INFO ( manager ) . print ( " manage_group() is done \n " ) ;
}
2022-12-08 15:59:09 -07:00
bool ChannelManager : : manage_one ( const df : : coord & map_pos , bool set_marker_mode , bool marker_mode ) const {
2022-11-06 01:12:35 -06:00
if ( Maps : : isValidTilePos ( map_pos ) ) {
2022-12-08 12:21:17 -07:00
TRACE ( manager ) . print ( " manage_one(( " COORD " ), %d, %d) \n " , COORDARGS ( map_pos ) , set_marker_mode , marker_mode ) ;
2022-11-06 01:12:35 -06:00
df : : map_block * block = Maps : : getTileBlock ( map_pos ) ;
// we calculate the position inside the block*
df : : coord local ( map_pos ) ;
local . x = local . x % 16 ;
local . y = local . y % 16 ;
df : : tile_occupancy & tile_occupancy = block - > occupancy [ Coord ( local ) ] ;
// ensure that we aren't on the top-most layers
if ( map_pos . z < mapz - 3 ) {
// do we already know whether to set marker mode?
2022-12-08 15:59:09 -07:00
if ( ! marker_mode ) {
// marker_mode is set to true if it is unsafe to dig
marker_mode = ( ! set_marker_mode & &
( has_group_above ( groups , map_pos ) | | ! is_safe_to_dig_down ( map_pos ) ) ) | |
( set_marker_mode & &
is_channel_designation ( block - > designation [ Coord ( local ) ] ) & & ! is_safe_to_dig_down ( map_pos ) ) ;
}
if ( marker_mode ) {
if ( jobs . count ( map_pos ) ) {
cancel_job ( map_pos ) ;
2022-11-06 01:12:35 -06:00
}
2022-12-08 15:59:09 -07:00
} else if ( ! block - > flags . bits . designated ) {
block - > flags . bits . designated = true ;
2022-11-06 01:12:35 -06:00
}
2022-12-08 15:59:09 -07:00
tile_occupancy . bits . dig_marked = marker_mode ;
TRACE ( manager ) . print ( " marker mode %s \n " , marker_mode ? " ENABLED " : " DISABLED " ) ;
2022-11-06 01:12:35 -06:00
} else {
// if we are though, it should be totally safe to dig
tile_occupancy . bits . dig_marked = false ;
}
2022-12-08 12:21:17 -07:00
TRACE ( manager ) . print ( " <- manage_one() exits normally \n " ) ;
return true ;
2022-11-06 01:12:35 -06:00
}
return false ;
}
void ChannelManager : : mark_done ( const df : : coord & map_pos ) {
groups . remove ( map_pos ) ;
2022-11-06 16:59:30 -07:00
jobs . erase ( map_pos ) ;
2022-11-10 16:59:48 -07:00
CSP : : dignow_queue . erase ( map_pos ) ;
2022-11-06 16:59:30 -07:00
TileCache : : Get ( ) . uncache ( map_pos ) ;
2022-11-06 01:12:35 -06:00
}