11#include " Debug.h"
2+ #include " MemAccess.h"
23#include " PluginManager.h"
34#include " TileTypes.h"
45
6+ #include " modules/EventManager.h"
57#include " modules/Gui.h"
8+ #include " modules/Job.h"
69#include " modules/Maps.h"
710#include " modules/Screen.h"
811#include " modules/Textures.h"
912
13+ #include " df/block_square_event_designation_priorityst.h"
1014#include " df/init.h"
15+ #include " df/job_list_link.h"
1116#include " df/map_block.h"
1217#include " df/tile_designation.h"
18+ #include " df/world.h"
1319
1420#include < functional>
1521
@@ -21,6 +27,7 @@ REQUIRE_GLOBAL(init);
2127REQUIRE_GLOBAL (window_x);
2228REQUIRE_GLOBAL (window_y);
2329REQUIRE_GLOBAL (window_z);
30+ REQUIRE_GLOBAL (world);
2431
2532namespace DFHack {
2633 DBG_DECLARE (pathable, log, DebugCategory::LINFO);
@@ -209,8 +216,221 @@ static void paintScreenWarmDamp(bool show_hidden = false) {
209216 }
210217}
211218
219+ struct designation {
220+ df::coord pos;
221+ df::tile_designation td;
222+ df::tile_occupancy to;
223+ designation () = default ;
224+ designation (const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), td(td), to(to) {}
225+
226+ bool operator ==(const designation &rhs) const {
227+ return pos == rhs.pos ;
228+ }
229+
230+ bool operator !=(const designation &rhs) const {
231+ return !(rhs == *this );
232+ }
233+ };
234+
235+ namespace std {
236+ template <>
237+ struct hash <designation> {
238+ std::size_t operator ()(const designation &c) const {
239+ std::hash<df::coord> hash_coord;
240+ return hash_coord (c.pos );
241+ }
242+ };
243+ }
244+
245+ class Designations {
246+ private:
247+ std::unordered_map<df::coord, designation> designations;
248+ public:
249+ Designations () {
250+ df::job_list_link *link = world->jobs .list .next ;
251+ for (; link; link = link->next ) {
252+ df::job *job = link->item ;
253+
254+ if (!job || !Maps::isValidTilePos (job->pos ))
255+ continue ;
256+
257+ df::tile_designation td;
258+ df::tile_occupancy to;
259+ bool keep_if_taken = false ;
260+
261+ switch (job->job_type ) {
262+ case df::job_type::SmoothWall:
263+ case df::job_type::SmoothFloor:
264+ keep_if_taken = true ;
265+ // fallthrough
266+ case df::job_type::CarveFortification:
267+ td.bits .smooth = 1 ;
268+ break ;
269+ case df::job_type::DetailWall:
270+ case df::job_type::DetailFloor:
271+ td.bits .smooth = 2 ;
272+ break ;
273+ case job_type::CarveTrack:
274+ to.bits .carve_track_north = (job->item_category .whole >> 18 ) & 1 ;
275+ to.bits .carve_track_south = (job->item_category .whole >> 19 ) & 1 ;
276+ to.bits .carve_track_west = (job->item_category .whole >> 20 ) & 1 ;
277+ to.bits .carve_track_east = (job->item_category .whole >> 21 ) & 1 ;
278+ break ;
279+ default :
280+ continue ;
281+ }
282+ if (keep_if_taken || !Job::getWorker (job))
283+ designations.emplace (job->pos , designation (job->pos , td, to));
284+ }
285+ }
286+
287+ // get from job; if no job, then fall back to querying map
288+ designation get (const df::coord &pos) const {
289+ if (designations.count (pos)) {
290+ return designations.at (pos);
291+ }
292+ auto pdes = Maps::getTileDesignation (pos);
293+ auto pocc = Maps::getTileOccupancy (pos);
294+ if (!pdes || !pocc)
295+ return {};
296+ return designation (pos, *pdes, *pocc);
297+ }
298+ };
299+
300+ static bool is_designated_for_smoothing (const designation &designation) {
301+ return designation.td .bits .smooth == 1 ;
302+ }
303+
304+ static bool is_designated_for_engraving (const designation &designation) {
305+ return designation.td .bits .smooth == 2 ;
306+ }
307+
308+ static bool is_designated_for_track_carving (const designation &designation) {
309+ const df::tile_occupancy &occ = designation.to ;
310+ return occ.bits .carve_track_east || occ.bits .carve_track_north || occ.bits .carve_track_south || occ.bits .carve_track_west ;
311+ }
312+
313+ static char get_track_char (const designation &designation) {
314+ const df::tile_occupancy &occ = designation.to ;
315+ if (occ.bits .carve_track_east && occ.bits .carve_track_north && occ.bits .carve_track_south && occ.bits .carve_track_west )
316+ return (char )0xCE ; // NSEW
317+ if (occ.bits .carve_track_east && occ.bits .carve_track_north && occ.bits .carve_track_south )
318+ return (char )0xCC ; // NSE
319+ if (occ.bits .carve_track_east && occ.bits .carve_track_north && occ.bits .carve_track_west )
320+ return (char )0xCA ; // NEW
321+ if (occ.bits .carve_track_east && occ.bits .carve_track_south && occ.bits .carve_track_west )
322+ return (char )0xCB ; // SEW
323+ if (occ.bits .carve_track_north && occ.bits .carve_track_south && occ.bits .carve_track_west )
324+ return (char )0xB9 ; // NSW
325+ if (occ.bits .carve_track_north && occ.bits .carve_track_south )
326+ return (char )0xBA ; // NS
327+ if (occ.bits .carve_track_east && occ.bits .carve_track_west )
328+ return (char )0xCD ; // EW
329+ if (occ.bits .carve_track_east && occ.bits .carve_track_north )
330+ return (char )0xC8 ; // NE
331+ if (occ.bits .carve_track_north && occ.bits .carve_track_west )
332+ return (char )0xBC ; // NW
333+ if (occ.bits .carve_track_east && occ.bits .carve_track_south )
334+ return (char )0xC9 ; // SE
335+ if (occ.bits .carve_track_south && occ.bits .carve_track_west )
336+ return (char )0xBB ; // SW
337+ if (occ.bits .carve_track_north )
338+ return (char )0xD0 ; // N
339+ if (occ.bits .carve_track_south )
340+ return (char )0xD2 ; // S
341+ if (occ.bits .carve_track_east )
342+ return (char )0xC6 ; // E
343+ if (occ.bits .carve_track_west )
344+ return (char )0xB5 ; // W
345+ return (char )0xC5 ; // single line cross; should never happen
346+ }
347+
348+ static bool is_smooth_wall (const df::coord &pos) {
349+ df::tiletype *tt = Maps::getTileType (pos);
350+ return tt && tileSpecial (*tt) == df::tiletype_special::SMOOTH
351+ && tileShape (*tt) == df::tiletype_shape::WALL;
352+ }
353+
354+ static bool blink (int delay) {
355+ return (Core::getInstance ().p ->getTickCount ()/delay) % 2 == 0 ;
356+ }
357+
358+ static char get_tile_char (const df::coord &pos, char desig_char, bool draw_priority) {
359+ if (!draw_priority)
360+ return desig_char;
361+
362+ std::vector<df::block_square_event_designation_priorityst *> priorities;
363+ Maps::SortBlockEvents (Maps::getTileBlock (pos), NULL , NULL , NULL , NULL , NULL , NULL , NULL , &priorities);
364+ if (priorities.empty ())
365+ return desig_char;
366+ switch (priorities[0 ]->priority [pos.x % 16 ][pos.y % 16 ] / 1000 ) {
367+ case 1 : return ' 1' ;
368+ case 2 : return ' 2' ;
369+ case 3 : return ' 3' ;
370+ case 4 : return ' 4' ;
371+ case 5 : return ' 5' ;
372+ case 6 : return ' 6' ;
373+ case 7 : return ' 7' ;
374+ default :
375+ return ' 4' ;
376+ }
377+ }
378+
379+ static void paintScreenCarve () {
380+ DEBUG (log).print (" entering paintScreenCarve\n " );
381+
382+ if (Screen::inGraphicsMode () || blink (500 ))
383+ return ;
384+
385+ Designations designations;
386+ bool draw_priority = blink (1000 );
387+
388+ auto dims = Gui::getDwarfmodeViewDims ().map ();
389+ for (int y = dims.first .y ; y <= dims.second .y ; ++y) {
390+ for (int x = dims.first .x ; x <= dims.second .x ; ++x) {
391+ df::coord map_pos (*window_x + x, *window_y + y, *window_z);
392+
393+ if (!Maps::isValidTilePos (map_pos))
394+ continue ;
395+
396+ if (!Maps::isTileVisible (map_pos)) {
397+ TRACE (log).print (" skipping hidden tile\n " );
398+ continue ;
399+ }
400+
401+ TRACE (log).print (" scanning map tile at (%d, %d, %d) screen offset (%d, %d)\n " ,
402+ map_pos.x , map_pos.y , map_pos.z , x, y);
403+
404+ Screen::Pen cur_tile;
405+ cur_tile.fg = COLOR_DARKGREY;
406+
407+ auto des = designations.get (map_pos);
408+
409+ if (is_designated_for_smoothing (des)) {
410+ if (is_smooth_wall (map_pos))
411+ cur_tile.ch = get_tile_char (map_pos, (char )206 , draw_priority); // hash, indicating a fortification designation
412+ else
413+ cur_tile.ch = get_tile_char (map_pos, (char )219 , draw_priority); // solid block, indicating a smoothing designation
414+ }
415+ else if (is_designated_for_engraving (des)) {
416+ cur_tile.ch = get_tile_char (map_pos, (char )10 , draw_priority); // solid block with a circle on it
417+ }
418+ else if (is_designated_for_track_carving (des)) {
419+ cur_tile.ch = get_tile_char (map_pos, get_track_char (des), draw_priority); // directional track
420+ }
421+ else {
422+ TRACE (log).print (" skipping tile with no carving designation\n " );
423+ continue ;
424+ }
425+
426+ Screen::paintTile (cur_tile, x, y, true );
427+ }
428+ }
429+ }
430+
212431DFHACK_PLUGIN_LUA_FUNCTIONS {
213432 DFHACK_LUA_FUNCTION (paintScreenPathable),
214433 DFHACK_LUA_FUNCTION (paintScreenWarmDamp),
434+ DFHACK_LUA_FUNCTION (paintScreenCarve),
215435 DFHACK_LUA_END
216436};
0 commit comments