Implements plugin: channel-safely v1.2

- Updates rst documentation
  - Adds feature: risk-averse
- Revises ChannelManager::manage_group
  - Now performs analysis of group designations
    - If any designation has fall space, designations are analyzed for accessibility (a weighted score of how many ways it can be accessed)
    - If a designation has no fall space, but cannot be accessed it will be "dig_now"'ed
    - accessibility scores are stored for the management phase
  - Management loop has been extended
    - iff no cave-in candidates exist, then perform simple management (as requested)
    - if candidates do exist, then we must check if our current position is one
      - if the current position is a cave-in candidate it must also be within range (+2) of the least access
        - if the candidate is in range or on the dignow queue, then we activate the designation and modify the dig priority according to distance from least_access
      - if not a candidate, or the other checks failed, then we set the designation to marker mode
develop
Josh Cooper 2022-12-08 11:37:28 -08:00
parent 540faff88f
commit a4bf266770
2 changed files with 93 additions and 2 deletions

@ -58,6 +58,8 @@ Features
--------
:require-vision: Toggle whether the dwarves need vision of a tile before channeling to it can be deemed unsafe. (default: enabled)
:risk-averse: Toggles whether to use cave-in prevention. Designations are activated in stages
and their priorities along edges are modified. (default: enabled)
:monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled)
:resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled
units who die while digging. (default: disabled)

@ -57,8 +57,97 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool
if (!set_marker_mode) {
marker_mode = has_any_groups_above(groups, group);
}
for (auto &designation: group) {
manage_one(designation, true, marker_mode);
// cavein prevention
bool cavein_possible = false;
uint8_t least_access = 100;
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);
mark_done(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);
least_access = min(access, least_access);
}
} else if (config.insta_dig && isEntombed(miner_pos, pos)) {
manage_one(pos, true, false);
dig_now(DFHack::Core::getInstance().getConsole(), pos);
mark_done(pos);
}
}
DEBUG(manager).print("cavein possible(%d)\n"
"%zu candidates\n"
"least access %d\n", cavein_possible, cavein_candidates.size(), least_access);
}
// 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");
assert(manage_one(pos, true, marker_mode));
continue;
}
// cavein is only possible if marker_mode is false
const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed
if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) &&
least_access < MAX && cavein_candidates[pos] <= least_access+2)) {
TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+2);
// we want to dig the cavein candidates first
df::coord local(pos);
local.x %= 16;
local.y %= 16;
auto block = Maps::ensureTileBlock(pos);
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
auto b = max(0, cavein_candidates[pos] - least_access);
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;
}
}
assert(manage_one(pos, true, false));
continue;
}
if (cavein_candidates.count(pos)) {
DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access + 2);
} else {
DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n");
}
assert(manage_one(pos, true, true));
}
INFO(manager).print("manage_group() is done\n");
}