diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 124987535..c9a5f66d6 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -167,8 +167,8 @@ int linear_index(const std::vector &vec, FT CT::*field, FT key) return -1; } -template -int binsearch_index(const std::vector &vec, FT CT::*field, FT key, bool exact = true) +template +int binsearch_index(const std::vector &vec, FT MT::*field, FT key, bool exact = true) { // Returns the index of the value >= the key int min = -1, max = (int)vec.size(); @@ -245,8 +245,8 @@ unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) return pos; } -template -unsigned insert_into_vector(std::vector &vec, FT CT::*field, CT *obj, bool *inserted = NULL) +template +unsigned insert_into_vector(std::vector &vec, FT MT::*field, CT *obj, bool *inserted = NULL) { unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false); bool to_ins = (pos >= vec.size() || vec[pos] != obj); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 653f8bccf..5077b5b52 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -82,6 +82,7 @@ using namespace DFHack; #include "df/map_block.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" +#include "df/squad.h" #include "df/ui_look_list.h" #include "df/unit.h" #include "df/unit_relationship_type.h" @@ -168,34 +169,24 @@ void buildings_onUpdate(color_ostream &out) static void building_into_zone_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < zone->contained_buildings.size(); bid++) + for (auto contained_building : zone->contained_buildings) { - if (zone->contained_buildings[bid] == bld) + if (contained_building == bld) return; } - zone->contained_buildings.push_back(bld); - - std::sort(zone->contained_buildings.begin(), zone->contained_buildings.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(zone->contained_buildings, &df::building::id, bld); } static void zone_into_building_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < bld->relations.size(); bid++) + for (auto relation : bld->relations) { - if (bld->relations[bid] == zone) + if (relation == zone) return; } - bld->relations.push_back(zone); - - std::sort(bld->relations.begin(), bld->relations.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(bld->relations, &df::building_civzonest::id, zone); } static bool is_suitable_building_for_zoning(df::building* bld) @@ -222,10 +213,8 @@ static void add_building_to_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - add_building_to_zone(bld, cv[i]); - } + for (auto zone : cv) + add_building_to_zone(bld, zone); } static void add_zone_to_all_buildings(df::building* zone_as_building) @@ -238,20 +227,16 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - - for (size_t i = 0; i < vec.size(); i++) + for (auto bld : world->buildings.other.IN_PLAY) { - auto against = vec[i]; - - if (zone->z != against->z) + if (zone->z != bld->z) continue; - if (!is_suitable_building_for_zoning(against)) + if (!is_suitable_building_for_zoning(bld)) continue; - int32_t cx = against->centerx; - int32_t cy = against->centery; + int32_t cx = bld->centerx; + int32_t cy = bld->centery; df::coord2d coord(cx, cy); @@ -262,7 +247,7 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (!etile || !*etile) continue; - add_building_to_zone(against, zone); + add_building_to_zone(bld, zone); } } } @@ -295,10 +280,8 @@ static void remove_building_from_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - remove_building_from_zone(bld, cv[i]); - } + for (auto zone : cv) + remove_building_from_zone(bld, zone); } static void remove_zone_from_all_buildings(df::building* zone_as_building) @@ -311,16 +294,10 @@ static void remove_zone_from_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - //this is a paranoid check and slower than it could be. Zones contain a list of children //good for fixing potentially bad game states when deleting a zone - for (size_t i = 0; i < vec.size(); i++) - { - df::building* bld = vec[i]; - + for (auto bld : world->buildings.other.IN_PLAY) remove_building_from_zone(bld, zone); - } } uint32_t Buildings::getNumBuildings() @@ -1348,6 +1325,62 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorsquad_room_info) + { + int32_t squad_id = room_info->squad_id; + + df::squad* squad = df::squad::find(squad_id); + + //if this is null, something has gone just *terribly* wrong + if (squad) + { + for (int i=(int)squad->rooms.size() - 1; i >= 0; i--) + { + if (squad->rooms[i]->building_id == zone->id) + { + auto room = squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + delete room; + } + } + } + + delete room_info; + } + + zone->squad_room_info.clear(); +} + +//unit owned_building pointers are known-bad as of 50.05 and dangle on zone delete +//do not use anything that touches anything other than the pointer value +//this means also that if dwarf fortress reuses a memory allocation, we will end up with duplicates +//this vector is also not sorted by id +//it also turns out that multiple units eg (solely?) spouses can point to one room +static void delete_assigned_unit_links(df::building_civzonest* zone) +{ + //not clear if this is always true + /*if (zone->assigned_unit_id == -1) + return;*/ + + for (df::unit* unit : world->units.active) + { + for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + { + if (unit->owned_buildings[i] == zone) + unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + } + } +} + +static void on_civzone_delete(df::building_civzonest* civzone) +{ + remove_zone_from_all_buildings(civzone); + delete_civzone_squad_links(civzone); + delete_assigned_unit_links(civzone); +} + bool Buildings::deconstruct(df::building *bld) { using df::global::plotinfo; @@ -1386,7 +1419,14 @@ bool Buildings::deconstruct(df::building *bld) bld->uncategorize(); remove_building_from_all_zones(bld); - remove_zone_from_all_buildings(bld); + + if (bld->getType() == df::building_type::Civzone) + { + auto zone = strict_virtual_cast(bld); + + if (zone) + on_civzone_delete(zone); + } delete bld;