Merge branch 'develop' into myk_dwarfvet

develop
Myk 2023-07-21 14:23:13 -07:00 committed by GitHub
commit 65fc79c5e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 35 deletions

@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `3dveins`: reinstated for v50, this plugin replaces vanilla DF's blobby vein generation with veins that flow smoothly and naturally between z-levels - `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 - `zone`: new searchable, sortable, filterable screen for assigning units to pastures
- `dwarfvet`: reinstated and updated for v50's new hospital mechanics; allow your animals to have their wounds treated at hospitals - `dwarfvet`: reinstated and updated for v50's new hospital mechanics; allow your animals to have their wounds treated at hospitals
- `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 ## Fixes
- Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing - Fix extra keys appearing in DFHack text boxes when shift (or any other modifier) is released before the other key you were pressing

@ -45,6 +45,7 @@ this::
info.txt info.txt
graphics/... graphics/...
objects/... objects/...
blueprints/...
scripts_modactive/example-mod.lua scripts_modactive/example-mod.lua
scripts_modactive/internal/example-mod/... scripts_modactive/internal/example-mod/...
scripts_modinstalled/... scripts_modinstalled/...
@ -58,6 +59,9 @@ Let's go through that line by line.
- Modifications to the game raws (potentially with custom raw tokens) go in - Modifications to the game raws (potentially with custom raw tokens) go in
the :file:`graphics/` and :file:`objects/` folders. You can read more about the :file:`graphics/` and :file:`objects/` folders. You can read more about
the files that go in these directories on the :wiki:`Modding` wiki page. the files that go in these directories on the :wiki:`Modding` wiki page.
- Any `quickfort` blueprints included with your mod go in the
:file:`blueprints` folder. Note that your mod can *just* be blueprints and
nothing else if you like.
- A control script in :file:`scripts_modactive/` directory that handles - A control script in :file:`scripts_modactive/` directory that handles
system-level event hooks (e.g. reloading state when a world is loaded), system-level event hooks (e.g. reloading state when a world is loaded),
registering `overlays <overlay-dev-guide>`, and registering `overlays <overlay-dev-guide>`, and

@ -179,3 +179,12 @@ Filters:
Take current designation and apply the selected pattern to it. Take current designation and apply the selected pattern to it.
After you have a pattern set, you can use ``expdig`` to apply it again. 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. Box selection characters and the keyboard cursor will also
change color as appropriate when over the warm or damp tile.

@ -74,7 +74,7 @@ function list()
end end
--------------------- ---------------------
-- mod script paths -- mod paths
-- this perhaps could/should be queried from the Steam API -- this perhaps could/should be queried from the Steam API
-- are there any installation configurations where this will be wrong, though? -- are there any installation configurations where this will be wrong, though?
@ -98,8 +98,8 @@ local function get_mod_id_and_version(path)
end end
if not version then if not version then
-- note this doesn't include the closing brace since some people put -- note this doesn't include the closing brace since some people put
-- non-number characters in here, and DF only reads the digits as the -- non-number characters in here, and DF only reads the leading digits
-- numeric version -- as the numeric version
_,_,version = line:find('^%[NUMERIC_VERSION:(%d+)') _,_,version = line:find('^%[NUMERIC_VERSION:(%d+)')
end end
-- note that we do *not* want to break out of this loop early since -- note that we do *not* want to break out of this loop early since
@ -108,37 +108,31 @@ local function get_mod_id_and_version(path)
return id, version return id, version
end end
local function add_script_path(mod_script_paths, path) local function add_mod_paths(mod_paths, id, base_path, subdir)
local sep = base_path:endswith('/') and '' or '/'
local path = ('%s%s%s'):format(base_path, sep, subdir)
if dfhack.filesystem.isdir(path) then if dfhack.filesystem.isdir(path) then
print('indexing mod scripts: ' .. path) print('indexing mod path: ' .. path)
table.insert(mod_script_paths, path) table.insert(mod_paths, {id=id, path=path})
end end
end end
local function add_script_paths(mod_script_paths, base_path, include_modactive) function get_mod_paths(installed_subdir, active_subdir)
if not base_path:endswith('/') then
base_path = base_path .. '/'
end
if include_modactive then
add_script_path(mod_script_paths, base_path..'scripts_modactive')
end
add_script_path(mod_script_paths, base_path..'scripts_modinstalled')
end
function get_mod_script_paths()
-- ordered map of mod id -> {handled=bool, versions=map of version -> path} -- ordered map of mod id -> {handled=bool, versions=map of version -> path}
local mods = utils.OrderedTable() local mods = utils.OrderedTable()
local mod_script_paths = {} local mod_paths = {}
-- if a world is loaded, process active mods first, and lock to active version -- if a world is loaded, process active mods first, and lock to active version
if dfhack.isWorldLoaded() then if active_subdir and dfhack.isWorldLoaded() then
for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do
path = tostring(path.value) path = tostring(path.value)
-- skip vanilla "mods"
if not path:startswith(INSTALLED_MODS_PATH) then goto continue end if not path:startswith(INSTALLED_MODS_PATH) then goto continue end
local id = get_mod_id_and_version(path) local id = get_mod_id_and_version(path)
if not id then goto continue end if not id then goto continue end
mods[id] = {handled=true} mods[id] = {handled=true}
add_script_paths(mod_script_paths, path, true) add_mod_paths(mod_paths, id, path, active_subdir)
add_mod_paths(mod_paths, id, path, installed_subdir)
::continue:: ::continue::
end end
end end
@ -159,8 +153,8 @@ function get_mod_script_paths()
::skip_path_root:: ::skip_path_root::
end end
-- add script paths from most recent version of all not-yet-handled mods -- add paths from most recent version of all not-yet-handled mods
for _,v in pairs(mods) do for id,v in pairs(mods) do
if v.handled then goto continue end if v.handled then goto continue end
local max_version, path local max_version, path
for version,mod_path in pairs(v.versions) do for version,mod_path in pairs(v.versions) do
@ -169,11 +163,19 @@ function get_mod_script_paths()
max_version = version max_version = version
end end
end end
add_script_paths(mod_script_paths, path) add_mod_paths(mod_paths, id, path, installed_subdir)
::continue:: ::continue::
end end
return mod_script_paths return mod_paths
end
function get_mod_script_paths()
local paths = {}
for _,v in ipairs(get_mod_paths('scripts_modinstalled', 'scripts_modactive')) do
table.insert(paths, v.path)
end
return paths
end end
return _ENV return _ENV

@ -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 = {asciiwarmdamp=WarmDampOverlay}
return _ENV

@ -1,23 +1,26 @@
#include "Debug.h"
#include "PluginManager.h"
#include "TileTypes.h"
#include "modules/Gui.h" #include "modules/Gui.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/Screen.h" #include "modules/Screen.h"
#include "modules/Textures.h" #include "modules/Textures.h"
#include "Debug.h"
#include "LuaTools.h"
#include "PluginManager.h"
#include "df/init.h" #include "df/init.h"
#include "df/map_block.h"
#include "df/tile_designation.h"
#include <functional>
using namespace DFHack; using namespace DFHack;
DFHACK_PLUGIN("pathable"); DFHACK_PLUGIN("pathable");
REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(init);
REQUIRE_GLOBAL(window_x); REQUIRE_GLOBAL(window_x);
REQUIRE_GLOBAL(window_y); REQUIRE_GLOBAL(window_y);
REQUIRE_GLOBAL(window_z); REQUIRE_GLOBAL(window_z);
REQUIRE_GLOBAL(world);
namespace DFHack { namespace DFHack {
DBG_DECLARE(pathable, log, DebugCategory::LINFO); DBG_DECLARE(pathable, log, DebugCategory::LINFO);
@ -31,7 +34,7 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out) {
return CR_OK; 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"); DEBUG(log).print("entering paintScreen\n");
bool use_graphics = Screen::inGraphicsMode(); bool use_graphics = Screen::inGraphicsMode();
@ -39,8 +42,8 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) {
int selected_tile_texpos = 0; int selected_tile_texpos = 0;
Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos);
long pathable_tile_texpos = df::global::init->load_bar_texpos[1]; long pathable_tile_texpos = init->load_bar_texpos[1];
long unpathable_tile_texpos = df::global::init->load_bar_texpos[4]; long unpathable_tile_texpos = init->load_bar_texpos[4];
long on_off_texpos = Textures::getMapPathableTexposStart(); long on_off_texpos = Textures::getMapPathableTexposStart();
if (on_off_texpos > 0) { if (on_off_texpos > 0) {
pathable_tile_texpos = on_off_texpos + 0; pathable_tile_texpos = on_off_texpos + 0;
@ -61,7 +64,7 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) {
continue; continue;
} }
if (skip_unrevealed && !Maps::isTileVisible(map_pos)) { if (!show_hidden && !Maps::isTileVisible(map_pos)) {
TRACE(log).print("skipping hidden tile\n"); TRACE(log).print("skipping hidden tile\n");
continue; continue;
} }
@ -110,7 +113,101 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) {
} }
} }
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;
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;
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;
}
// 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;
} 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_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(paintScreen), DFHACK_LUA_FUNCTION(paintScreenPathable),
DFHACK_LUA_FUNCTION(paintScreenWarmDamp),
DFHACK_LUA_END DFHACK_LUA_END
}; };