forked from DFHack/dfhack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchannel-manager.cpp
More file actions
207 lines (194 loc) · 9.71 KB
/
Copy pathchannel-manager.cpp
File metadata and controls
207 lines (194 loc) · 9.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include <channel-manager.h>
#include <tile-cache.h>
#include <inlines.h>
#include <modules/EventManager.h> //hash function for df::coord
#include <df/block_square_event_designation_priorityst.h>
#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); \
}
df::unit* find_dwarf(const df::coord &map_pos) {
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;
}
// sets mark flags as necessary, for all designations
void ChannelManager::manage_groups() {
INFO(manager).print("manage_groups()\n");
// 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");
}
void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) const {
INFO(manager).print("manage_group()\n");
if (!set_marker_mode) {
marker_mode = has_any_groups_above(groups, group);
}
// 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);
} 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 = std::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);
}
}
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");
d_assert(manage_one(pos, true, marker_mode), "manage_one() is failing under !cavein");
continue;
}
// cavein is only possible if marker_mode is false
// we want to dig the cavein candidates first, the least accessible ones specifically
const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed
const static uint8_t OFFSET = 2; //value has been tweaked to avoid cave-ins whilst activating as many designations as possible
if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) &&
least_access < MAX && cavein_candidates[pos] <= least_access+OFFSET)) {
TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET);
df::coord local(pos);
local.x %= 16;
local.y %= 16;
auto block = Maps::ensureTileBlock(pos);
// if we don't find the priority in block_events, it probably means bad things
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 = std::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;
}
}
d_assert(manage_one(pos, true, false), "manage_one() is failing for cavein ");
continue;
}
// cavein possible, but we failed to meet the criteria for activation
if (cavein_candidates.count(pos)) {
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);
} else {
DEBUG(manager).print("cave-in evaluated true and the position was not a candidate, nor was it set for dignow\n");
}
d_assert(manage_one(pos, true, true), "manage_one() is failing to set a cave-in causing designation to marker mode");
}
INFO(manager).print("manage_group() is done\n");
}
bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) const {
if (Maps::isValidTilePos(map_pos)) {
TRACE(manager).print("manage_one((" COORD "), %d, %d)\n", COORDARGS(map_pos), set_marker_mode, marker_mode);
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?
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);
}
} else if (!block->flags.bits.designated) {
block->flags.bits.designated = true;
}
tile_occupancy.bits.dig_marked = marker_mode;
TRACE(manager).print("marker mode %s\n", marker_mode ? "ENABLED" : "DISABLED");
} else {
// if we are though, it should be totally safe to dig
tile_occupancy.bits.dig_marked = false;
}
TRACE(manager).print(" <- manage_one() exits normally\n");
return true;
}
return false;
}
void ChannelManager::mark_done(const df::coord &map_pos) {
groups.remove(map_pos);
jobs.erase(map_pos);
CSP::dignow_queue.erase(map_pos);
TileCache::Get().uncache(map_pos);
}