From de2b52e686c301200510a171e6b95faa4b50393b Mon Sep 17 00:00:00 2001 From: Alistair-Afton Date: Wed, 16 Sep 2026 15:58:22 +0200 Subject: [PATCH] Units::teleport: update occupancy for the full EQUIPMENT footprint EQUIPMENT units (wagons) occupy a 3x3 footprint centered on their position, but teleport only cleared and set the unit occupancy bits on the center tile. Teleported wagons left stale unit flags on the eight surrounding source tiles and arrived with none set on the destination footprint, leaving the map occupancy inconsistent until something recomputed it (issue #5797). The footprint extent now comes from the race's EQUIPMENT_WAGON flag, so ordinary units keep the existing single-tile behavior. --- docs/changelog.txt | 1 + library/modules/Units.cpp | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 78e8a56cc3..0e40ec4c48 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -69,6 +69,7 @@ Template for new versions: - `aquifer`: make ``--skip-top`` and top-relative ``--levels`` take effect for the ``drain``, ``convert``, and ``add`` actions instead of being silently ignored - `autodump`: ``destroy`` no longer leaves the contents of destroyed containers in limbo or crashes when destroying unit-held items - `timestream`: deal properly with units who have breathing difficulties +- ``Units::teleport``: update unit occupancy on the full 3x3 footprint of EQUIPMENT units (e.g. wagons) instead of only their center tile ## Misc Improvements - Added ``Coord2d`` and ``Coord3d`` C++ templates, providing a standard set operations for 2-tuples and 3-tuples of any numeric type diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 33553f2874..b188992b64 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -42,6 +42,7 @@ distribution. #include "df/caste_raw.h" #include "df/creature_interaction_effect_display_namest.h" #include "df/creature_raw.h" +#include "df/creature_raw_flags.h" #include "df/curse_attr_change.h" #include "df/entity_position.h" #include "df/entity_position_assignment.h" @@ -777,12 +778,28 @@ bool Units::teleport(df::unit *unit, df::coord target_pos) if (!old_occ || !new_occ) return false; + // EQUIPMENT units (e.g. wagons) occupy a 3x3 footprint centered on their + // position; all other units occupy just their position tile + int extent = 0; + if (auto craw = df::creature_raw::find(unit->race); craw && + craw->flags.is_set(df::creature_raw_flags::EQUIPMENT_WAGON)) + extent = 1; + + auto for_each_occupied_tile = [&](df::coord center, auto &&fn) { + for (int dy = -extent; dy <= extent; ++dy) + for (int dx = -extent; dx <= extent; ++dx) + if (auto occ = Maps::getTileOccupancy(center.x+dx, center.y+dy, center.z)) + fn(*occ); + }; + // Clear appropriate occupancy flags at old tile - if (unit->flags1.bits.on_ground) - // This is potentially wrong, but the game will recompute this as needed - old_occ->bits.unit_grounded = false; - else - old_occ->bits.unit = false; + for_each_occupied_tile(unit->pos, [&](df::tile_occupancy &occ) { + if (unit->flags1.bits.on_ground) + // This is potentially wrong, but the game will recompute this as needed + occ.bits.unit_grounded = false; + else + occ.bits.unit = false; + }); // Clear unit projectile info if (unit->flags1.bits.projectile) { @@ -801,10 +818,12 @@ bool Units::teleport(df::unit *unit, df::coord target_pos) unit->flags1.bits.on_ground = true; // Set appropriate occupancy flags at new tile - if (unit->flags1.bits.on_ground) - new_occ->bits.unit_grounded = true; - else - new_occ->bits.unit = true; + for_each_occupied_tile(target_pos, [&](df::tile_occupancy &occ) { + if (unit->flags1.bits.on_ground) + occ.bits.unit_grounded = true; + else + occ.bits.unit = true; + }); // Move unit to destination unit->pos = target_pos;