diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 6f418fb8fd..4705300665 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -13,6 +13,7 @@ void CellExt::Serialize(T& Stm) Stm .Process(this->RadSites) .Process(this->RadLevels) + .Process(this->CoveringLights) .Process(this->InfantryCount) ; } diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 03055ff1a1..d990bcf87a 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -44,8 +44,9 @@ class CellExt final : public AbstractExt } std::vector RadSites {}; - std::vector RadLevels { }; - int InfantryCount{ 0 }; + std::vector RadLevels {}; + std::vector CoveringLights {}; + int InfantryCount { 0 }; CellExt(CellClass* OwnerObject) : AbstractExt(OwnerObject) { } diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index 3472a5f499..a676921e8b 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -9,3 +9,97 @@ DEFINE_HOOK(0x480EA8, CellClass_DamageWall_AdjacentWallDamage, 0x7) pThis->DamageWall(RulesExt::Global()->AdjacentWallDamage); return SkipGameCode; } + +#pragma region LightSource Optimize + +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::ExtMap.Find(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::ExtMap.Find(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(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(diffX) * diffX + static_cast(diffY) * diffY; + + if (static_cast(distanceSqr) > static_cast(pThis->LightVisibility) * pThis->LightVisibility) + return OutOfRange; + + if (const auto pCell = MapClass::Instance.TryGetCellAt(cell)) + { + pCell->MarkForRedraw(); + + if (const auto pBuilding = pCell->GetBuilding()) + pBuilding->MarkForRedraw(); + } + + return InRange; +} + +DEFINE_JUMP(LJMP, 0x554D0A, 0x554D16) // Skip GScreenClass::MarkNeedsRedraw(1); + +#pragma endregion