Merge pull request #2681 from 20k/develop

More civzone <-> building work, add removing zones with associated squads support
develop
Myk 2023-01-22 17:14:24 -08:00 committed by GitHub
commit d82a795ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 46 deletions

@ -167,8 +167,8 @@ int linear_index(const std::vector<CT*> &vec, FT CT::*field, FT key)
return -1; return -1;
} }
template <typename CT, typename FT> template <typename CT, typename FT, typename MT>
int binsearch_index(const std::vector<CT*> &vec, FT CT::*field, FT key, bool exact = true) int binsearch_index(const std::vector<CT*> &vec, FT MT::*field, FT key, bool exact = true)
{ {
// Returns the index of the value >= the key // Returns the index of the value >= the key
int min = -1, max = (int)vec.size(); int min = -1, max = (int)vec.size();
@ -245,8 +245,8 @@ unsigned insert_into_vector(std::vector<FT> &vec, FT key, bool *inserted = NULL)
return pos; return pos;
} }
template<typename CT, typename FT> template<typename CT, typename FT, typename MT>
unsigned insert_into_vector(std::vector<CT*> &vec, FT CT::*field, CT *obj, bool *inserted = NULL) unsigned insert_into_vector(std::vector<CT*> &vec, FT MT::*field, CT *obj, bool *inserted = NULL)
{ {
unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false); unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false);
bool to_ins = (pos >= vec.size() || vec[pos] != obj); bool to_ins = (pos >= vec.size() || vec[pos] != obj);

@ -82,6 +82,7 @@ using namespace DFHack;
#include "df/map_block.h" #include "df/map_block.h"
#include "df/tile_occupancy.h" #include "df/tile_occupancy.h"
#include "df/plotinfost.h" #include "df/plotinfost.h"
#include "df/squad.h"
#include "df/ui_look_list.h" #include "df/ui_look_list.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/unit_relationship_type.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) 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; return;
} }
zone->contained_buildings.push_back(bld); insert_into_vector(zone->contained_buildings, &df::building::id, bld);
std::sort(zone->contained_buildings.begin(), zone->contained_buildings.end(), [](df::building* b1, df::building* b2)
{
return b1->id < b2->id;
});
} }
static void zone_into_building_unidir(df::building* bld, df::building_civzonest* zone) 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; return;
} }
bld->relations.push_back(zone); insert_into_vector(bld->relations, &df::building_civzonest::id, zone);
std::sort(bld->relations.begin(), bld->relations.end(), [](df::building* b1, df::building* b2)
{
return b1->id < b2->id;
});
} }
static bool is_suitable_building_for_zoning(df::building* bld) 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<df::building_civzonest*> cv; std::vector<df::building_civzonest*> cv;
Buildings::findCivzonesAt(&cv, coord); Buildings::findCivzonesAt(&cv, coord);
for (size_t i=0; i < cv.size(); i++) for (auto zone : cv)
{ add_building_to_zone(bld, zone);
add_building_to_zone(bld, cv[i]);
}
} }
static void add_zone_to_all_buildings(df::building* zone_as_building) 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) if (zone == nullptr)
return; return;
auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; for (auto bld : world->buildings.other.IN_PLAY)
for (size_t i = 0; i < vec.size(); i++)
{ {
auto against = vec[i]; if (zone->z != bld->z)
if (zone->z != against->z)
continue; continue;
if (!is_suitable_building_for_zoning(against)) if (!is_suitable_building_for_zoning(bld))
continue; continue;
int32_t cx = against->centerx; int32_t cx = bld->centerx;
int32_t cy = against->centery; int32_t cy = bld->centery;
df::coord2d coord(cx, cy); df::coord2d coord(cx, cy);
@ -262,7 +247,7 @@ static void add_zone_to_all_buildings(df::building* zone_as_building)
if (!etile || !*etile) if (!etile || !*etile)
continue; 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<df::building_civzonest*> cv; std::vector<df::building_civzonest*> cv;
Buildings::findCivzonesAt(&cv, coord); Buildings::findCivzonesAt(&cv, coord);
for (size_t i=0; i < cv.size(); i++) for (auto zone : cv)
{ remove_building_from_zone(bld, zone);
remove_building_from_zone(bld, cv[i]);
}
} }
static void remove_zone_from_all_buildings(df::building* zone_as_building) 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) if (zone == nullptr)
return; 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 //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 //good for fixing potentially bad game states when deleting a zone
for (size_t i = 0; i < vec.size(); i++) for (auto bld : world->buildings.other.IN_PLAY)
{
df::building* bld = vec[i];
remove_building_from_zone(bld, zone); remove_building_from_zone(bld, zone);
}
} }
uint32_t Buildings::getNumBuildings() uint32_t Buildings::getNumBuildings()
@ -1348,6 +1325,62 @@ bool Buildings::constructWithFilters(df::building *bld, std::vector<df::job_item
return true; return true;
} }
static void delete_civzone_squad_links(df::building_civzonest* zone)
{
for (df::building_civzonest::T_squad_room_info* room_info : zone->squad_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) bool Buildings::deconstruct(df::building *bld)
{ {
using df::global::plotinfo; using df::global::plotinfo;
@ -1386,7 +1419,14 @@ bool Buildings::deconstruct(df::building *bld)
bld->uncategorize(); bld->uncategorize();
remove_building_from_all_zones(bld); remove_building_from_all_zones(bld);
remove_zone_from_all_buildings(bld);
if (bld->getType() == df::building_type::Civzone)
{
auto zone = strict_virtual_cast<df::building_civzonest>(bld);
if (zone)
on_civzone_delete(zone);
}
delete bld; delete bld;