Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Ext/Cell/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void CellExt::Serialize(T& Stm)
Stm
.Process(this->RadSites)
.Process(this->RadLevels)
.Process(this->CoveringLights)
.Process(this->CoveringTerrains)
.Process(this->InfantryCount)
;
}
Expand Down
6 changes: 4 additions & 2 deletions src/Ext/Cell/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ class CellExt final : public AbstractExt
}

std::vector<RadSiteClass*> RadSites {};
std::vector<RadLevel> RadLevels { };
int InfantryCount{ 0 };
std::vector<RadLevel> RadLevels {};
std::vector<LightSourceClass*> CoveringLights {};
std::vector<TerrainClass*> CoveringTerrains {};
int InfantryCount { 0 };

CellExt(CellClass* OwnerObject) : AbstractExt(OwnerObject)
{ }
Expand Down
129 changes: 129 additions & 0 deletions src/Ext/Cell/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,132 @@ DEFINE_HOOK(0x480EA8, CellClass_DamageWall_AdjacentWallDamage, 0x7)
pThis->DamageWall(RulesExt::Global()->AdjacentWallDamage);
return SkipGameCode;
}

#pragma region LightSource Optimize

namespace LightSourceTemp
{
std::vector<TerrainClass*> RedrawTerrains {};
}

DEFINE_HOOK(0x5547C8, LightSourceClass_CTOR_SetInCells, 0x5)
{
GET(LightSourceClass*, pThis, ESI);

for (CellRangeEnumerator cell(CellClass::Coord2Cell(pThis->Location), pThis->LightVisibility / Unsorted::LeptonsPerCell + 0.5); cell; ++cell)
{
const auto pCell = MapClass::Instance.TryGetCellAt(*cell);

if (!pCell)
continue;

const auto pCellExt = CellExt::Fetch(pCell);
pCellExt->CoveringLights.emplace_back(pThis);
}

return 0;
}

DEFINE_HOOK(0x555176, LightSourceClass_DTOR_ResetInCells, 0x6)
{
GET(LightSourceClass*, pThis, ESI);

for (CellRangeEnumerator cell(CellClass::Coord2Cell(pThis->Location), pThis->LightVisibility / Unsorted::LeptonsPerCell + 0.5); cell; ++cell)
{
const auto pCell = MapClass::Instance.TryGetCellAt(*cell);

if (!pCell)
continue;

const auto pCellExt = CellExt::Fetch(pCell);
const auto it = std::ranges::find(pCellExt->CoveringLights, pThis);

if (it != pCellExt->CoveringLights.cend())
pCellExt->CoveringLights.erase(it);
}

return 0;
}

DEFINE_HOOK_AGAIN(0x48444C, CellClass_ProcessColourComponents_LightSourceCount, 0x6)
DEFINE_HOOK(0x48427D, CellClass_ProcessColourComponents_LightSourceCount, 0x6)
{
GET(CellClass*, pThis, EDI);
const auto pExt = CellExt::Fetch(pThis);

R->ECX(static_cast<int>(pExt->CoveringLights.size()));
return R->Origin() + 0x6;
}

DEFINE_HOOK(0x48428B, CellClass_ProcessColourComponents_LightSourceItem, 0x6)
{
enum { ApplyItem = 0x484294 };

GET(CellClass*, pThis, EDI);
GET(int, idx, EAX);
const auto pExt = CellExt::Fetch(pThis);

R->ESI(pExt->CoveringLights[idx]);
return ApplyItem;
}

DEFINE_JUMP(LJMP, 0x4842DC, 0x4842E2) // Skip useless code

DEFINE_HOOK(0x554BF6, LightSourceClass_554AF0_Distance_Optimize, 0x5)
{
enum { InRange = 0x554C4B, OutOfRange = 0x554CE4 };

GET(LightSourceClass*, pThis, EDI);
REF_STACK(CellStruct, cell, STACK_OFFSET(0x30, -0x24));
const auto cellCoords = CellClass::Cell2Coord(cell);
const int diffX = pThis->Location.X - cellCoords.X;
const int diffY = pThis->Location.Y - cellCoords.Y;
const double distanceSqr = static_cast<double>(diffX) * diffX + static_cast<double>(diffY) * diffY;

if (static_cast<double>(distanceSqr) > static_cast<double>(pThis->LightVisibility) * pThis->LightVisibility)
return OutOfRange;

if (const auto pCell = MapClass::Instance.TryGetCellAt(cell))
{
pCell->MarkForRedraw();

if (const auto pBuilding = pCell->GetBuilding())
pBuilding->MarkForRedraw();

auto& redrawTerrains = LightSourceTemp::RedrawTerrains;
const auto pCellExt = CellExt::Fetch(pCell);

for (const auto pCoveringTerrain : pCellExt->CoveringTerrains)
{
if (std::ranges::find(redrawTerrains, pCoveringTerrain) != redrawTerrains.cend())
continue;

redrawTerrains.emplace_back(pCoveringTerrain);
}
}

return InRange;
}

