diff --git a/docs/changelog.txt b/docs/changelog.txt index cd43392b78..d65aeace62 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -56,9 +56,11 @@ Template for new versions: ## New Features ## Fixes +- `spectate`: don't show a hover tooltip for hidden units (e.g. invisible snatchers) - `stockpiles`: fix one-off error in item type when importing furniture stockpile settings - `dig-now`: fix cases where boulders/rough gems of incorrect material were being generated when digging through walls - `dig-now`: properly generate ice boulders when digging through ice walls +- `gui/teleport`: now properly handles teleporting units that are currently falling or being flung ## Misc Improvements - `spectate`: show dwarves' activities (like prayer) @@ -67,6 +69,7 @@ Template for new versions: ## API - ``Military`` module: added ``addToSquad`` function +- ``Units::teleport``: projectile information is now cleared for teleported units ## Lua - ``dfhack.military.addToSquad``: expose Military API function diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 3679323eb8..bad77d7935 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -318,6 +318,22 @@ namespace DFHack return cur->item; } + I * operator->() + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); + + return cur->item; + } + + I * operator->() const + { + CHECK_NULL_POINTER(root); + CHECK_NULL_POINTER(cur); + + return cur->item; + } + operator const_iterator() const { return const_iterator(*this); diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 8568a55c0a..2b9426202d 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -309,24 +309,43 @@ Link *linked_list_insert_after(Link *pos, Link *link) } /** - * Returns true if the item with id idToRemove was found, deleted, and removed - * from the list. Otherwise returns false. + * Returns true if an item that matches the given function was found, deleted, + * and removed from the list. Otherwise returns false. Only removes the first + * match. + * + * Example usage: + * + * linked_list_remove(&world->projectiles.all, [&](df::projectile *proj) { + * if (proj->getType() != df::enums::projectile_type::Unit) + * return false; + * if (auto unit_proj = virtual_cast(proj)) + * return unit_proj->unit == unit; + * return false; + * }); */ -template -bool linked_list_remove(Link *head, int32_t idToRemove) -{ - for (Link *link = head; link; link = link->next) - { - if (!link->item || link->item->id != idToRemove) - continue; - - link->prev->next = link->next; - if (link->next) - link->next->prev = link->prev; - delete(link); - return true; - } - return false; +template F> +bool linked_list_remove(L *list, F matches) { + auto matches_wrapper = [&](L::iterator::value_type item) { + return item && matches(item); + }; + typename L::const_iterator it = std::find_if(list->cbegin(), list->cend(), matches_wrapper); + if (it == list->cend()) + return false; + auto item = *it; + list->erase(it); + delete item; + return true; +} + +/** + * Returns true if the item with id idToRemove was found, deleted, and + * removed from the list. Otherwise returns false. + */ +template +bool linked_list_remove(L *list, int32_t idToRemove) { + return linked_list_remove(list, [&](L::iterator::value_type item) { + return item->id == idToRemove; + }); } template diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 21f39932fe..e604562c77 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -67,6 +67,7 @@ distribution. #include "df/nemesis_record.h" #include "df/personality_goalst.h" #include "df/plotinfost.h" +#include "df/proj_unitst.h" #include "df/reputation_profilest.h" #include "df/syndrome.h" #include "df/tile_occupancy.h" @@ -773,6 +774,18 @@ bool Units::teleport(df::unit *unit, df::coord target_pos) else old_occ->bits.unit = false; + // Clear unit projectile info + if (unit->flags1.bits.projectile) { + unit->flags1.bits.projectile = false; + linked_list_remove(&world->projectiles.all, [&](df::projectile *proj) { + if (proj->getType() != df::enums::projectile_type::Unit) + return false; + if (auto unit_proj = virtual_cast(proj)) + return unit_proj->unit == unit; + return false; + }); + } + // If there's already somebody standing at the destination, then force the unit to lay down if (new_occ->bits.unit) unit->flags1.bits.on_ground = true; diff --git a/plugins/lua/spectate.lua b/plugins/lua/spectate.lua index fd2f736d0e..8c7e653a7b 100644 --- a/plugins/lua/spectate.lua +++ b/plugins/lua/spectate.lua @@ -345,11 +345,15 @@ local function GetUnitInfoText(unit, settings_group_name) return txt end +local function unit_filter(unit) + return not dfhack.units.isHidden(unit) +end + local function GetHoverText(pos) if not pos then return end local txt = {} - local units = dfhack.units.getUnitsInBox(pos, pos) or {} -- todo: maybe (optionally) use filter parameter here? + local units = dfhack.units.getUnitsInBox(pos, pos, unit_filter) or {} for _,unit in ipairs(units) do local info = GetUnitInfoText(unit, 'hover')