Skip to content

Commit

Permalink
Add option to consider buildings as destroyable pathfinding obstacles
Browse files Browse the repository at this point in the history
  • Loading branch information
Starkku committed May 2, 2024
1 parent d96f2df commit b1ed818
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ This page lists all the individual contributions to the project by their author.
- Projectile return weapon
- Aircraft landing / docking direction
- `DeploysInto` cursor desync fix
- Buildings considered as destroyable pathfinding obstacles
- **Morton (MortonPL)**:
- `XDrawOffset` for animations
- Shield passthrough & absorption
Expand Down
10 changes: 10 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,16 @@ PowersUp.Owner=Self ; list of Affected House Enumeration (none|owner/self|allies
PowersUp.Buildings= ; list of BuildingTypes
```

### Destroyable pathfinding obstacles

It is possible to make buildings be considered pathfinding obstacles that can be destroyed by setting `IsDestroyableBlockage` to true. What this does is make the building be considered impassable and impenetrable pathfinding obstacle to every unit that is not flying or have appropriate `MovementZone` (ones that allow destroyable obstacles to be overcome, e.g `(Infantry|Amphibious)Destroyer`) akin to wall overlays and TerrainTypes.

In `rulesmd.ini`:
```ini
[SOMEBUILDING] ; BuildingType
IsDestroyableObstacle=false ; boolean
```

### Power plant enhancer

- When it exists, it can increase the power amount generated by the power plants.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ New:
- Allow upgrade animations to use `Powered` & `PoweredLight/Effect/Special` keys (by Starkku)
- Toggle for `Explodes=true` BuildingTypes to not explode during buildup or being sold (by Starkku)
- Toggleable height-based shadow scaling for voxel air units (by Trsdy & Starkku)
- Buildings considered as destroyable pathfinding obstacles (by Starkku)
Vanilla fixes:
- Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy)
Expand Down
45 changes: 45 additions & 0 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,51 @@ bool BuildingExt::HandleInfiltrate(BuildingClass* pBuilding, HouseClass* pInfilt
return true;
}

// Get all cells covered by the building, optionally including those covered by OccupyHeight.
const std::vector<CellStruct> BuildingExt::GetFoundationCells(BuildingClass* const pThis, CellStruct const baseCoords, bool includeOccupyHeight)
{
const CellStruct foundationEnd = { 0x7FFF, 0x7FFF };
auto const pFoundation = pThis->GetFoundationData(false);

int occupyHeight = includeOccupyHeight ? pThis->Type->OccupyHeight : 1;

if (occupyHeight <= 0)
occupyHeight = 1;

auto pCellIterator = pFoundation;

while (*pCellIterator != foundationEnd)
++pCellIterator;

std::vector<CellStruct> foundationCells;
foundationCells.reserve(static_cast<int>(std::distance(pFoundation, pCellIterator + 1)) * occupyHeight);
pCellIterator = pFoundation;

while (*pCellIterator != foundationEnd)
{
auto actualCell = baseCoords + *pCellIterator;

for (auto i = occupyHeight; i > 0; --i)
{
foundationCells.push_back(actualCell);
--actualCell.X;
--actualCell.Y;
}
++pCellIterator;
}

std::sort(foundationCells.begin(), foundationCells.end(),
[](const CellStruct& lhs, const CellStruct& rhs) -> bool
{
return lhs.X > rhs.X || lhs.X == rhs.X && lhs.Y > rhs.Y;
});

auto const it = std::unique(foundationCells.begin(), foundationCells.end());
foundationCells.erase(it, foundationCells.end());

return foundationCells;
}

// =============================
// load / save

Expand Down
1 change: 1 addition & 0 deletions src/Ext/Building/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,5 @@ class BuildingExt
static bool DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechno, int refund);
static bool HandleInfiltrate(BuildingClass* pBuilding, HouseClass* pInfiltratorHouse);
static bool CanUndeployOnSell(BuildingClass* pThis);
static const std::vector<CellStruct> GetFoundationCells(BuildingClass* pThis, CellStruct baseCoords, bool includeOccupyHeight = false);
};
66 changes: 66 additions & 0 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,69 @@ DEFINE_HOOK(0x4511D6, BuildingClass_AnimationAI_SellBuildup, 0x7)

return pTypeExt->SellBuildupLength == pThis->Animation.Value ? Continue : Skip;
}

#pragma region DestroyableObstacle

template <bool remove = false>
static void RecalculateCells(BuildingClass* pThis)
{
auto const cells = BuildingExt::GetFoundationCells(pThis, pThis->GetMapCoords());

auto& map = MapClass::Instance;

for (auto const& cell : cells)
{
if (auto pCell = map->TryGetCellAt(cell))
{
pCell->RecalcAttributes(DWORD(-1));

if constexpr (remove)
map->ResetZones(cell);
else
map->RecalculateZones(cell);

map->RecalculateSubZones(cell);

}
}
}

DEFINE_HOOK(0x440D01, BuildingClass_Unlimbo_DestroyableObstacle, 0x6)
{
GET(BuildingClass*, pThis, ESI);

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type);

if (pTypeExt->IsDestroyableObstacle)
RecalculateCells(pThis);

return 0;
}

DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6)
{
GET(BuildingClass*, pThis, ESI);

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type);

if (pTypeExt->IsDestroyableObstacle)
RecalculateCells<true>(pThis);

return 0;
}

DEFINE_HOOK(0x483D8E, CellClass_CheckPassability_DestroyableObstacle, 0x6)
{
enum { IsBlockage = 0x483CD4 };

GET(BuildingClass*, pBuilding, ESI);

auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type);

if (pTypeExt->IsDestroyableObstacle)
return IsBlockage;

return 0;
}

#pragma endregion
2 changes: 2 additions & 0 deletions src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)

this->ConsideredVehicle.Read(exINI, pSection, "ConsideredVehicle");
this->SellBuildupLength.Read(exINI, pSection, "SellBuildupLength");
this->IsDestroyableObstacle.Read(exINI, pSection, "IsDestroyableObstacle");

if (pThis->NumberOfDocks > 0)
{
Expand Down Expand Up @@ -259,6 +260,7 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm)
.Process(this->ZShapePointMove_OnBuildup)
.Process(this->SellBuildupLength)
.Process(this->AircraftDockingDirs)
.Process(this->IsDestroyableObstacle)
;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class BuildingTypeExt
Nullable<bool> ConsideredVehicle;
Valueable<bool> ZShapePointMove_OnBuildup;
Valueable<int> SellBuildupLength;
Valueable<bool> IsDestroyableObstacle;

std::vector<OptionalStruct<DirType, true>> AircraftDockingDirs;

Expand Down Expand Up @@ -101,6 +102,7 @@ class BuildingTypeExt
, ZShapePointMove_OnBuildup { false }
, SellBuildupLength { 23 }
, AircraftDockingDirs {}
, IsDestroyableObstacle { false }
{ }

// Ares 0.A functions
Expand Down

0 comments on commit b1ed818

Please sign in to comment.