DEFINE_HOOK(0x554D0A, LightSourceClass_554AF0_CellRedraw, 0x7)
{
enum { SkipGScreenRedraw = 0x554D16 };

auto& redrawTerrains = LightSourceTemp::RedrawTerrains;

if (!redrawTerrains.empty())
{
for (const auto pTerrain : redrawTerrains)
{
RectangleStruct rect;
pTerrain->GetRenderDimensions(&rect);
TacticalClass::Instance->RegisterDirtyArea(rect, false);
}

redrawTerrains.clear();
}

return SkipGScreenRedraw;
}

#pragma endregion
128 changes: 128 additions & 0 deletions src/Ext/TerrainType/Hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Body.h"

#include <Ext/Rules/Body.h>
#include <Ext/Cell/Body.h>

namespace TerrainTypeTemp
{
Expand Down Expand Up @@ -344,3 +345,130 @@ DEFINE_HOOK(0x568432, MapClass_PlaceDown_0x0TerrainTypes, 0x8)

return 0;
}

#pragma region LightSource Dirty

static bool RectangleIntersectsDiamond(const RectangleStruct& rect, int cx, int cy, int rx, int ry)
{
const int rl = rect.X;
const int rt = rect.Y;
const int rr = rect.X + rect.Width;
const int rb = rect.Y + rect.Height;

if (rr < cx - rx || rl > cx + rx || rb < cy - ry || rt > cy + ry)
return false;

auto InDiamond = [=](int x, int y)
{
return std::abs(static_cast<double>(x - cx) / rx) + std::abs(static_cast<double>(y - cy) / ry) <= 1.0;
};

auto InRect = [=](int x, int y)
{
return x >= rl && x <= rr && y >= rt && y <= rb;
};

if (InRect(cx, cy))
return true;

if (InDiamond(rl, rt)
|| InDiamond(rr, rt)
|| InDiamond(rl, rb)
|| InDiamond(rr, rb))
return true;

if (InRect(cx - rx, cy)
|| InRect(cx + rx, cy)
|| InRect(cx, cy - ry)
|| InRect(cx, cy + ry))
return true;

return false;
}

static std::vector<CellClass*> GetTerrainCoveredCells(TerrainClass* pThis)
{
const auto baseCell = pThis->GetMapCoords();
RectangleStruct rect;
pThis->GetRenderDimensions(&rect);

const auto tacticalPos = TacticalClass::Instance->TacticalPos;
const auto anchor = TacticalClass::CoordsToScreen(pThis->GetCoords()) - tacticalPos;

const int leftW = anchor.X - rect.X;
const int rightW = rect.X + rect.Width - anchor.X;

if (rightW > leftW)
rect.Width = 2 * leftW;

const int range = std::max(4, (rect.Height + 60) / 30);
const int rx = Unsorted::CellWidthInPixels / 2;
const int ry = Unsorted::CellHeightInPixels / 2;
std::vector<CellClass*> result;

for (int cy = baseCell.Y - range; cy <= baseCell.Y + range; ++cy)
{
for (int cx = baseCell.X - range; cx <= baseCell.X + range; ++cx)
{
if (cx + cy > baseCell.X + baseCell.Y)
continue;

const CellStruct cell { static_cast<short>(cx), static_cast<short>(cy) };
const auto pCell = MapClass::Instance.TryGetCellAt(cell);

if (!pCell)
continue;

const auto cellClient = TacticalClass::CoordsToScreen(pCell->GetCoords()) - tacticalPos;

if (!RectangleIntersectsDiamond(rect, cellClient.X, cellClient.Y, rx, ry))
continue;

result.emplace_back(pCell);
}
}

return result;
}

DEFINE_HOOK(0x71D0E7, TerrainClass_Unlimbo_Covering, 0x6)
{
enum { ReturnTrue = 0x71D132 };

GET_STACK(TerrainClass*, pThis, STACK_OFFSET(0x20, -0x10));
GET(CellClass*, pCell, EAX);
const auto covering = GetTerrainCoveredCells(pThis);

for (const auto pCovering : covering)
CellExt::Fetch(pCovering)->CoveringTerrains.emplace_back(pThis);

const int overlayIdx = pCell->OverlayTypeIndex;

if (overlayIdx != -1 && OverlayTypeClass::Array[overlayIdx]->Tiberium)
{
pCell->OverlayTypeIndex = -1;
pCell->OverlayData = 0;
}

return ReturnTrue;
}

DEFINE_HOOK(0x71CA1C, TerrainClass_Limbo_ResetCovering, 0x5)
{
GET(TerrainClass*, pThis, EDI);
const auto covering = GetTerrainCoveredCells(pThis);

for (const auto pCovering : covering)
{
auto& coveringTerrains = CellExt::Fetch(pCovering)->CoveringTerrains;
const auto it = std::ranges::find(coveringTerrains, pThis);

if (it != coveringTerrains.cend())
coveringTerrains.erase(it);
}

return 0;
}


#pragma endregion
Loading