diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c0835a4..186fabf4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,8 @@ IF(CMAKE_CROSSCOMPILING) INCLUDE("${DFHACK_NATIVE_BUILD_DIR}/ImportExecutables.cmake") ENDIF() +find_package(Perl REQUIRED) + # set up folder structures for IDE solutions # MSVC Express won't load solutions that use this. It also doesn't include MFC supported # Check for MFC! @@ -137,7 +139,7 @@ endif() # set up versioning. set(DF_VERSION "0.43.05") -SET(DFHACK_RELEASE "beta1") +SET(DFHACK_RELEASE "beta2") SET(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/NEWS.rst b/NEWS.rst index 4229da17f..7f7695254 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -14,7 +14,7 @@ Internals Lua - New [Internal Commands | Plugins | Scripts | Features] + New [Internal Commands | Plugins | Scripts | Tweaks | Features] Fixes Misc Improvements Removed @@ -35,17 +35,21 @@ Changelog .. contents:: :depth: 2 -DFHack future -============= +DFHack 0.43.05-r1 +================= Internals --------- - 64-bit support on all platforms -- Visual Studio 2015 now required on Windows instead of 2010 -- GCC 4.8 or newer required on Linux and OS X (and now supported on OS X) - Several structure fixes to match 64-bit DF's memory layout - Added ``DFHack::Job::removeJob()`` function +- New module: ``Designations`` - handles designation creation (currently for plants only) +- Added ``Gui::getSelectedPlant()`` +- Added ``Units::getMainSocialActivity()``, ``Units::getMainSocialEvent()`` +- Visual Studio 2015 now required to build on Windows instead of 2010 +- GCC 4.8 or newer required to build on Linux and OS X (and now supported on OS X) - Updated TinyXML from 2.5.3 to 2.6.2 +- Added the ability to download files manually before building Lua --- @@ -56,6 +60,8 @@ Lua - ``df.new()`` supports more types: ``char``, ``intptr_t``, ``uintptr_t``, ``long``, ``unsigned long`` - String representations of vectors and a few other containers now include their lengths - Added a ``tile-material`` module +- Added a ``Painter:key_string()`` method +- Made ``dfhack.gui.revealInDwarfmodeMap()`` available Ruby ---- @@ -71,34 +77,63 @@ New Plugins New Scripts ----------- +- `adv-rumors`: improves the "Bring up specific incident or rumor" menu in adventure mode - `fix/tile-occupancy`: Clears bad occupancy flags on the selected tile. +- `install-info`: Logs basic troubleshooting information about the current DFHack installation - `load-save`: loads a save non-interactively - `modtools/change-build-menu`: Edit the build mode sidebar menus - `modtools/if-entity`: Run a command if the current entity matches a given ID - `season-palette`: Swap color palettes with the changes of the seasons +- `unforbid`: Unforbids all items + +New Tweaks +---------- +- `tweak condition-material `: fixes a crash in the work order condition material list +- `tweak hotkey-clear `: adds an option to clear bindings from DF hotkeys Fixes ----- - The DF path on OS X can now contain spaces and ``:`` characters - Buildings::setOwner() changes now persist properly when saved - ``ls`` now lists scripts in folders other than ``hack/scripts``, when applicable +- Fixed ``plug`` output alignment for plugins with long names - `add-thought`: fixed support for emotion names -- `autofarm`: Made surface farms detect local biome +- `autochop`: + + - fixed several issues with job creation and removal + - stopped designating the center tile (unreachable) for large trees + - stopped options from moving when enabling and disabling burrows + - fixed display of unnamed burrows + - `devel/find-offsets`: fixed a crash when vtables used by globals aren't available +- `getplants`: + + - fixed several issues with job creation and removal + - stopped designating the center tile (unreachable) for large trees + +- `gui/workflow`: added extra keybinding to work with `gui/extended-status` - `manipulator`: - Fixed crash when selecting a profession from an empty list - Custom professions are now sorted alphabetically more reliably -- `modtools/create-unit`: stopped permanently overwriting the creature creation - menu in arena mode +- `modtools/create-unit`: + + - stopped permanently overwriting the creature creation menu in arena mode + - now uses non-English names + +- `modtools/item-trigger`: fixed errors with plant growths +- `remotefortressreader`: fixed a crash when serializing the local map - `title-version`: now hidden when loading an arena Misc Improvements ----------------- - Documented all default keybindings (from :file:`dfhack.init-example`) in the docs for the relevant commands; updates enforced by build system. +- `autounsuspend`: reduced update frequency to address potential performance issues +- `gui/extended-status`: added a feature to queue beds - `lua` and `gui/gm-editor` now support the same aliases (``scr``, ``unit``, etc.) +- `manipulator`: added social activities to job column - `remotefortressreader`: Added support for - world map snow coverage @@ -106,8 +141,11 @@ Misc Improvements - wall info - site towers, world buildings - surface material + - building items + - DF version info - `title-version`: Added a prerelease indicator +- `workflow`: Re-added ``Alt-W`` keybindings DFHack 0.43.03-r1 ================= diff --git a/depends/gunzip.pl b/depends/gunzip.pl index 4a21daafd..caabad5a8 100755 --- a/depends/gunzip.pl +++ b/depends/gunzip.pl @@ -18,4 +18,4 @@ if (-f $out_file and !exists($args{'--force'})) { die "output file exists, not overwriting: \"$out_file\""; } -gunzip $in_file => $out_file or die "gunzip failed: $GunzipError\n"; +gunzip $in_file => $out_file, BinModeOut => 1 or die "gunzip failed: $GunzipError\n"; diff --git a/dfhack.init-example b/dfhack.init-example index f8878ea0a..19bef37e6 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -135,6 +135,12 @@ keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path # workshop job details keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job +# workflow front-end +keybinding add Alt-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow +keybinding add Alt-W@overallstatus "gui/workflow status" +# equivalent to the one above when gui/extended-status is enabled +keybinding add Alt-W@dfhack/lua/status_overlay "gui/workflow status" + # autobutcher front-end keybinding add Shift-B@pet/List/Unit "gui/autobutcher" @@ -184,6 +190,9 @@ tweak import-priority-category # Fixes a crash in the work order contition material list (bug 9905). tweak condition-material +# Adds an option to clear currently-bound hotkeys +tweak hotkey-clear + # Misc. UI tweaks tweak block-labors # Prevents labors that can't be used from being toggled tweak civ-view-agreement diff --git a/docs/Authors.rst b/docs/Authors.rst index c606fbe38..034ff8635 100644 --- a/docs/Authors.rst +++ b/docs/Authors.rst @@ -12,6 +12,7 @@ Name Github Other 8Z 8Z acwatkins acwatkins Alexander Gavrilov angavrilov ag +Amostubal Amostubal AndreasPK AndreasPK Angus Mezick amezick Antalia tamarakorr @@ -75,6 +76,7 @@ Nick Rart nickrart comestible Nikolay Amiantov abbradar nocico nocico Omniclasm +Paul Fenwick pjf PeridexisErrant PeridexisErrant Petr Mrázek peterix potato @@ -87,7 +89,9 @@ rampaging-poet Raoul van Putten Raoul XQ raoulxq reverb +Rich Rauenzahn rrauenza Rinin Rinin +rndmvar rndmvar Robert Heinrich rh73 Robert Janetzko robertjanetzko rofl0r rofl0r @@ -107,6 +111,7 @@ Simon Jackson sizeak stolencatkarma sv-esk sv-esk Tacomagic +TheHologram TheHologram Tim Walberg twalberg Timothy Collett danaris Tom Jobbins TheBloke diff --git a/docs/Lua API.rst b/docs/Lua API.rst index 2db98abad..1c08407ae 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -921,6 +921,10 @@ Gui module Returns the building selected via :kbd:`q`, :kbd:`t`, :kbd:`k` or :kbd:`i`. +* ``dfhack.gui.getSelectedPlant([silent])`` + + Returns the plant selected via :kbd:`k`. + * ``dfhack.gui.writeToGamelog(text)`` Writes a string to :file:`gamelog.txt` without doing an announcement. diff --git a/docs/NEWS-dev.rst b/docs/NEWS-dev.rst index df74735c0..2be774672 100644 --- a/docs/NEWS-dev.rst +++ b/docs/NEWS-dev.rst @@ -37,6 +37,57 @@ Development Changelog .. contents:: :depth: 2 +DFHack 0.43.05-beta2 +==================== + +Fixes +----- +- Fixed Buildings::updateBuildings(), along with building creation/deletion events +- Fixed ``plug`` output alignment for plugins with long names +- Fixed a crash that happened when a ``LUA_PATH`` environment variable was set +- `add-thought`: fixed number conversion +- `gui/workflow`: fixed range editing producing the wrong results for certain numbers +- `modtools/create-unit`: now uses non-English names +- `modtools/item-trigger`: fixed errors with plant growths +- `remotefortressreader`: fixed a crash when serializing the local map +- `stockflow`: fixed an issue with non-integer manager order limits +- `title-folder`: fixed compatibility issues with certain SDL libraries on macOS + +Structures +---------- +- Added some missing renderer VTable addresses on macOS +- ``entity.resources.organic``: identified ``parchment`` +- ``entity_sell_category``: added ``Parchment`` and ``CupsMugsGoblets`` +- ``ui_advmode_menu``: added ``Build`` +- ``ui_unit_view_mode``: added ``PrefOccupation`` +- ``unit_skill``: identified ``natural_skill_lvl`` (was ``unk_1c``) +- ``viewscreen_jobmanagementst``: identified ``max_workshops`` +- ``viewscreen_overallstatusst``: made ``visible_pages`` an enum +- ``viewscreen_pricest``: identified fields +- ``viewscreen_workquota_conditionst``: gave some fields ``unk`` names + +API Changes +----------- +- Allowed the Lua API to accept integer-like floats and strings when expecting an integer +- Lua: New ``Painter:key_string()`` method +- Lua: Added ``dfhack.getArchitecture()`` and ``dfhack.getArchitectureName()`` + +Additions/Removals: +------------------- +- Added `adv-rumors` script: improves the "Bring up specific incident or rumor" menu in adventure mode +- Added `install-info` script for basic troubleshooting +- Added `tweak condition-material `: fixes a crash in the work order condition material list +- Added `tweak hotkey-clear `: adds an option to clear bindings from DF hotkeys +- `autofarm`: reverted local biome detection (from 0.43.05-alpha3) + +Other Changes +------------- +- Added a DOWNLOAD_RUBY CMake option, to allow use of a system/external ruby library +- Added the ability to download files manually before building +- `gui/extended-status`: added a feature to queue beds +- `remotefortressreader`: added building items, DF version info +- `stonesense`: Added support for 64-bit macOS and Linux + DFHack 0.43.05-beta1 ==================== diff --git a/docs/Plugins.rst b/docs/Plugins.rst index c81b284c3..4b5985ae7 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -287,6 +287,7 @@ Subcommands that persist until disabled or DF quits: the current item (fully, in case of a stack), and scroll down one line. :fps-min: Fixes the in-game minimum FPS setting :hide-priority: Adds an option to hide designation priority indicators +:hotkey-clear: Adds an option to clear currently-bound hotkeys (in the :kbd:`H` menu) :import-priority-category: Allows changing the priority of all goods in a category when discussing an import agreement with the liaison @@ -1656,13 +1657,14 @@ quotas. Open the dashboard by running:: - getplants autochop + enable autochop -The plugin must be activated (with ``c``) before it can be used. You can then set logging quotas -and restrict designations to specific burrows (with 'Enter') if desired. The plugin's activity -cycle runs once every in game day. +The plugin must be activated (with :kbd:`d`-:kbd:`t`-:kbd:`c`-:kbd:`a`) before +it can be used. You can then set logging quotas and restrict designations to +specific burrows (with 'Enter') if desired. The plugin's activity cycle runs +once every in game day. -If you add ``enable getplants`` to your dfhack.init there will be a hotkey to +If you add ``enable autochop`` to your dfhack.init there will be a hotkey to open the dashboard from the chop designation menu. diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 9f8477993..033c82053 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -16,8 +16,6 @@ ENDIF() include_directories (proto) include_directories (include) -SET(PERL_EXECUTABLE "perl" CACHE FILEPATH "This is the perl executable to run in the codegen step. Tweak it if you need to run a specific one.") - execute_process(COMMAND ${PERL_EXECUTABLE} xml/list.pl xml ${dfapi_SOURCE_DIR}/include/df ";" WORKING_DIRECTORY ${dfapi_SOURCE_DIR} OUTPUT_VARIABLE GENERATED_HDRS) @@ -115,36 +113,39 @@ SET(MODULE_HEADERS include/modules/Buildings.h include/modules/Burrows.h include/modules/Constructions.h -include/modules/Units.h +include/modules/Designations.h include/modules/Engravings.h include/modules/EventManager.h +include/modules/Filesystem.h +include/modules/Graphic.h include/modules/Gui.h include/modules/GuiHooks.h include/modules/Items.h include/modules/Job.h include/modules/kitchen.h -include/modules/Maps.h include/modules/MapCache.h +include/modules/Maps.h include/modules/Materials.h include/modules/Notes.h +include/modules/Once.h include/modules/Random.h include/modules/Renderer.h include/modules/Screen.h include/modules/Translation.h +include/modules/Units.h include/modules/Vermin.h include/modules/World.h -include/modules/Graphic.h -include/modules/Once.h -include/modules/Filesystem.h ) SET( MODULE_SOURCES modules/Buildings.cpp modules/Burrows.cpp modules/Constructions.cpp -modules/Units.cpp +modules/Designations.cpp modules/Engravings.cpp modules/EventManager.cpp +modules/Filesystem.cpp +modules/Graphic.cpp modules/Gui.cpp modules/Items.cpp modules/Job.cpp @@ -153,16 +154,15 @@ modules/MapCache.cpp modules/Maps.cpp modules/Materials.cpp modules/Notes.cpp +modules/Once.cpp modules/Random.cpp modules/Renderer.cpp modules/Screen.cpp modules/Translation.cpp +modules/Units.cpp modules/Vermin.cpp -modules/World.cpp -modules/Graphic.cpp modules/Windows.cpp -modules/Once.cpp -modules/Filesystem.cpp +modules/World.cpp ) SET(STATIC_FIELDS_FILES) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index a2446c78a..266677045 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -56,12 +56,15 @@ distribution. #include "modules/Constructions.h" #include "modules/Random.h" #include "modules/Filesystem.h" +#include "modules/Designations.h" #include "LuaWrapper.h" #include "LuaTools.h" #include "MiscUtils.h" +#include "df/activity_entry.h" +#include "df/activity_event.h" #include "df/job.h" #include "df/job_item.h" #include "df/building.h" @@ -90,6 +93,7 @@ distribution. #include "df/itemdef.h" #include "df/enabler.h" #include "df/feature_init.h" +#include "df/plant.h" #include #include @@ -1449,6 +1453,7 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedBuilding), + WRAPM(Gui, getSelectedPlant), WRAPM(Gui, writeToGamelog), WRAPM(Gui, makeAnnouncement), WRAPM(Gui, addCombatReport), @@ -1457,6 +1462,7 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, showZoomAnnouncement), WRAPM(Gui, showPopupAnnouncement), WRAPM(Gui, showAutoAnnouncement), + WRAPM(Gui, revealInDwarfmodeMap), { NULL, NULL } }; @@ -1580,6 +1586,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isUndead), WRAPM(Units, isGelded), WRAPM(Units, isDomesticated), + WRAPM(Units, getMainSocialActivity), + WRAPM(Units, getMainSocialEvent), { NULL, NULL } }; @@ -2309,6 +2317,27 @@ static const luaL_Reg dfhack_filesystem_funcs[] = { {NULL, NULL} }; +/***** Designations module *****/ + +static const LuaWrapper::FunctionReg dfhack_designations_module[] = { + WRAPM(Designations, markPlant), + WRAPM(Designations, unmarkPlant), + WRAPM(Designations, canMarkPlant), + WRAPM(Designations, canUnmarkPlant), + WRAPM(Designations, isPlantMarked), + {NULL, NULL} +}; + +static int designations_getPlantDesignationTile(lua_State *state) +{ + return Lua::PushPosXYZ(state, Designations::getPlantDesignationTile(Lua::CheckDFObject(state, 1))); +} + +static const luaL_Reg dfhack_designations_funcs[] = { + {"getPlantDesignationTile", designations_getPlantDesignationTile}, + {NULL, NULL} +}; + /***** Internal module *****/ static void *checkaddr(lua_State *L, int idx, bool allow_null = false) @@ -2787,5 +2816,6 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "constructions", dfhack_constructions_module); OpenModule(state, "screen", dfhack_screen_module, dfhack_screen_funcs); OpenModule(state, "filesystem", dfhack_filesystem_module, dfhack_filesystem_funcs); + OpenModule(state, "designations", dfhack_designations_module, dfhack_designations_funcs); OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs); } diff --git a/library/git-describe.cmake b/library/git-describe.cmake index 9a22617ad..3fb3095ff 100644 --- a/library/git-describe.cmake +++ b/library/git-describe.cmake @@ -1,10 +1,10 @@ -execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long +execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" OUTPUT_VARIABLE DFHACK_GIT_COMMIT) -execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match +execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match WORKING_DIRECTORY "${dfhack_SOURCE_DIR}" RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT OUTPUT_QUIET ERROR_QUIET) diff --git a/library/include/modules/Designations.h b/library/include/modules/Designations.h new file mode 100644 index 000000000..914605e58 --- /dev/null +++ b/library/include/modules/Designations.h @@ -0,0 +1,21 @@ +#pragma once + +namespace df { + struct plant; +} + +namespace DFHack { + namespace Designations { + // Mark or un-mark a plant (e.g. fell trees, gather plants) + // Return value indicates whether the plant's designation was changed or not + // (This can be false if markPlant() is called on an already-designated plant, for example) + DFHACK_EXPORT bool markPlant(const df::plant *plant); + DFHACK_EXPORT bool unmarkPlant(const df::plant *plant); + DFHACK_EXPORT bool canMarkPlant(const df::plant *plant); + DFHACK_EXPORT bool canUnmarkPlant(const df::plant *plant); + DFHACK_EXPORT bool isPlantMarked(const df::plant *plant); + + // Return the tile that should be designated for this plant + DFHACK_EXPORT df::coord getPlantDesignationTile(const df::plant *plant); + } +} diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index ae53e7603..7ea4d0e48 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -45,6 +45,7 @@ namespace df { struct job; struct unit; struct item; + struct plant; }; /** @@ -104,6 +105,11 @@ namespace DFHack DFHACK_EXPORT df::building *getAnyBuilding(df::viewscreen *top); DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); + // A plant is selected, e.g. via 'k' + DFHACK_EXPORT bool any_plant_hotkey(df::viewscreen *top); + DFHACK_EXPORT df::plant *getAnyPlant(df::viewscreen *top); + DFHACK_EXPORT df::plant *getSelectedPlant(color_ostream &out, bool quiet = false); + // Low-level API that gives full control over announcements and reports DFHACK_EXPORT void writeToGamelog(std::string message); diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 2c4a29062..4e53b0b54 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -44,6 +44,7 @@ namespace df struct item; struct unit; struct building; + struct plant; } /** @@ -326,10 +327,11 @@ namespace DFHack virtual std::string getFocusString() = 0; virtual void onShow() {}; virtual void onDismiss() {}; - virtual df::unit *getSelectedUnit() { return NULL; } - virtual df::item *getSelectedItem() { return NULL; } - virtual df::job *getSelectedJob() { return NULL; } - virtual df::building *getSelectedBuilding() { return NULL; } + virtual df::unit *getSelectedUnit() { return nullptr; } + virtual df::item *getSelectedItem() { return nullptr; } + virtual df::job *getSelectedJob() { return nullptr; } + virtual df::building *getSelectedBuilding() { return nullptr; } + virtual df::plant *getSelectedPlant() { return nullptr; } }; class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen { @@ -369,5 +371,6 @@ namespace DFHack virtual df::item *getSelectedItem(); virtual df::job *getSelectedJob(); virtual df::building *getSelectedBuilding(); + virtual df::plant *getSelectedPlant(); }; } diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 79b8ff18c..08baf98d6 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -41,6 +41,8 @@ distribution. namespace df { + struct activity_entry; + struct activity_event; struct nemesis_record; struct burrow; struct identity; @@ -301,6 +303,10 @@ DFHACK_EXPORT int8_t getProfessionColor(df::unit *unit, bool ignore_noble = fals DFHACK_EXPORT int8_t getCasteProfessionColor(int race, int caste, df::profession pid); DFHACK_EXPORT std::string getSquadName(df::unit *unit); + +DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); +DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); + } } #endif diff --git a/library/lua/makeown.lua b/library/lua/makeown.lua index bf3d8077c..2b16db1df 100644 --- a/library/lua/makeown.lua +++ b/library/lua/makeown.lua @@ -144,14 +144,14 @@ function make_citizen(unit) hf.caste = unit.caste hf.sex = unit.sex hf.appeared_year = dfg.cur_year - hf.born_year = unit.relations.birth_year - hf.born_seconds = unit.relations.birth_time - hf.curse_year = unit.relations.curse_year - hf.curse_seconds = unit.relations.curse_time - hf.anon_1 = unit.relations.anon_2 - hf.anon_2 = unit.relations.anon_3 - hf.old_year = unit.relations.old_year - hf.old_seconds = unit.relations.old_time + hf.born_year = unit.birth_year + hf.born_seconds = unit.birth_time + hf.curse_year = unit.curse_year + hf.curse_seconds = unit.curse_time + hf.birth_year_bias=unit.bias_birth_bias + hf.birth_time_bias=unit.birth_time_bias + hf.old_year = unit.old_year + hf.old_seconds = unit.old_time hf.died_year = -1 hf.died_seconds = -1 hf.name:assign(unit.name) @@ -299,4 +299,4 @@ end -return _ENV \ No newline at end of file +return _ENV diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 4800042f0..07db41808 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -635,6 +635,8 @@ function df_shortcut_var(k) return dfhack.gui.getSelectedWorkshopJob() elseif k == 'unit' then return dfhack.gui.getSelectedUnit() + elseif k == 'plant' then + return dfhack.gui.getSelectedPlant() else for g in pairs(df.global) do if g == k then diff --git a/library/modules/Designations.cpp b/library/modules/Designations.cpp new file mode 100644 index 000000000..3fb325096 --- /dev/null +++ b/library/modules/Designations.cpp @@ -0,0 +1,153 @@ +#include "DataDefs.h" +#include "Error.h" + +#include "modules/Designations.h" +#include "modules/Job.h" +#include "modules/Maps.h" + +#include "df/job.h" +#include "df/map_block.h" +#include "df/plant.h" +#include "df/plant_tree_info.h" +#include "df/plant_tree_tile.h" +#include "df/tile_dig_designation.h" +#include "df/world.h" + +using namespace DFHack; +using namespace df::enums; + +using df::global::world; + +static df::map_block *getPlantBlock(const df::plant *plant) +{ + if (!world) + return nullptr; + return Maps::getTileBlock(Designations::getPlantDesignationTile(plant)); +} + +df::coord Designations::getPlantDesignationTile(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + if (!plant->tree_info) + return plant->pos; + + int dimx = plant->tree_info->dim_x; + int dimy = plant->tree_info->dim_y; + int cx = dimx / 2; + int cy = dimy / 2; + + // Find the southeast trunk tile + int x = cx; + int y = cy; + + while (x + 1 < dimx && y + 1 < dimy) + { + if (plant->tree_info->body[0][(y * dimx) + (x + 1)].bits.trunk) + ++x; + else if (plant->tree_info->body[0][((y + 1) * dimx) + x].bits.trunk) + ++y; + else + break; + } + + return df::coord(plant->pos.x - cx + x, plant->pos.y - cy + y, plant->pos.z); +} + +bool Designations::isPlantMarked(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + df::coord des_pos = getPlantDesignationTile(plant); + df::map_block *block = Maps::getTileBlock(des_pos); + if (!block) + return false; + + if (block->designation[des_pos.x % 16][des_pos.y % 16].bits.dig == tile_dig_designation::Default) + return true; + + for (auto *link = world->job_list.next; link; link = link->next) + { + df::job *job = link->item; + if (!job) + continue; + if (job->job_type != job_type::FellTree && job->job_type != job_type::GatherPlants) + continue; + if (job->pos == des_pos) + return true; + } + return false; +} + +bool Designations::canMarkPlant(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + if (!getPlantBlock(plant)) + return false; + + return !isPlantMarked(plant); +} + +bool Designations::markPlant(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + if (canMarkPlant(plant)) + { + df::coord des_pos = getPlantDesignationTile(plant); + df::map_block *block = Maps::getTileBlock(des_pos); + block->designation[des_pos.x % 16][des_pos.y % 16].bits.dig = tile_dig_designation::Default; + block->flags.bits.designated = true; + return true; + } + else + { + return false; + } +} + +bool Designations::canUnmarkPlant(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + if (!getPlantBlock(plant)) + return false; + + return isPlantMarked(plant); +} + +bool Designations::unmarkPlant(const df::plant *plant) +{ + CHECK_NULL_POINTER(plant); + + if (canUnmarkPlant(plant)) + { + df::coord des_pos = getPlantDesignationTile(plant); + df::map_block *block = Maps::getTileBlock(des_pos); + block->designation[des_pos.x % 16][des_pos.y % 16].bits.dig = tile_dig_designation::No; + block->flags.bits.designated = true; + + auto *link = world->job_list.next; + while (link) + { + auto *next = link->next; + df::job *job = link->item; + + if (job && + (job->job_type == job_type::FellTree || job->job_type == job_type::GatherPlants) && + job->pos == des_pos) + { + Job::removeJob(job); + } + + link = next; + } + + return true; + } + else + { + return false; + } +} diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 9a86e69ec..5ad569c5a 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -92,6 +92,7 @@ using namespace DFHack; #include "df/game_mode.h" #include "df/unit.h" #include "df/occupation.h" +#include "df/plant.h" using namespace df::enums; using df::global::gview; @@ -1120,6 +1121,50 @@ df::building *Gui::getSelectedBuilding(color_ostream &out, bool quiet) return building; } +df::plant *Gui::getAnyPlant(df::viewscreen *top) +{ + using df::global::cursor; + using df::global::ui; + using df::global::world; + + if (auto dfscreen = dfhack_viewscreen::try_cast(top)) + return dfscreen->getSelectedPlant(); + + if (Gui::dwarfmode_hotkey(top)) + { + if (!cursor || !ui || !world) + return nullptr; + + if (ui->main.mode == ui_sidebar_mode::LookAround) + { + for (df::plant *plant : world->plants.all) + { + if (plant->pos.x == cursor->x && plant->pos.y == cursor->y && plant->pos.z == cursor->z) + { + return plant; + } + } + } + } + + return nullptr; +} + +bool Gui::any_plant_hotkey(df::viewscreen *top) +{ + return getAnyPlant(top) != nullptr; +} + +df::plant *Gui::getSelectedPlant(color_ostream &out, bool quiet) +{ + df::plant *plant = getAnyPlant(Core::getTopViewscreen()); + + if (!plant && !quiet) + out.printerr("No plant is selected in the UI.\n"); + + return plant; +} + // DFHACK_EXPORT void Gui::writeToGamelog(std::string message) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 71103ffda..0b9a500fa 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -57,6 +57,7 @@ using namespace DFHack; #include "df/job.h" #include "df/building.h" #include "df/renderer.h" +#include "df/plant.h" using namespace df::enums; using df::global::init; @@ -934,3 +935,11 @@ df::building *dfhack_lua_viewscreen::getSelectedBuilding() safe_call_lua(do_notify, 1, 1); return Lua::GetDFObject(Lua::Core::State, -1); } + +df::plant *dfhack_lua_viewscreen::getSelectedPlant() +{ + Lua::StackUnwinder frame(Lua::Core::State); + lua_pushstring(Lua::Core::State, "onGetSelectedPlant"); + safe_call_lua(do_notify, 1, 1); + return Lua::GetDFObject(Lua::Core::State, -1); +} diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 0596d529f..94b11b87e 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -48,6 +48,7 @@ using namespace std; #include "Core.h" #include "MiscUtils.h" +#include "df/activity_entry.h" #include "df/burrow.h" #include "df/caste_raw.h" #include "df/creature_raw.h" @@ -1831,6 +1832,24 @@ std::string Units::getSquadName(df::unit *unit) return Translation::TranslateName(&squad->name, true); } +df::activity_entry *Units::getMainSocialActivity(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + if (unit->social_activities.empty()) + return nullptr; + + return df::activity_entry::find(unit->social_activities[unit->social_activities.size() - 1]); +} + +df::activity_event *Units::getMainSocialEvent(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + df::activity_entry *entry = getMainSocialActivity(unit); + if (!entry || entry->events.empty()) + return nullptr; + return entry->events[entry->events.size() - 1]; +} + bool Units::isMerchant(df::unit* unit) { CHECK_NULL_POINTER(unit); diff --git a/library/xml b/library/xml index 9b834c089..b48397cf5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9b834c089efb4657d43a8fa4f8f0822e8224e576 +Subproject commit b48397cf5b4c954a098151943f7314a1a6eacf90 diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 73a3da24e..dbc79af42 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -10,24 +10,26 @@ #include "DataDefs.h" #include "TileTypes.h" -#include "df/world.h" -#include "df/map_block.h" -#include "df/tile_dig_designation.h" -#include "df/plant_raw.h" -#include "df/plant.h" -#include "df/ui.h" #include "df/burrow.h" -#include "df/item_flags.h" #include "df/item.h" +#include "df/item_flags.h" #include "df/items_other_id.h" +#include "df/job.h" +#include "df/map_block.h" +#include "df/plant.h" +#include "df/plant_raw.h" +#include "df/tile_dig_designation.h" +#include "df/ui.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/world.h" -#include "modules/Screen.h" -#include "modules/Maps.h" #include "modules/Burrows.h" -#include "modules/World.h" -#include "modules/MapCache.h" +#include "modules/Designations.h" #include "modules/Gui.h" +#include "modules/MapCache.h" +#include "modules/Maps.h" +#include "modules/Screen.h" +#include "modules/World.h" #include @@ -228,38 +230,33 @@ static int do_chop_designation(bool chop, bool count_only) if (!count_only && !watchedBurrows.isValidPos(plant->pos)) continue; - bool dirty = false; - if (chop && cur->designation[x][y].bits.dig == tile_dig_designation::No) + if (chop && !Designations::isPlantMarked(plant)) { if (count_only) { - ++count; + if (Designations::canMarkPlant(plant)) + count++; } else { - cur->designation[x][y].bits.dig = tile_dig_designation::Default; - dirty = true; + if (Designations::markPlant(plant)) + count++; } } - if (!chop && cur->designation[x][y].bits.dig == tile_dig_designation::Default) + if (!chop && Designations::isPlantMarked(plant)) { if (count_only) { - ++count; + if (Designations::canUnmarkPlant(plant)) + count++; } else { - cur->designation[x][y].bits.dig = tile_dig_designation::No; - dirty = true; + if (Designations::unmarkPlant(plant)) + count++; } } - - if (dirty) - { - cur->flags.bits.designated = true; - ++count; - } } return count; @@ -390,10 +387,12 @@ public: auto last_selected_index = burrows_column.highlighted_index; burrows_column.clear(); - for (auto iter = ui->burrows.list.begin(); iter != ui->burrows.list.end(); iter++) + for (df::burrow *burrow : ui->burrows.list) { - df::burrow* burrow = *iter; - auto elem = ListEntry(burrow->name, burrow); + string name = burrow->name; + if (name.empty()) + name = "Burrow " + int_to_string(burrow->id + 1); + auto elem = ListEntry(name, burrow); elem.selected = watchedBurrows.isBurrowWatched(burrow); burrows_column.add(elem); } @@ -590,6 +589,7 @@ public: if (burrows_column.getSelectedElems().size() > 0) { OutputString(COLOR_GREEN, x, y, "Will chop in selected burrows", true, left_margin); + ++y; } else { diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 87fc35960..2cf382d01 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -1,5 +1,7 @@ // (un)designate matching plants for gathering/cutting +#include + #include "Core.h" #include "Console.h" #include "Export.h" @@ -7,18 +9,19 @@ #include "DataDefs.h" #include "TileTypes.h" -#include "df/world.h" #include "df/map_block.h" -#include "df/tile_dig_designation.h" -#include "df/plant_raw.h" #include "df/plant.h" +#include "df/plant_raw.h" +#include "df/tile_dig_designation.h" +#include "df/world.h" +#include "modules/Designations.h" #include "modules/Maps.h" -#include using std::string; using std::vector; using std::set; + using namespace DFHack; using namespace df::enums; @@ -129,20 +132,14 @@ command_result df_getplants (color_ostream &out, vector & parameters) continue; if (cur->designation[x][y].bits.hidden) continue; - if (deselect && cur->designation[x][y].bits.dig == tile_dig_designation::Default) + if (deselect && Designations::unmarkPlant(plant)) { - cur->designation[x][y].bits.dig = tile_dig_designation::No; - dirty = true; ++count; } - if (!deselect && cur->designation[x][y].bits.dig == tile_dig_designation::No) + if (!deselect && Designations::markPlant(plant)) { - cur->designation[x][y].bits.dig = tile_dig_designation::Default; - dirty = true; ++count; } - if (dirty) - cur->flags.bits.designated = true; } if (count) out.print("Updated %d plant designations.\n", count); @@ -171,4 +168,4 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector #include #include - #include + +#include "df/activity_event.h" #include "df/world.h" #include "df/ui.h" #include "df/graphic.h" @@ -289,7 +290,8 @@ struct UnitInfo int active_index; string squad_effective_name; string squad_info; - string job_info; + string job_desc; + enum { IDLE, SOCIAL, JOB } job_mode; bool selected; struct { // Used for custom professions, 1-indexed @@ -363,16 +365,18 @@ bool sortBySquad (const UnitInfo *d1, const UnitInfo *d2) bool sortByJob (const UnitInfo *d1, const UnitInfo *d2) { - bool gt = false; + if (d1->job_mode != d2->job_mode) + { + if (descending) + return int(d1->job_mode) < int(d2->job_mode); + else + return int(d1->job_mode) > int(d2->job_mode); + } - if (d1->job_info == "Idle") - gt = false; - else if (d2->job_info == "Idle") - gt = true; + if (descending) + return d1->job_desc > d2->job_desc; else - gt = (d1->job_info > d2->job_info); - - return descending ? gt : !gt; + return d1->job_desc < d2->job_desc; } bool sortByStress (const UnitInfo *d1, const UnitInfo *d2) @@ -1234,9 +1238,18 @@ void viewscreen_unitlaborsst::refreshNames() cur->profession = Units::getProfessionName(unit); if (unit->job.current_job == NULL) { - cur->job_info = "Idle"; + df::activity_event *event = Units::getMainSocialEvent(unit); + if (event) { + event->getName(unit->id, &cur->job_desc); + cur->job_mode = UnitInfo::SOCIAL; + } + else { + cur->job_desc = "Idle"; + cur->job_mode = UnitInfo::IDLE; + } } else { - cur->job_info = DFHack::Job::getName(unit->job.current_job); + cur->job_desc = DFHack::Job::getName(unit->job.current_job); + cur->job_mode = UnitInfo::JOB; } if (unit->military.squad_id > -1) { cur->squad_effective_name = Units::getSquadName(unit); @@ -1283,7 +1296,7 @@ void viewscreen_unitlaborsst::calcSize() if (detail_mode == DETAIL_MODE_SQUAD) { detail_cmp = units[i]->squad_info.size(); } else if (detail_mode == DETAIL_MODE_JOB) { - detail_cmp = units[i]->job_info.size(); + detail_cmp = units[i]->job_desc.size(); } else { detail_cmp = units[i]->profession.size(); } @@ -1954,11 +1967,13 @@ void viewscreen_unitlaborsst::render() fg = 11; detail_str = cur->squad_info; } else if (detail_mode == DETAIL_MODE_JOB) { - detail_str = cur->job_info; - if (detail_str == "Idle") { - fg = 14; + detail_str = cur->job_desc; + if (cur->job_mode == UnitInfo::IDLE) { + fg = COLOR_YELLOW; + } else if (cur->job_mode == UnitInfo::SOCIAL) { + fg = COLOR_LIGHTGREEN; } else { - fg = 10; + fg = COLOR_LIGHTCYAN; } } else { fg = cur->color; diff --git a/plugins/stonesense b/plugins/stonesense index 1925760b2..1ffdc984d 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 1925760b2f611d246d1715a2e3cfb591a02ef00b +Subproject commit 1ffdc984d0c3d50e790b9ff5991c02fb21dec463 diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index 1bab3ebcf..a9ef091c7 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -90,6 +90,7 @@ #include "tweaks/fast-trade.h" #include "tweaks/fps-min.h" #include "tweaks/hide-priority.h" +#include "tweaks/hotkey-clear.h" #include "tweaks/import-priority-category.h" #include "tweaks/kitchen-keys.h" #include "tweaks/kitchen-prefs-color.h" @@ -205,6 +206,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector main.mode == df::ui_sidebar_mode::Hotkeys) + { + int x = 26, y = 19; + OutputHotkeyString(x, y, "Clear", df::interface_key::CUSTOM_C, false, 0, COLOR_WHITE, COLOR_LIGHTRED); + } + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (ui->main.mode == df::ui_sidebar_mode::Hotkeys && + input->count(df::interface_key::CUSTOM_C) && + !ui->main.in_rename_hotkey) + { + auto &hotkey = ui->main.hotkeys[ui->main.selected_hotkey]; + hotkey.name = ""; + hotkey.cmd = df::ui_hotkey::T_cmd::None; + hotkey.x = 0; + hotkey.y = 0; + hotkey.z = 0; + hotkey.unit_id = 0; + hotkey.item_id = 0; + } + else + { + INTERPOSE_NEXT(feed)(input); + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(hotkey_clear_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(hotkey_clear_hook, render); diff --git a/scripts b/scripts index cf367974b..e9c3119e7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cf367974b5e1513d454b2988d45122e98cd28f52 +Subproject commit e9c3119e751c3e2073eb02bbcda01d140cc6ae4a