Skip to content

Raiding units aren't flagged inactive, update code and docs #4948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Template for new versions:
## Misc Improvements

## Documentation
Document caveats about unit/item position fns w/r/t raiding units.

## API

Expand Down
22 changes: 16 additions & 6 deletions docs/dev/Lua API.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)``

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)``

Expand Down Expand Up @@ -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)``

Expand Down
2 changes: 1 addition & 1 deletion library/RemoteTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
{
Expand Down
3 changes: 2 additions & 1 deletion library/include/modules/Items.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions library/include/modules/Units.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<df::unit *> &units, const cuboid &box,
std::function<bool(df::unit *)> filter = [](df::unit *u) { return true; });
DFHACK_EXPORT inline bool getUnitsInBox(std::vector<df::unit *> &units, int16_t x1, int16_t y1, int16_t z1,
Expand All @@ -202,7 +203,8 @@ inline auto citizensRange(std::vector<df::unit *> &vec, bool exclude_residents =
DFHACK_EXPORT void forCitizens(std::function<void(df::unit *)> fn, bool exclude_residents = false, bool include_insane = false);
DFHACK_EXPORT bool getCitizens(std::vector<df::unit *> &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.
Expand Down
2 changes: 1 addition & 1 deletion library/modules/Units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ bool Units::getUnitsInBox(vector<df::unit *> &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;
}
Expand Down
2 changes: 2 additions & 0 deletions plugins/spectate/spectate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down