From 237075080924cc01cd45ad5c7d3b4939a96ab5eb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:17:09 -0700 Subject: [PATCH 1/7] add warm/damp highlight overlay for ascii mode --- docs/plugins/dig.rst | 8 +++ plugins/lua/dig.lua | 29 +++++++++ plugins/pathable.cpp | 148 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 plugins/lua/dig.lua diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index a10db97dfb..46238add68 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -179,3 +179,11 @@ Filters: Take current designation and apply the selected pattern to it. After you have a pattern set, you can use ``expdig`` to apply it again. + +Overlay +------- + +This tool also provides an overlay that is managed by the `overlay` framework. +When the ``dig.asciiwarmdamp`` overlay is enabled and you are in non-graphics +(ASCII) mode, warm tiles will be highlighted in red and damp tiles will be +highlighted in blue. diff --git a/plugins/lua/dig.lua b/plugins/lua/dig.lua new file mode 100644 index 0000000000..c9b8cf62be --- /dev/null +++ b/plugins/lua/dig.lua @@ -0,0 +1,29 @@ +local _ENV = mkmodule('plugins.dig') + +local overlay = require('plugins.overlay') +local pathable = require('plugins.pathable') + +WarmDampOverlay = defclass(WarmDampOverlay, overlay.OverlayWidget) +WarmDampOverlay.ATTRS{ + viewscreens={ + 'dwarfmode/Designate/DIG_DIG', + 'dwarfmode/Designate/DIG_REMOVE_STAIRS_RAMPS', + 'dwarfmode/Designate/DIG_STAIR_UP', + 'dwarfmode/Designate/DIG_STAIR_UPDOWN', + 'dwarfmode/Designate/DIG_STAIR_DOWN', + 'dwarfmode/Designate/DIG_RAMP', + 'dwarfmode/Designate/DIG_CHANNEL', + 'dwarfmode/Designate/DIG_FROM_MARKER', + 'dwarfmode/Designate/DIG_TO_MARKER', + }, + default_enabled=true, + overlay_only=true, +} + +function WarmDampOverlay:onRenderFrame(dc) + pathable.paintScreenWarmDamp() +end + +OVERLAY_WIDGETS = {overlay=WarmDampOverlay} + +return _ENV diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index 6ed8903250..a7f6af0cd2 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -1,23 +1,27 @@ +#include "Debug.h" +#include "PluginManager.h" +#include "TileTypes.h" + #include "modules/Gui.h" #include "modules/Maps.h" #include "modules/Screen.h" #include "modules/Textures.h" -#include "Debug.h" -#include "LuaTools.h" -#include "PluginManager.h" - #include "df/init.h" +#include "df/map_block.h" +#include "df/tile_designation.h" + +#include using namespace DFHack; DFHACK_PLUGIN("pathable"); -REQUIRE_GLOBAL(gps); +REQUIRE_GLOBAL(init); +REQUIRE_GLOBAL(selection_rect); REQUIRE_GLOBAL(window_x); REQUIRE_GLOBAL(window_y); REQUIRE_GLOBAL(window_z); -REQUIRE_GLOBAL(world); namespace DFHack { DBG_DECLARE(pathable, log, DebugCategory::LINFO); @@ -31,7 +35,7 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out) { return CR_OK; } -static void paintScreen(df::coord target, bool skip_unrevealed = false) { +static void paintScreenPathable(df::coord target, bool show_hidden = false) { DEBUG(log).print("entering paintScreen\n"); bool use_graphics = Screen::inGraphicsMode(); @@ -39,8 +43,8 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { int selected_tile_texpos = 0; Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); - long pathable_tile_texpos = df::global::init->load_bar_texpos[1]; - long unpathable_tile_texpos = df::global::init->load_bar_texpos[4]; + long pathable_tile_texpos = init->load_bar_texpos[1]; + long unpathable_tile_texpos = init->load_bar_texpos[4]; long on_off_texpos = Textures::getMapPathableTexposStart(); if (on_off_texpos > 0) { pathable_tile_texpos = on_off_texpos + 0; @@ -61,7 +65,7 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { continue; } - if (skip_unrevealed && !Maps::isTileVisible(map_pos)) { + if (!show_hidden && !Maps::isTileVisible(map_pos)) { TRACE(log).print("skipping hidden tile\n"); continue; } @@ -110,7 +114,129 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { } } +static bool init_mouse_selection_rect(rect2d &rect) { + df::coord mouse_pos = Gui::getMousePos(); + if (!mouse_pos.isValid()) + return false; + rect.first.x = std::min(selection_rect->start_x, (int32_t)mouse_pos.x); + rect.second.x = std::max(selection_rect->start_x, (int32_t)mouse_pos.x); + rect.first.y = std::min(selection_rect->start_y, (int32_t)mouse_pos.y); + rect.second.y = std::max(selection_rect->start_y, (int32_t)mouse_pos.y); + return true; +} + +static bool in_mouse_selection_rect(const rect2d &rect, const df::coord &pos) { + return ((pos.y == rect.first.y || pos.y == rect.second.y) && (pos.x >= rect.first.x || pos.x <= rect.second.x)) || + ((pos.x == rect.first.x || pos.x == rect.second.x) && (pos.y >= rect.first.y || pos.y <= rect.second.y)); +} + +static bool is_warm(const df::coord &pos) { + auto block = Maps::getTileBlock(pos); + if (!block) + return false; + return block->temperature_1[pos.x&15][pos.y&15] >= 10075; +} + +static bool is_rough_wall(int16_t x, int16_t y, int16_t z) { + df::tiletype *tt = Maps::getTileType(x, y, z); + if (!tt) + return false; + + return tileShape(*tt) == df::tiletype_shape::WALL && + tileSpecial(*tt) != df::tiletype_special::SMOOTH; +} + +static bool will_leak(int16_t x, int16_t y, int16_t z) { + auto des = Maps::getTileDesignation(x, y, z); + if (!des) + return false; + if (des->bits.liquid_type == df::tile_liquid::Water && des->bits.flow_size >= 1) + return true; + if (des->bits.water_table && is_rough_wall(x, y, z)) + return true; + return false; +} + +static bool is_damp(const df::coord &pos) { + return will_leak(pos.x-1, pos.y-1, pos.z) || + will_leak(pos.x, pos.y-1, pos.z) || + will_leak(pos.x+1, pos.y-1, pos.z) || + will_leak(pos.x-1, pos.y, pos.z) || + will_leak(pos.x+1, pos.y, pos.z) || + will_leak(pos.x-1, pos.y+1, pos.z) || + will_leak(pos.x, pos.y+1, pos.z) || + will_leak(pos.x+1, pos.y+1, pos.z); + will_leak(pos.x, pos.y+1, pos.z+1); +} + +static void paintScreenWarmDamp(bool show_hidden = false) { + DEBUG(log).print("entering paintScreenDampWarm\n"); + + if (Screen::inGraphicsMode()) + return; + + bool has_mouse_selection_rect = selection_rect->start_x >= 0; + rect2d mouse_sel_rect; + if (has_mouse_selection_rect) { + has_mouse_selection_rect = init_mouse_selection_rect(mouse_sel_rect); + } + + bool has_kbd_selection_rect = false; // TODO where is this info stored? + + auto dims = Gui::getDwarfmodeViewDims().map(); + for (int y = dims.first.y; y <= dims.second.y; ++y) { + for (int x = dims.first.x; x <= dims.second.x; ++x) { + df::coord map_pos(*window_x + x, *window_y + y, *window_z); + + if (!Maps::isValidTilePos(map_pos)) + continue; + + // don't overwrite selection box tiles + if (has_mouse_selection_rect && in_mouse_selection_rect(mouse_sel_rect, map_pos)) { + TRACE(log).print("skipping mouse selection box tile\n"); + continue; + } + + if (!show_hidden && !Maps::isTileVisible(map_pos)) { + TRACE(log).print("skipping hidden tile\n"); + continue; + } + + TRACE(log).print("scanning map tile at (%d, %d, %d) screen offset (%d, %d)\n", + map_pos.x, map_pos.y, map_pos.z, x, y); + + Screen::Pen cur_tile = Screen::readTile(x, y, true); + if (!cur_tile.valid()) { + DEBUG(log).print("cannot read tile at offset %d, %d\n", x, y); + continue; + } + + int color = is_warm(map_pos) ? COLOR_RED : is_damp(map_pos) ? COLOR_BLUE : COLOR_BLACK; + if (color == COLOR_BLACK) { + TRACE(log).print("skipping non-warm, non-damp tile\n"); + continue; + } + + if (cur_tile.fg && cur_tile.ch != ' ') { + cur_tile.fg = color; + cur_tile.bg = 0; + } else { + cur_tile.fg = 0; + cur_tile.bg = color; + } + + cur_tile.bold = false; + + if (cur_tile.tile) + cur_tile.tile_mode = Screen::Pen::CharColor; + + Screen::paintTile(cur_tile, x, y, true); + } + } +} + DFHACK_PLUGIN_LUA_FUNCTIONS { - DFHACK_LUA_FUNCTION(paintScreen), + DFHACK_LUA_FUNCTION(paintScreenPathable), + DFHACK_LUA_FUNCTION(paintScreenWarmDamp), DFHACK_LUA_END }; From 040d2caa956796a1363fe0f0ccc95b1cae199799 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:22:30 -0700 Subject: [PATCH 2/7] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 97bc202fee..92d6aae4d0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins - `3dveins`: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels - `zone`: new searchable, sortable, filterable screen for assigning units to pastures +- `dig`: new ``dig.asciiwarmdamp`` overlay that highlights warm and damp tiles when in ASCII mode. there is no effect in graphics mode since the tiles are already highlighted there ## Fixes - Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing From a6b304d9b4e02d4ee998d52c3fc25acc8b65b5ca Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:28:49 -0700 Subject: [PATCH 3/7] fix box select bounds logic --- plugins/pathable.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index a7f6af0cd2..f7bbe2e766 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -126,8 +126,13 @@ static bool init_mouse_selection_rect(rect2d &rect) { } static bool in_mouse_selection_rect(const rect2d &rect, const df::coord &pos) { - return ((pos.y == rect.first.y || pos.y == rect.second.y) && (pos.x >= rect.first.x || pos.x <= rect.second.x)) || - ((pos.x == rect.first.x || pos.x == rect.second.x) && (pos.y >= rect.first.y || pos.y <= rect.second.y)); + return ((pos.y == rect.first.y || pos.y == rect.second.y) && (pos.x >= rect.first.x && pos.x <= rect.second.x)) || + ((pos.x == rect.first.x || pos.x == rect.second.x) && (pos.y >= rect.first.y && pos.y <= rect.second.y)); +} + +static bool in_kbd_selection_rect(const rect2d &rect, const df::coord &pos) { + return pos.y >= rect.first.y && pos.y <= rect.second.y && + pos.x >= rect.first.x && pos.x <= rect.second.x; } static bool is_warm(const df::coord &pos) { @@ -182,6 +187,7 @@ static void paintScreenWarmDamp(bool show_hidden = false) { } bool has_kbd_selection_rect = false; // TODO where is this info stored? + rect2d kbd_sel_rect; auto dims = Gui::getDwarfmodeViewDims().map(); for (int y = dims.first.y; y <= dims.second.y; ++y) { @@ -196,6 +202,10 @@ static void paintScreenWarmDamp(bool show_hidden = false) { TRACE(log).print("skipping mouse selection box tile\n"); continue; } + if (has_kbd_selection_rect && in_kbd_selection_rect(kbd_sel_rect, map_pos)){ + TRACE(log).print("skipping keyboard selection box tile\n"); + continue; + } if (!show_hidden && !Maps::isTileVisible(map_pos)) { TRACE(log).print("skipping hidden tile\n"); From d18a1f12f73ab8a0001417b0e675595dfeb881f7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:32:38 -0700 Subject: [PATCH 4/7] allow the color to override box select and cursor --- plugins/pathable.cpp | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index f7bbe2e766..b4c7f703f4 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -114,27 +114,6 @@ static void paintScreenPathable(df::coord target, bool show_hidden = false) { } } -static bool init_mouse_selection_rect(rect2d &rect) { - df::coord mouse_pos = Gui::getMousePos(); - if (!mouse_pos.isValid()) - return false; - rect.first.x = std::min(selection_rect->start_x, (int32_t)mouse_pos.x); - rect.second.x = std::max(selection_rect->start_x, (int32_t)mouse_pos.x); - rect.first.y = std::min(selection_rect->start_y, (int32_t)mouse_pos.y); - rect.second.y = std::max(selection_rect->start_y, (int32_t)mouse_pos.y); - return true; -} - -static bool in_mouse_selection_rect(const rect2d &rect, const df::coord &pos) { - return ((pos.y == rect.first.y || pos.y == rect.second.y) && (pos.x >= rect.first.x && pos.x <= rect.second.x)) || - ((pos.x == rect.first.x || pos.x == rect.second.x) && (pos.y >= rect.first.y && pos.y <= rect.second.y)); -} - -static bool in_kbd_selection_rect(const rect2d &rect, const df::coord &pos) { - return pos.y >= rect.first.y && pos.y <= rect.second.y && - pos.x >= rect.first.x && pos.x <= rect.second.x; -} - static bool is_warm(const df::coord &pos) { auto block = Maps::getTileBlock(pos); if (!block) @@ -180,15 +159,6 @@ static void paintScreenWarmDamp(bool show_hidden = false) { if (Screen::inGraphicsMode()) return; - bool has_mouse_selection_rect = selection_rect->start_x >= 0; - rect2d mouse_sel_rect; - if (has_mouse_selection_rect) { - has_mouse_selection_rect = init_mouse_selection_rect(mouse_sel_rect); - } - - bool has_kbd_selection_rect = false; // TODO where is this info stored? - rect2d kbd_sel_rect; - auto dims = Gui::getDwarfmodeViewDims().map(); for (int y = dims.first.y; y <= dims.second.y; ++y) { for (int x = dims.first.x; x <= dims.second.x; ++x) { @@ -197,16 +167,6 @@ static void paintScreenWarmDamp(bool show_hidden = false) { if (!Maps::isValidTilePos(map_pos)) continue; - // don't overwrite selection box tiles - if (has_mouse_selection_rect && in_mouse_selection_rect(mouse_sel_rect, map_pos)) { - TRACE(log).print("skipping mouse selection box tile\n"); - continue; - } - if (has_kbd_selection_rect && in_kbd_selection_rect(kbd_sel_rect, map_pos)){ - TRACE(log).print("skipping keyboard selection box tile\n"); - continue; - } - if (!show_hidden && !Maps::isTileVisible(map_pos)) { TRACE(log).print("skipping hidden tile\n"); continue; @@ -227,6 +187,8 @@ static void paintScreenWarmDamp(bool show_hidden = false) { continue; } + // this will also change the color of the cursor or any selection box that overlaps + // the tile. this is intentional since it makes the UI more readable if (cur_tile.fg && cur_tile.ch != ' ') { cur_tile.fg = color; cur_tile.bg = 0; From 25301bf93e36b4056ce82d2a0cfc2a4327d26b8f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:35:02 -0700 Subject: [PATCH 5/7] update docs --- docs/plugins/dig.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index 46238add68..310bd16471 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -186,4 +186,5 @@ Overlay This tool also provides an overlay that is managed by the `overlay` framework. When the ``dig.asciiwarmdamp`` overlay is enabled and you are in non-graphics (ASCII) mode, warm tiles will be highlighted in red and damp tiles will be -highlighted in blue. +highlighted in blue. Box selection characters and the keyboard cursor will also +change color as appropriate when over the warm or damp tile. From a77a6b5943d36c9aadd6bed38e177b1b59a79024 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 20 Jul 2023 19:40:40 -0700 Subject: [PATCH 6/7] clean up globals --- plugins/pathable.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index b4c7f703f4..f604694897 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -18,7 +18,6 @@ using namespace DFHack; DFHACK_PLUGIN("pathable"); REQUIRE_GLOBAL(init); -REQUIRE_GLOBAL(selection_rect); REQUIRE_GLOBAL(window_x); REQUIRE_GLOBAL(window_y); REQUIRE_GLOBAL(window_z); From d14054716c634580a97ae514a60fcac6630c11e1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 21 Jul 2023 00:28:05 -0700 Subject: [PATCH 7/7] better name for overlay --- plugins/lua/dig.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/dig.lua b/plugins/lua/dig.lua index c9b8cf62be..43012d5d2a 100644 --- a/plugins/lua/dig.lua +++ b/plugins/lua/dig.lua @@ -24,6 +24,6 @@ function WarmDampOverlay:onRenderFrame(dc) pathable.paintScreenWarmDamp() end -OVERLAY_WIDGETS = {overlay=WarmDampOverlay} +OVERLAY_WIDGETS = {asciiwarmdamp=WarmDampOverlay} return _ENV