forked from DFHack/dfhack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
filltraffic.cpp
391 lines (336 loc) · 11.4 KB
/
filltraffic.cpp
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
// Wide-area traffic designation utility.
// Flood-fill from cursor or fill entire map.
#include <ctype.h> //For toupper().
#include <algorithm> //for min().
#include <map>
#include <vector>
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "modules/Gui.h"
using std::stack;
using MapExtras::MapCache;
using namespace DFHack;
using namespace df::enums;
//Function pointer type for whole-map tile checks.
typedef void (*checkTile)(DFCoord, MapExtras::MapCache &);
//Forward Declarations for Commands
command_result filltraffic(color_ostream &out, std::vector<std::string> & params);
command_result alltraffic(color_ostream &out, std::vector<std::string> & params);
command_result restrictLiquid(color_ostream &out, std::vector<std::string> & params);
command_result restrictIce(color_ostream &out, std::vector<std::string> & params);
//Forward Declarations for Utility Functions
command_result setAllMatching(color_ostream &out, checkTile checkProc,
DFCoord minCoord = DFCoord(0, 0, 0),
DFCoord maxCoord = DFCoord(0xFFFF, 0xFFFF, 0xFFFF));
void allHigh(DFCoord coord, MapExtras::MapCache & map);
void allNormal(DFCoord coord, MapExtras::MapCache & map);
void allLow(DFCoord coord, MapExtras::MapCache & map);
void allRestricted(DFCoord coord, MapExtras::MapCache & map);
void restrictLiquidProc(DFCoord coord, MapExtras::MapCache &map);
void restrictIceProc(DFCoord coord, MapExtras::MapCache &map);
DFHACK_PLUGIN("filltraffic");
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"filltraffic",
"Flood-fill selected traffic designation from cursor.",
filltraffic, Gui::cursor_hotkey));
commands.push_back(PluginCommand(
"alltraffic",
"Set traffic designation for the entire map.",
alltraffic));
commands.push_back(PluginCommand(
"restrictliquids",
"Restrict traffic on every visible square with liquid.",
restrictLiquid));
commands.push_back(PluginCommand(
"restrictice",
"Restrict traffic on squares above visible ice.",
restrictIce));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
command_result filltraffic(color_ostream &out, std::vector<std::string> & params)
{
// HOTKEY COMMAND; CORE ALREADY SUSPENDED
//Maximum map size.
uint32_t x_max,y_max,z_max;
//Source and target traffic types.
df::tile_traffic source = tile_traffic::Normal;
df::tile_traffic target = tile_traffic::Normal;
//Option flags
bool updown = false;
bool checkpit = true;
bool checkbuilding = true;
//Loop through parameters
for(size_t i = 0; i < params.size();i++)
{
if (params[i] == "help" || params[i] == "?" || params[i].size() != 1)
return CR_WRONG_USAGE;
switch (toupper(params[i][0]))
{
case 'H':
target = tile_traffic::High; break;
case 'N':
target = tile_traffic::Normal; break;
case 'L':
target = tile_traffic::Low; break;
case 'R':
target = tile_traffic::Restricted; break;
case 'X':
updown = true; break;
case 'B':
checkbuilding = false; break;
case 'P':
checkpit = false; break;
default:
return CR_WRONG_USAGE;
}
}
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
int32_t cx, cy, cz;
Maps::getSize(x_max,y_max,z_max);
uint32_t tx_max = x_max * 16;
uint32_t ty_max = y_max * 16;
Gui::getCursorCoords(cx,cy,cz);
while(cx == -30000)
{
out.printerr("Cursor is not active.\n");
return CR_FAILURE;
}
DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz);
MapExtras::MapCache MCache;
df::tile_designation des = MCache.designationAt(xy);
df::tiletype tt = MCache.tiletypeAt(xy);
df::tile_occupancy oc;
if (checkbuilding)
oc = MCache.occupancyAt(xy);
source = (df::tile_traffic)des.bits.traffic;
if(source == target)
{
out.printerr("This tile is already set to the target traffic type.\n");
return CR_FAILURE;
}
if(isWallTerrain(tt))
{
out.printerr("This tile is a wall. Please select a passable tile.\n");
return CR_FAILURE;
}
if(checkpit && isOpenTerrain(tt))
{
out.printerr("This tile is a hole. Please select a passable tile.\n");
return CR_FAILURE;
}
if(checkbuilding && oc.bits.building)
{
out.printerr("This tile contains a building. Please select an empty tile.\n");
return CR_FAILURE;
}
out.print("%d/%d/%d ... FILLING!\n", cx,cy,cz);
//Naive four-way or six-way flood fill with possible tiles on a stack.
stack <DFCoord> flood;
flood.push(xy);
while(!flood.empty())
{
xy = flood.top();
flood.pop();
des = MCache.designationAt(xy);
if (des.bits.traffic != source) continue;
tt = MCache.tiletypeAt(xy);
if(isWallTerrain(tt)) continue;
if(checkpit && isOpenTerrain(tt)) continue;
if (checkbuilding)
{
oc = MCache.occupancyAt(xy);
if(oc.bits.building) continue;
}
//This tile is ready. Set its traffic level and add surrounding tiles.
if (MCache.testCoord(xy))
{
des.bits.traffic = target;
MCache.setDesignationAt(xy,des);
if (xy.x > 0)
{
flood.push(DFCoord(xy.x - 1, xy.y, xy.z));
}
if (xy.x < int32_t(tx_max) - 1)
{
flood.push(DFCoord(xy.x + 1, xy.y, xy.z));
}
if (xy.y > 0)
{
flood.push(DFCoord(xy.x, xy.y - 1, xy.z));
}
if (xy.y < int32_t(ty_max) - 1)
{
flood.push(DFCoord(xy.x, xy.y + 1, xy.z));
}
if (updown)
{
if (xy.z > 0 && LowPassable(tt))
{
flood.push(DFCoord(xy.x, xy.y, xy.z - 1));
}
if (xy.z < int32_t(z_max) && HighPassable(tt))
{
flood.push(DFCoord(xy.x, xy.y, xy.z + 1));
}
}
}
}
MCache.WriteAll();
return CR_OK;
}
enum e_checktype {no_check, check_equal, check_nequal};
command_result alltraffic(color_ostream &out, std::vector<std::string> & params)
{
void (*proc)(DFCoord, MapExtras::MapCache &) = allNormal;
//Loop through parameters
for(size_t i = 0; i < params.size();i++)
{
if (params[i] == "help" || params[i] == "?" || params[i].size() != 1)
return CR_WRONG_USAGE;
//Pick traffic type. Possibly set bounding rectangle later.
switch (toupper(params[i][0]))
{
case 'H':
proc = allHigh; break;
case 'N':
proc = allNormal; break;
case 'L':
proc = allLow; break;
case 'R':
proc = allRestricted; break;
default:
return CR_WRONG_USAGE;
}
}
return setAllMatching(out, proc);
}
command_result restrictLiquid(color_ostream &out, std::vector<std::string> & params)
{
return setAllMatching(out, restrictLiquidProc);
}
command_result restrictIce(color_ostream &out, std::vector<std::string> & params)
{
return setAllMatching(out, restrictIceProc);
}
//Helper function for writing new functions that check every tile on the map.
//newTraffic is the traffic designation to set.
//check takes a coordinate and the map cache as arguments, and returns true if the criteria is met.
//minCoord and maxCoord can be used to specify a bounding cube.
command_result setAllMatching(color_ostream &out, checkTile checkProc,
DFCoord minCoord, DFCoord maxCoord)
{
//Initialization.
CoreSuspender suspend;
if (!Maps::IsValid())
{
out.printerr("Map is not available!\n");
return CR_FAILURE;
}
//Maximum map size.
uint32_t x_max,y_max,z_max;
Maps::getSize(x_max,y_max,z_max);
uint32_t tx_max = x_max * 16;
uint32_t ty_max = y_max * 16;
//Ensure maximum coordinate is within map. Truncate to map edge.
maxCoord.x = std::min((uint32_t) maxCoord.x, tx_max);
maxCoord.y = std::min((uint32_t) maxCoord.y, ty_max);
maxCoord.z = std::min((uint32_t) maxCoord.z, z_max);
//Check minimum co-ordinates against maximum map size
if (minCoord.x > maxCoord.x)
{
out.printerr("Minimum x coordinate is greater than maximum x coordinate.\n");
return CR_FAILURE;
}
if (minCoord.y > maxCoord.y)
{
out.printerr("Minimum y coordinate is greater than maximum y coordinate.\n");
return CR_FAILURE;
}
if (minCoord.z > maxCoord.y)
{
out.printerr("Minimum z coordinate is greater than maximum z coordinate.\n");
return CR_FAILURE;
}
MapExtras::MapCache MCache;
out.print("Setting traffic...\n");
//Loop through every single tile
for(int32_t x = minCoord.x; x <= maxCoord.x; x++)
{
for(int32_t y = minCoord.y; y <= maxCoord.y; y++)
{
for(int32_t z = minCoord.z; z <= maxCoord.z; z++)
{
DFCoord tile = DFCoord(x, y, z);
checkProc(tile, MCache);
}
}
}
MCache.WriteAll();
out.print("Complete!\n");
return CR_OK;
}
//Unconditionally set map to target traffic type
void allHigh(DFCoord coord, MapExtras::MapCache &map)
{
df::tile_designation des = map.designationAt(coord);
des.bits.traffic = tile_traffic::High;
map.setDesignationAt(coord, des);
}
void allNormal(DFCoord coord, MapExtras::MapCache &map)
{
df::tile_designation des = map.designationAt(coord);
des.bits.traffic = tile_traffic::Normal;
map.setDesignationAt(coord, des);
}
void allLow(DFCoord coord, MapExtras::MapCache &map)
{
df::tile_designation des = map.designationAt(coord);
des.bits.traffic = tile_traffic::Low;
map.setDesignationAt(coord, des);
}
void allRestricted(DFCoord coord, MapExtras::MapCache &map)
{
df::tile_designation des = map.designationAt(coord);
des.bits.traffic = tile_traffic::Restricted;
map.setDesignationAt(coord, des);
}
//Restrict traffic if tile is visible and liquid is present.
void restrictLiquidProc(DFCoord coord, MapExtras::MapCache &map)
{
df::tile_designation des = map.designationAt(coord);
if ((des.bits.hidden == 0) && (des.bits.flow_size != 0))
{
des.bits.traffic = tile_traffic::Restricted;
map.setDesignationAt(coord, des);
}
}
//Restrict traffice if tile is above visible ice wall.
void restrictIceProc(DFCoord coord, MapExtras::MapCache &map)
{
//There is no ice below the bottom of the map.
if (coord.z == 0)
return;
DFCoord tile_below = DFCoord(coord.x, coord.y, coord.z - 1);
df::tiletype tt = map.tiletypeAt(tile_below);
df::tile_designation des = map.designationAt(tile_below);
if ((des.bits.hidden == 0) && (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID))
{
des = map.designationAt(coord);
des.bits.traffic = tile_traffic::Restricted;
map.setDesignationAt(coord, des);
}
}