diff --git a/Client/game_sa/CEntitySA.cpp b/Client/game_sa/CEntitySA.cpp index 02a9f00380..4c6f4228df 100644 --- a/Client/game_sa/CEntitySA.cpp +++ b/Client/game_sa/CEntitySA.cpp @@ -66,11 +66,6 @@ CRect* CEntitySAInterface::GetBoundRect_(CRect* pRect) return pRect; } -void CEntitySAInterface::StaticSetHooks() -{ - HookInstall(0x534120, &CEntitySAInterface::GetBoundRect_); -} - CEntitySA::CEntitySA() { // Set these variables to a constant state @@ -715,3 +710,77 @@ bool CEntitySA::GetUnderwater() { return m_pInterface->bUnderwater; } + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for CEntity::CreateEffects & CEntity::DestroyEffects +// +// Handle modified 2dfx effects during streaming +// This is necessary because once the object is streamed in, the 2dfx effects are loaded from RwStream +// and the default effects are restored even though they have been modified. +// +////////////////////////////////////////////////////////////////////////////////////////// +static void Keep2DFXEffectsBeforeRemove(std::uint32_t modelID) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(modelID); + if (!modelInfo) + return; + + modelInfo->CopyModified2DFXEffects(); +} + +#define HOOKPOS_CEntity_DestroyEffects 0x533C01 +#define HOOKSIZE_CEntity_DestroyEffects 5 +static constexpr DWORD CONTINUE_CEntity_DestroyEffects = 0x533C06; +static void _declspec(naked) HOOK_CEntity_DestroyEffects() +{ + _asm + { + pushad + push eax + call Keep2DFXEffectsBeforeRemove + add esp, 4 + popad + + movzx eax, byte ptr [ecx+0Dh] + push ebp + jmp CONTINUE_CEntity_DestroyEffects + } +} + +static void Restore2DFXEffects(std::uint32_t modelID) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(modelID); + if (!modelInfo) + return; + + modelInfo->RestoreModified2DFXEffects(); +} + +#define HOOKPOS_CEntity_CreateEffects 0x533BAE +#define HOOKSIZE_CEntity_CreateEffects 9 +static constexpr DWORD RETURN_CEntity_CreateEffects = 0x533BB7; +static void _declspec(naked) HOOK_CEntity_CreateEffects() +{ + _asm + { + pushad + push [ebp+22h] + call Restore2DFXEffects + add esp, 4 + popad + + pop edi + pop ebp + pop ebx + add esp, 0C0h + jmp RETURN_CEntity_CreateEffects + } +} + +void CEntitySAInterface::StaticSetHooks() +{ + HookInstall(0x534120, &CEntitySAInterface::GetBoundRect_); + EZHookInstall(CEntity_DestroyEffects); + EZHookInstall(CEntity_CreateEffects); +} diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 6fe90b9fde..aedbf8d6e1 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -37,10 +37,12 @@ std::unordered_map CModelInfo std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; -std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; -static std::unordered_map m_numCustom2dfxEffects; -static std::vector d2fxEffects; -static std::vector removedDefaultEffects; +static std::unordered_map> ms_DefaultEffectsMap; +static std::unordered_map numCustom2dfxEffects; +static std::vector d2fxEffects; +static std::vector removedDefault2dfxEffects; +static std::unordered_map defaultNumOf2DFXEffects; +static std::unordered_map> tempCopy2dfxEffects; int C2DEffectSA::effect2dPluginOffset = *(int*)0xC3A1E0; // g2dEffectPluginOffset @@ -116,7 +118,7 @@ CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() void CModelInfoSA::SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; - MapSet(m_numCustom2dfxEffects, ppModelInfo[dwModelID], 0); + MapSet(numCustom2dfxEffects, ppModelInfo[dwModelID], 0); } bool CModelInfoSA::IsBoat() @@ -1197,10 +1199,10 @@ void CModelInfoSA::StaticReset2DFXEffects() memcpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), innerIter->first); - if (removedEffect != removedDefaultEffects.end()) + auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), innerIter->first); + if (removedEffect != removedDefault2dfxEffects.end()) { - removedDefaultEffects.erase(removedEffect); + removedDefault2dfxEffects.erase(removedEffect); modelInfoInterface->ucNumOf2DEffects++; } @@ -1211,15 +1213,15 @@ void CModelInfoSA::StaticReset2DFXEffects() } // Decrement the counter by the number of custom effects - auto customEffectsCount = MapGet(m_numCustom2dfxEffects, modelInfoInterface); + auto customEffectsCount = MapGet(numCustom2dfxEffects, modelInfoInterface); if (customEffectsCount && customEffectsCount > 0) modelInfoInterface->ucNumOf2DEffects -= customEffectsCount; - MapSet(m_numCustom2dfxEffects, modelInfoInterface, 0); + MapSet(numCustom2dfxEffects, modelInfoInterface, 0); } // Clear maps - removedDefaultEffects.clear(); + removedDefault2dfxEffects.clear(); ms_DefaultEffectsMap.clear(); d2fxEffects.clear(); } @@ -2118,6 +2120,9 @@ void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) return; + if (!MapContains(defaultNumOf2DFXEffects, m_dwModelID)) + MapSet(defaultNumOf2DFXEffects, m_dwModelID, ppModelInfo[m_dwModelID]->ucNumOf2DEffects); + // Copy an existing default effect C2DEffectSAInterface* copy = new C2DEffectSAInterface(); memcpy(copy, effect, sizeof(C2DEffectSAInterface)); @@ -2158,10 +2163,10 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) memcpy(it->first, it->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), it->first); - if (removedEffect != removedDefaultEffects.end()) + auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), it->first); + if (removedEffect != removedDefault2dfxEffects.end()) { - removedDefaultEffects.erase(removedEffect); + removedDefault2dfxEffects.erase(removedEffect); m_pInterface->ucNumOf2DEffects++; } @@ -2205,7 +2210,7 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Update counters m_pInterface->ucNumOf2DEffects = m_pInterface->ucNumOf2DEffects ? m_pInterface->ucNumOf2DEffects + 1 : 1; - MapGet(m_numCustom2dfxEffects, m_pInterface)++; + MapGet(numCustom2dfxEffects, m_pInterface)++; // Save our effect d2fxEffects.push_back(effectInterface); @@ -2233,7 +2238,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) m_pInterface->ucNumOf2DEffects--; if (isCustomEffect) { - MapGet(m_numCustom2dfxEffects, m_pInterface)--; + MapGet(numCustom2dfxEffects, m_pInterface)--; d2fxEffects.erase(it); } @@ -2307,7 +2312,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) effect = nullptr; } else - removedDefaultEffects.push_back(effect); + removedDefault2dfxEffects.push_back(effect); } bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault) @@ -2410,7 +2415,7 @@ static C2DEffectSAInterface* Get2dEffect(CBaseModelInfoSAInterface* modelInfo, R static auto* storedEffects = reinterpret_cast(ARRAY_2DFXInfoStore); - std::uint32_t numCustomEffects = m_numCustom2dfxEffects[modelInfo]; + std::uint32_t numCustomEffects = numCustom2dfxEffects[modelInfo]; std::uint32_t numStoredEffects = modelInfo->ucNumOf2DEffects - numPluginEffects - numCustomEffects; if (index < numStoredEffects) @@ -2447,6 +2452,64 @@ static void _declspec(naked) HOOK_Get2dEffect() } } +void CModelInfoSA::CopyModified2DFXEffects() +{ + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo || modelInfo->ucNumOf2DEffects == 0) + return; + + // Has modified effects? + if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) + return; + + auto tempVec = std::vector(); + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + // Copy effect + auto copy = new C2DEffectSAInterface(); + memcpy(copy, effect, sizeof(C2DEffectSAInterface)); + tempVec.push_back(copy); + } + + MapSet(tempCopy2dfxEffects, m_dwModelID, tempVec); +} + +void CModelInfoSA::RestoreModified2DFXEffects() +{ + if (!MapContains(tempCopy2dfxEffects, m_dwModelID)) + return; + + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo) + return; + + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + auto& tempVec = MapGet(tempCopy2dfxEffects, m_dwModelID); + if (tempVec.size() > 0) + { + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + if (tempVec[i]) + { + memcpy(effect, tempVec[i], sizeof(C2DEffectSAInterface)); + delete tempVec[i]; + } + } + } + + tempVec.clear(); + tempCopy2dfxEffects.erase(m_dwModelID); +} + ////////////////////////////////////////////////////////////////////////////////////////// // // Setup hooks diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 6c5691a893..73aeb6642a 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -333,7 +333,6 @@ class CModelInfoSA : public CModelInfo static std::unordered_map> ms_VehicleModelDefaultWheelSizes; static std::map ms_DefaultTxdIDMap; SVehicleSupportedUpgrades m_ModelSupportedUpgrades; - static std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; public: CModelInfoSA(); @@ -482,6 +481,9 @@ class CModelInfoSA : public CModelInfo bool Reset2DFXEffects(bool removeCustomEffects = false); static void StaticReset2DFXEffects(); + void CopyModified2DFXEffects(); + void RestoreModified2DFXEffects(); + bool IsDynamic() { return m_pInterface ? m_pInterface->usDynamicIndex != 0xffff : false; }; private: diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 6de796075b..477b6641d4 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -356,6 +356,9 @@ class CModelInfo virtual void StoreDefault2DFXEffect(C2DEffectSAInterface* effect) = 0; virtual bool Reset2DFXEffects(bool removeCustomEffects = false) = 0; + virtual void CopyModified2DFXEffects() = 0; + virtual void RestoreModified2DFXEffects() = 0; + virtual unsigned int GetParentID() = 0; virtual bool IsDynamic() = 0; };