diff --git a/docs/changelog.txt b/docs/changelog.txt index 7fbf560635..b45e459f9a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -33,6 +33,7 @@ Template for new versions: ## Misc Improvements ## Documentation +Document caveats about unit/item position fns w/r/t raiding units. ## API diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 232daea9eb..64c4646121 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1402,7 +1402,10 @@ Units module * ``dfhack.units.isActive(unit)`` - The unit is active (non-dead and on the map). + The unit is active (non-dead and probably on the map). Unit must also be + present in the ``world.units.active`` vector to rule out raid missions. Use + ``utils.linear_index`` after this function returns true if you aren't + certain (i.e., not already iterating active units). * ``dfhack.units.isVisible(unit)`` @@ -1608,12 +1611,15 @@ Units module * ``dfhack.units.isUnitInBox(unit,x1,y1,z1,x2,y2,z2)`` - Returns true if the unit is within a box defined by the - specified coordinates. + Returns true if the unit is within a box defined by the specified + coordinates. Make sure the unit is flagged active and is present in + ``world.units.active`` first, as the result may indicate that the unit + died or left the map here. * ``dfhack.units.getUnitsInBox(x1,y1,z1,x2,y2,z2[,filter])`` - Returns a table of all units within the specified coordinates. + Returns a table of all units within the specified coordinates. Returned + units are guaranteed to be active (unlike ``isUnitInBox`` above). If the ``filter`` argument is given, only units where ``filter(unit)`` returns true will be included. Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to the arguments required by @@ -1643,7 +1649,9 @@ Units module Returns the true *x,y,z* of the unit, or *nil* if invalid. You should generally use this method instead of reading *unit.pos* directly since - that field can be inaccurate when the unit is caged. + that field can be inaccurate when the unit is caged. Make sure the unit is + active (and present in ``world.units.active``) first or else the result can + indicate where the unit died or left the map. * ``dfhack.units.teleport(unit, pos)`` @@ -2033,7 +2041,9 @@ Items module Returns the true *x,y,z* of the item, or *nil* if invalid. You should generally use this method instead of reading *item.pos* directly since that field only stores - the last position where the item was on the ground. + the last position where the item was on the ground. Make sure the item is present in + ``world.items.other.IN_PLAY`` first, otherwise the result can indicate where a unit + left the map with the item. * ``dfhack.items.getBookTitle(item)`` diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index c8fc810c8b..79fcf7b225 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -584,7 +584,7 @@ static command_result ListUnits(color_ostream &stream, if (in->scan_all()) { - auto &vec = df::unit::get_vector(); + auto &vec = df::global::world->units.active; for (size_t i = 0; i < vec.size(); i++) { diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 79aed92c36..0f797ae50e 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -137,7 +137,8 @@ DFHACK_EXPORT df::building *getHolderBuilding(df::item *item); // Get unit that holds the item or NULL. DFHACK_EXPORT df::unit *getHolderUnit(df::item *item); -// Returns the true position of the item (non-trivial if in inventory). +/// Returns the true position of the item (non-trivial if in inventory). +/// Note: Make sure the item is in world.items.other.IN_PLAY first, else can be inaccurate. DFHACK_EXPORT df::coord getPosition(df::item *item); /// Returns the title of a codex or "tool", either as the codex title or as the title of the diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 7eaa61ec62..0fd16bc3f9 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -69,7 +69,8 @@ namespace Units { * The Units module - allows reading all non-vermin units and their properties */ -// Unit is non-dead and on the map. +/// Unit is non-dead and on the map (usually). Unit must also be present in world.units.active +/// to rule out raid missions. DFHACK_EXPORT bool isActive(df::unit *unit); // Unit is on visible map tile. Doesn't account for ambushing. DFHACK_EXPORT bool isVisible(df::unit *unit); @@ -174,12 +175,12 @@ DFHACK_EXPORT bool isDanger(df::unit *unit); // Megabeasts, titans, forgotten beasts, and demons. DFHACK_EXPORT bool isGreatDanger(df::unit *unit); -// Check if unit is inside the cuboid area. +// Check if unit is inside the cuboid area. Note: Make sure unit is truly active first, else can be inaccurate. DFHACK_EXPORT bool isUnitInBox(df::unit *u, const cuboid &box); DFHACK_EXPORT inline bool isUnitInBox(df::unit *u, int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2) { return isUnitInBox(u, cuboid(x1, y1, z1, x2, y2, z2)); } -// Fill vector with units in box matching filter. +// Fill vector with units in box matching filter. Note: Units guaranteed to be active. DFHACK_EXPORT bool getUnitsInBox(std::vector &units, const cuboid &box, std::function filter = [](df::unit *u) { return true; }); DFHACK_EXPORT inline bool getUnitsInBox(std::vector &units, int16_t x1, int16_t y1, int16_t z1, @@ -202,7 +203,8 @@ inline auto citizensRange(std::vector &vec, bool exclude_residents = DFHACK_EXPORT void forCitizens(std::function fn, bool exclude_residents = false, bool include_insane = false); DFHACK_EXPORT bool getCitizens(std::vector &citizens, bool exclude_residents = false, bool include_insane = false); -// Returns the true position of the unit (non-trivial in case of caged). +/// Returns the true position of the unit (non-trivial in case of caged). +/// Note: Make sure unit is truly active first, else can be inaccurate. DFHACK_EXPORT df::coord getPosition(df::unit *unit); // Moves unit and any riders to the target coordinates. Sets tile occupancy flags. diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 44fec6ae2c..ee835f67af 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -667,7 +667,7 @@ bool Units::getUnitsInBox(vector &units, const cuboid &box, std::fun units.clear(); for (auto unit : world->units.active) - if (filter(unit) && isUnitInBox(unit, box)) + if (isActive(unit) && filter(unit) && isUnitInBox(unit, box)) units.push_back(unit); return true; } diff --git a/plugins/spectate/spectate.cpp b/plugins/spectate/spectate.cpp index 61584e8258..dd3e3b54be 100644 --- a/plugins/spectate/spectate.cpp +++ b/plugins/spectate/spectate.cpp @@ -202,6 +202,8 @@ namespace SP { } }; static auto valid = [](df::unit* unit) { + if (!Units::isActive(unit)) + return false; if (Units::isAnimal(unit)) { return config.animals; }