diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 2db97a5245..abfb3b4fc8 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1481,41 +1481,43 @@ void CModelInfoSA::ResetAllVehiclesWheelSizes() ms_VehicleModelDefaultWheelSizes.clear(); } -bool CModelInfoSA::SetCustomModel(RpClump* pClump) +bool CModelInfoSA::SetCustomModel(RpClump* clump) { - if (!pClump) + if (!clump) return false; if (!IsLoaded()) { // Wait for the game to eventually stream-in the model and then try to replace it (via MakeCustomModel). - m_pCustomClump = pClump; + m_pCustomClump = clump; return true; } + auto modelID = static_cast(m_dwModelID); bool success = false; switch (GetModelType()) { case eModelInfoType::PED: - success = pGame->GetRenderWare()->ReplacePedModel(pClump, static_cast(m_dwModelID)); + success = pGame->GetRenderWare()->ReplacePedModel(clump, modelID); break; case eModelInfoType::WEAPON: - success = pGame->GetRenderWare()->ReplaceWeaponModel(pClump, static_cast(m_dwModelID)); + success = pGame->GetRenderWare()->ReplaceWeaponModel(clump, modelID); break; case eModelInfoType::VEHICLE: - success = pGame->GetRenderWare()->ReplaceVehicleModel(pClump, static_cast(m_dwModelID)); + success = pGame->GetRenderWare()->ReplaceVehicleModel(clump, modelID); break; case eModelInfoType::ATOMIC: case eModelInfoType::LOD_ATOMIC: case eModelInfoType::TIME: - success = pGame->GetRenderWare()->ReplaceAllAtomicsInModel(pClump, static_cast(m_dwModelID)); + case eModelInfoType::CLUMP: + success = pGame->GetRenderWare()->ReplaceAllAtomicsInModel(clump, modelID, clump->object.type == RP_TYPE_CLUMP); break; default: break; } - m_pCustomClump = success ? pClump : nullptr; + m_pCustomClump = success ? clump : nullptr; return success; } @@ -1778,6 +1780,80 @@ void CModelInfoSA::MakeClumpModel(ushort usBaseID) CopyStreamingInfoFromModel(usBaseID); } +static bool GetFirstAtomicCB(RpAtomic* atomic, void* data) +{ + RpAtomic** firstAtomic = reinterpret_cast(data); + if (!*firstAtomic) + *firstAtomic = atomic; + + return true; +} + +bool CModelInfoSA::MakeAtomicModel() +{ + if (GetModelType() != eModelInfoType::CLUMP) + return false; + + // Create new interface + CAtomicModelInfoSAInterface* newInterface = new CAtomicModelInfoSAInterface(); + + // Copy existing data to the new interface + CBaseModelInfoSAInterface* pBaseObjectInfo = ppModelInfo[m_dwModelID]; + MemCpyFast(newInterface, pBaseObjectInfo, sizeof(CAtomicModelInfoSAInterface)); + + RpClump* clump = reinterpret_cast(GetInterface()->pRwObject); + RpAtomic* firstAtomic = nullptr; + RpClumpForAllAtomics(clump, GetFirstAtomicCB, &firstAtomic); + + RwFrame* frame = RwFrameCreate(); + RpAtomic* cloned = RpAtomicClone(firstAtomic); + RpSetFrame(cloned, frame); + + newInterface->pRwObject = nullptr; + + // Call CAtomicModelInfo::SetAtomic + ((void(__thiscall*)(CAtomicModelInfoSAInterface*, RpAtomic*))FUNC_CAtomicModelInfo_SetAtomic)(newInterface, cloned); + + // Replace interface with new + delete ppModelInfo[m_dwModelID]; + ppModelInfo[m_dwModelID] = newInterface; + + RpClumpDestroy(clump); + m_dwParentID = -1; + return true; +} + +bool CModelInfoSA::MakeClumpModel() +{ + if (GetModelType() != eModelInfoType::ATOMIC) + return false; + + // Create new interface + CClumpModelInfoSAInterface* newInterface = new CClumpModelInfoSAInterface(); + + // Copy existing data to the new interface + CBaseModelInfoSAInterface* pBaseObjectInfo = ppModelInfo[m_dwModelID]; + MemCpyFast(newInterface, pBaseObjectInfo, sizeof(CClumpModelInfoSAInterface)); + newInterface->m_nAnimFileIndex = -1; + + // Create new empty clump like CFileLoader::LoadClumpFile + RpClump* parentClump = RpClumpCreate(); + RpSetFrame(parentClump, RwFrameCreate()); + + // Set temp atomic to avoid crash because of empty clump + //RpClumpAddAtomic(parentClump, reinterpret_cast(pBaseObjectInfo->pRwObject)); + + // Call CClumpModelInfo::SetClump + ((void(__thiscall*)(CClumpModelInfoSAInterface*, RpClump*))FUNC_CClumpModelInfo_SetClump)(newInterface, parentClump); + + // Replace interface with new + delete reinterpret_cast(ppModelInfo[m_dwModelID]); + ppModelInfo[m_dwModelID] = newInterface; + + m_dwParentID = -1; + return true; +} + void CModelInfoSA::MakeVehicleAutomobile(ushort usBaseID) { CVehicleModelInfoSAInterface* m_pInterface = new CVehicleModelInfoSAInterface(); diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index b261bcc04a..0808d11c87 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -63,6 +63,10 @@ static void* ARRAY_ModelInfo = *(void**)(0x403DA4 + 3); #define VAR_CTempColModels_ModelPed1 0x968DF0 +#define FUNC_CClumpModelInfo_SetClump 0x4C4F70 +#define FUNC_CAtomicModelInfo_SetAtomic 0x4C4360 +#define FUNC_CVisibilityPlugins_SetAtomicId 0x732230 + class CBaseModelInfoSAInterface; class CModelInfoSAInterface { @@ -253,6 +257,10 @@ class CClumpModelInfoSAInterface : public CBaseModelInfoSAInterface }; }; +class CAtomicModelInfoSAInterface : public CBaseModelInfoSAInterface +{ +}; + class CTimeModelInfoSAInterface : public CBaseModelInfoSAInterface { public: @@ -428,7 +436,7 @@ class CModelInfoSA : public CModelInfo void SetVoice(const char* szVoiceType, const char* szVoice); // Custom collision related functions - bool SetCustomModel(RpClump* pClump) override; + bool SetCustomModel(RpClump* clump) override; void RestoreOriginalModel() override; void SetColModel(CColModel* pColModel) override; void RestoreColModel() override; @@ -450,6 +458,8 @@ class CModelInfoSA : public CModelInfo void MakeVehicleAutomobile(ushort usBaseModelID); void MakeTimedObjectModel(ushort usBaseModelID); void MakeClumpModel(ushort usBaseModelID); + bool MakeClumpModel(); + bool MakeAtomicModel(); void DeallocateModel(void); unsigned int GetParentID() { return m_dwParentID; }; diff --git a/Client/game_sa/CRenderWareSA.cpp b/Client/game_sa/CRenderWareSA.cpp index 56f5ce34e4..0de273a9c4 100644 --- a/Client/game_sa/CRenderWareSA.cpp +++ b/Client/game_sa/CRenderWareSA.cpp @@ -477,51 +477,112 @@ unsigned int CRenderWareSA::LoadAtomics(RpClump* pClump, RpAtomicContainer* pAto } // Replaces all atomics for a specific model -typedef struct +struct SAtomicsReplace { - unsigned short usTxdID; - RpClump* pClump; -} SAtomicsReplacer; -bool AtomicsReplacer(RpAtomic* pAtomic, void* data) + std::uint16_t txdID; + RpClump* clump; +}; + +static bool ReplaceAtomicsInModelCB(RpAtomic* atomic, void* data) { - SAtomicsReplacer* pData = reinterpret_cast(data); - SRelatedModelInfo relatedModelInfo = {0}; - relatedModelInfo.pClump = pData->pClump; + auto* cbData = reinterpret_cast(data); + SRelatedModelInfo relatedModelInfo = {}; + relatedModelInfo.pClump = cbData->clump; relatedModelInfo.bDeleteOldRwObject = true; - CFileLoader_SetRelatedModelInfoCB(pAtomic, &relatedModelInfo); - // The above function adds a reference to the model's TXD by either - // calling CAtomicModelInfo::SetAtomic or CDamagableModelInfo::SetDamagedAtomic. Remove it again. - CTxdStore_RemoveRef(pData->usTxdID); + CFileLoader_SetRelatedModelInfoCB(atomic, &relatedModelInfo); + + // The above function adds a reference to the model's TXD by + // calling CDamagableModelInfo::SetDamagedAtomic. Remove it again. + CTxdStore_RemoveRef(cbData->txdID); return true; } -bool CRenderWareSA::ReplaceAllAtomicsInModel(RpClump* pNew, unsigned short usModelID) +struct SAtomicsClone { - CModelInfo* pModelInfo = pGame->GetModelInfo(usModelID); + std::uint16_t modelID; + RpClump* clump; +}; - if (pModelInfo) - { - RpAtomic* pOldAtomic = (RpAtomic*)pModelInfo->GetRwObject(); +static bool CloneAtomicsToClumpCB(RpAtomic* atomic, void* data) +{ + auto* cbData = reinterpret_cast(data); - if (reinterpret_cast(pOldAtomic) != pNew && !DoContainTheSameGeometry(pNew, NULL, pOldAtomic)) - { - // Clone the clump that's to be replaced (FUNC_AtomicsReplacer removes the atomics from the source clump) - RpClump* pCopy = RpClumpClone(pNew); + // Clone atomic + RpAtomic* clone = RpAtomicClone(atomic); + RwFrame* frame = RpGetFrame(atomic); - // Replace the atomics - SAtomicsReplacer data; - data.usTxdID = ((CBaseModelInfoSAInterface**)ARRAY_ModelInfo)[usModelID]->usTextureDictionary; - data.pClump = pCopy; + // Set frames + RpAtomicSetFrame(clone, frame); - MemPutFast((DWORD*)DWORD_AtomicsReplacerModelID, usModelID); - RpClumpForAllAtomics(pCopy, AtomicsReplacer, &data); + // Add atomic to new clump + RpClumpAddAtomic(cbData->clump, clone); - // Get rid of the now empty copied clump - RpClumpDestroy(pCopy); - } + RwFrame* oldFrame = RpGetFrame(atomic); + RwFrame* newFrame = RwFrameCreate(); + RpAtomicSetFrame(clone, newFrame); + RwFrameCopyMatrix(RpGetFrame(clone), oldFrame); + RwFrame* rootFrame = RpGetFrame(cbData->clump); + RwFrameAddChild(rootFrame, newFrame); + + // Update atomic ID like in the CFileLoader::SetRelatedModelInfoCB + // Call CVisibilityPlugins::SetAtomicId + ((int(__cdecl*)(RpAtomic*, std::uint16_t))FUNC_CVisibilityPlugins_SetAtomicId)(clone, cbData->modelID); + + return true; +} + +static bool GetFirstAtomicCB(RpAtomic* atomic, void* data) +{ + RpAtomic** firstAtomic = reinterpret_cast(data); + if (!*firstAtomic) + *firstAtomic = atomic; + + return true; +} + +bool CRenderWareSA::ReplaceAllAtomicsInModel(RpClump* newClump, std::uint16_t modelID, bool isClump) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(modelID); + if (!modelInfo) + return true; + + RpAtomic* oldAtomic = reinterpret_cast(modelInfo->GetRwObject()); + if (reinterpret_cast(oldAtomic) == newClump || DoContainTheSameGeometry(newClump, nullptr, oldAtomic)) + return true; + + // Clone the clump that's to be replaced (CFileLoader_SetRelatedModelInfoCB removes the atomics from the source clump) + RpClump* copy = RpClumpClone(newClump); + + // Check if new model is clump or atomic + if (isClump) + { + SAtomicsClone data = {}; + data.modelID = modelID; + // Get our new clump created in MakeClumpModel + data.clump = reinterpret_cast(modelInfo->GetInterface()->pRwObject); + + //RpAtomic* firstAtomic = nullptr; + //RpClumpForAllAtomics(data.clump, GetFirstAtomicCB, &firstAtomic); + //RpClumpRemoveAtomic(data.clump, firstAtomic); + + // Clone atomics from new model to our empty clump + RpClumpForAllAtomics(copy, CloneAtomicsToClumpCB, &data); + } + else + { + // Replace the atomics + SAtomicsReplace data = {}; + data.txdID = static_cast(ARRAY_ModelInfo)[modelID]->usTextureDictionary; + data.clump = copy; + + MemPutFast(reinterpret_cast(DWORD_AtomicsReplacerModelID), modelID); + RpClumpForAllAtomics(copy, ReplaceAtomicsInModelCB, &data); } + // Destroy empty clump + RpClumpDestroy(copy); + return true; } diff --git a/Client/game_sa/CRenderWareSA.h b/Client/game_sa/CRenderWareSA.h index 6a0e64ea50..438d8224cd 100644 --- a/Client/game_sa/CRenderWareSA.h +++ b/Client/game_sa/CRenderWareSA.h @@ -61,7 +61,7 @@ class CRenderWareSA : public CRenderWare unsigned int LoadAtomics(RpClump* pClump, RpAtomicContainer* pAtomics); // Replaces all atomics for a specific model - bool ReplaceAllAtomicsInModel(RpClump* pSrc, unsigned short usModelID) override; + bool ReplaceAllAtomicsInModel(RpClump* newClump, std::uint16_t modelID, bool isClump = false) override; // Replaces all atomics in a clump void ReplaceAllAtomicsInClump(RpClump* pDst, RpAtomicContainer* pAtomics, unsigned int uiAtomics); diff --git a/Client/game_sa/gamesa_renderware.h b/Client/game_sa/gamesa_renderware.h index 594c203b59..79cd4b17b9 100644 --- a/Client/game_sa/gamesa_renderware.h +++ b/Client/game_sa/gamesa_renderware.h @@ -105,6 +105,7 @@ typedef RpHAnimHierarchy*(__cdecl* GetAnimHierarchyFromSkinClump_t)(RpClump*); typedef int(__cdecl* RpHAnimIDGetIndex_t)(RpHAnimHierarchy*, int); typedef RwMatrix*(__cdecl* RpHAnimHierarchyGetMatrixArray_t)(RpHAnimHierarchy*); typedef RtQuat*(__cdecl* RtQuatRotate_t)(RtQuat* quat, const RwV3d* axis, float angle, RwOpCombineType combineOp); +typedef RpClump*(__cdecl* RpClumpCreate_t)(); /*****************************************************************************/ /** Renderware function mappings **/ @@ -195,6 +196,7 @@ RWFUNC(GetAnimHierarchyFromSkinClump_t GetAnimHierarchyFromSkinClump, (GetAnimHi RWFUNC(RpHAnimIDGetIndex_t RpHAnimIDGetIndex, (RpHAnimIDGetIndex_t)0xDEAD) RWFUNC(RpHAnimHierarchyGetMatrixArray_t RpHAnimHierarchyGetMatrixArray, (RpHAnimHierarchyGetMatrixArray_t)0xDEAD) RWFUNC(RtQuatRotate_t RtQuatRotate, (RtQuatRotate_t)0xDEAD) +RWFUNC(RpClumpCreate_t RpClumpCreate, reinterpret_cast(0xDEAD)) /*****************************************************************************/ /** GTA function definitions and mappings **/ diff --git a/Client/game_sa/gamesa_renderware.hpp b/Client/game_sa/gamesa_renderware.hpp index a638cfede0..4868288b14 100644 --- a/Client/game_sa/gamesa_renderware.hpp +++ b/Client/game_sa/gamesa_renderware.hpp @@ -89,7 +89,8 @@ void InitRwFunctions() RpHAnimIDGetIndex = (RpHAnimIDGetIndex_t)0x7C51A0; RpHAnimHierarchyGetMatrixArray = (RpHAnimHierarchyGetMatrixArray_t)0x7C5120; RtQuatRotate = (RtQuatRotate_t)0x7EB7C0; - + RpClumpCreate = reinterpret_cast(0x74A290); + SetTextureDict = (SetTextureDict_t)0x007319C0; LoadClumpFile = (LoadClumpFile_t)0x005371F0; LoadModel = (LoadModel_t)0x0040C6B0; diff --git a/Client/mods/deathmatch/logic/CClientDFF.cpp b/Client/mods/deathmatch/logic/CClientDFF.cpp index 7eb2a40a70..c4fbe31926 100644 --- a/Client/mods/deathmatch/logic/CClientDFF.cpp +++ b/Client/mods/deathmatch/logic/CClientDFF.cpp @@ -99,18 +99,18 @@ void CClientDFF::UnloadDFF() m_LoadedClumpInfoMap.clear(); } -bool CClientDFF::ReplaceModel(unsigned short usModel, bool bAlphaTransparency) +bool CClientDFF::ReplaceModel(std::uint16_t model, bool alphaTransparency) { // Record attempt in case it all goes wrong CArgMap argMap; - argMap.Set("id", usModel); + argMap.Set("id", model); argMap.Set("reason", "ReplaceModel"); SetApplicationSetting("diagnostics", "gta-model-fail", argMap.ToString()); - bool bResult = DoReplaceModel(usModel, bAlphaTransparency); + bool result = DoReplaceModel(model, alphaTransparency); SetApplicationSetting("diagnostics", "gta-model-fail", ""); - return bResult; + return result; } bool CClientDFF::LoadFromFile(SString filePath) @@ -134,65 +134,55 @@ bool CClientDFF::LoadFromBuffer(SString buffer) return true; } -bool CClientDFF::DoReplaceModel(unsigned short usModel, bool bAlphaTransparency) +bool CClientDFF::DoReplaceModel(std::uint16_t model, bool alphaTransparency) { - if (!CClientDFFManager::IsReplacableModel(usModel)) + // Check if model is replaceable + if (!CClientDFFManager::IsReplacableModel(model)) return false; // Get clump loaded for this model id - RpClump* pClump = GetLoadedClump(usModel); + RpClump* clump = GetLoadedClump(model); + if (!clump) + return false; - // We have a DFF? - if (pClump) + // Have someone already replaced that model? + // Remove it from its list so it won't restore the object if + // it's destroyed or its resource is when it's been replaced + // again by an another resource. + CClientDFF* replaced = m_pDFFManager->GetElementThatReplaced(model); + if (replaced) + replaced->m_Replaced.remove(model); + + // Is this a vehicle, ped, marker or object ID? + if (CClientVehicleManager::IsValidModel(model)) + return ReplaceVehicleModel(clump, model, alphaTransparency); + else if (CClientPlayerManager::IsValidModel(model)) + return ReplacePedModel(clump, model, alphaTransparency); + else if (CClientMarkerManager::IsMarkerModel(model)) { - // Have someone already replaced that model? - CClientDFF* pReplaced = m_pDFFManager->GetElementThatReplaced(usModel); - if (pReplaced) - { - // Remove it from its list so it won't restore the object if - // it's destroyed or its resource is when it's been replaced - // again by an another resource. - pReplaced->m_Replaced.remove(usModel); - } + bool wasReplaced = ReplaceObjectModel(clump, model, alphaTransparency); + // 'Restream' 3D markers + if (wasReplaced) + g_pClientGame->ReinitMarkers(); - // Is this a vehicle ID? - if (CClientVehicleManager::IsValidModel(usModel)) - { - return ReplaceVehicleModel(pClump, usModel, bAlphaTransparency); - } - else if (CClientPlayerManager::IsValidModel(usModel)) + return wasReplaced; + } + else if (CClientObjectManager::IsValidModel(model)) + { + // Is this a vehicle upgrade or weapon ID? + if (CVehicleUpgrades::IsUpgrade(model)) { - return ReplacePedModel(pClump, usModel, bAlphaTransparency); + bool result = ReplaceObjectModel(clump, model, alphaTransparency); + // 'Restream' upgrades after model replacement + m_pManager->GetVehicleManager()->RestreamVehicleUpgrades(model); + return result; } - else if (CClientMarkerManager::IsMarkerModel(usModel)) - { - bool wasReplaced = ReplaceObjectModel(pClump, usModel, bAlphaTransparency); - - // 'Restream' 3D markers - if (wasReplaced) - g_pClientGame->ReinitMarkers(); + else if (CClientPedManager::IsValidWeaponModel(model)) + return ReplaceWeaponModel(clump, model, alphaTransparency); - return wasReplaced; - } - else if (CClientObjectManager::IsValidModel(usModel)) - { - if (CVehicleUpgrades::IsUpgrade(usModel)) - { - bool bResult = ReplaceObjectModel(pClump, usModel, bAlphaTransparency); - // 'Restream' upgrades after model replacement - m_pManager->GetVehicleManager()->RestreamVehicleUpgrades(usModel); - return bResult; - } - if (CClientPedManager::IsValidWeaponModel(usModel)) - { - return ReplaceWeaponModel(pClump, usModel, bAlphaTransparency); - } - return ReplaceObjectModel(pClump, usModel, bAlphaTransparency); - } + // Replace object if this is a object ID + return ReplaceObjectModel(clump, model, alphaTransparency); } - - // No supported type or no loaded clump - return false; } bool CClientDFF::HasReplaced(unsigned short usModel) @@ -296,24 +286,28 @@ void CClientDFF::InternalRestoreModel(unsigned short usModel) } } -bool CClientDFF::ReplaceObjectModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency) +bool CClientDFF::ReplaceObjectModel(RpClump* clump, std::uint16_t model, bool alphaTransparency) { // Stream out all the object models with matching ID. // Streamer will stream them back in async after a frame // or so. - m_pManager->GetObjectManager()->RestreamObjects(usModel); - g_pGame->GetModelInfo(usModel)->RestreamIPL(); + m_pManager->GetObjectManager()->RestreamObjects(model); + g_pGame->GetModelInfo(model)->RestreamIPL(); // Grab the model info for that model and replace the model - CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModel); + CModelInfo* pModelInfo = g_pGame->GetModelInfo(model); - if (!pModelInfo->SetCustomModel(pClump)) + // If new model is clump then we need to convert existing model + if (clump->object.type == RP_TYPE_CLUMP) + pModelInfo->MakeClumpModel(); + + if (!pModelInfo->SetCustomModel(clump)) return false; - pModelInfo->SetAlphaTransparencyEnabled(bAlphaTransparency); + pModelInfo->SetAlphaTransparencyEnabled(alphaTransparency); // Remember that we've replaced that object model - m_Replaced.push_back(usModel); + m_Replaced.push_back(model); // Success return true; diff --git a/Client/mods/deathmatch/logic/CClientDFF.h b/Client/mods/deathmatch/logic/CClientDFF.h index 3b6392c561..6f58c573b5 100644 --- a/Client/mods/deathmatch/logic/CClientDFF.h +++ b/Client/mods/deathmatch/logic/CClientDFF.h @@ -37,7 +37,7 @@ class CClientDFF final : public CClientEntity bool Load(bool isRaw, SString input); - bool ReplaceModel(unsigned short usModel, bool bAlphaTransparency); + bool ReplaceModel(std::uint16_t model, bool alphaTransparency); bool HasReplaced(unsigned short usModel); @@ -55,11 +55,11 @@ class CClientDFF final : public CClientEntity bool LoadFromFile(SString filePath); bool LoadFromBuffer(SString buffer); - bool DoReplaceModel(unsigned short usModel, bool bAlphaTransparency); + bool DoReplaceModel(std::uint16_t model, bool alphaTransparency); void UnloadDFF(); void InternalRestoreModel(unsigned short usModel); - bool ReplaceObjectModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); + bool ReplaceObjectModel(RpClump* clump, std::uint16_t model, bool alphaTransparency); bool ReplaceVehicleModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); bool ReplaceWeaponModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); bool ReplacePedModel(RpClump* pClump, ushort usModel, bool bAlphaTransparency); diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 2a9eeb0c8d..02453c8b87 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -93,6 +93,12 @@ bool CClientModel::Deallocate() if (!pModelInfo || !pModelInfo->IsValid()) return false; + /* if (m_convertedToClump) + { + pModelInfo->MakeAtomicModel(); + m_convertedToClump = false; + }*/ + if (m_eModelType != eClientModelType::TXD) { // Remove model info diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 7ca02230d5..c3696d89b3 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -81,7 +81,7 @@ void CLuaEngineDefs::LoadFunctions() {"engineImportTXD", EngineImportTXD}, {"engineReplaceCOL", EngineReplaceCOL}, {"engineRestoreCOL", EngineRestoreCOL}, - {"engineReplaceModel", EngineReplaceModel}, + {"engineReplaceModel", ArgumentParserWarn}, {"engineRestoreModel", EngineRestoreModel}, {"engineReplaceAnimation", EngineReplaceAnimation}, {"engineRestoreAnimation", EngineRestoreAnimation}, @@ -790,40 +790,32 @@ bool CLuaEngineDefs::EngineRestoreTXDImage(uint uiModelID) return false; } -int CLuaEngineDefs::EngineReplaceModel(lua_State* luaVM) +bool CLuaEngineDefs::EngineReplaceModel(CClientDFF* dff, std::variant model, std::optional alphaTransparency) { - CClientDFF* pDFF; - SString strModelName; - bool bAlphaTransparency; + // For backwards compatibility + if (std::holds_alternative(model)) + model = std::to_string(std::get(model)); - CScriptArgReader argStream(luaVM); - argStream.ReadUserData(pDFF); - argStream.ReadString(strModelName); - argStream.ReadBool(bAlphaTransparency, false); + // Is it possible? + if (!std::holds_alternative(model)) + return false; - if (!argStream.HasErrors()) + // Get the real model ID + std::uint16_t modelID = CModelNames::ResolveModelID(std::get(model)); + if (modelID == INVALID_MODEL_ID) + throw LuaFunctionError("Expected valid model ID or name at argument 2", false); + + // Fixes vehicle dff leak problem with engineReplaceModel + m_pDFFManager->RestoreModel(modelID); + + if (!dff->ReplaceModel(modelID, alphaTransparency.value_or(false))) { - ushort usModelID = CModelNames::ResolveModelID(strModelName); - if (usModelID != INVALID_MODEL_ID) - { - // Fixes vehicle dff leak problem with engineReplaceModel - m_pDFFManager->RestoreModel(usModelID); - if (pDFF->ReplaceModel(usModelID, bAlphaTransparency)) - { - lua_pushboolean(luaVM, true); - return 1; - } - else - argStream.SetCustomError(SString("Model ID %d replace failed", usModelID)); - } - else - argStream.SetCustomError("Expected valid model ID or name at argument 2"); + char errorMsg[32]; + snprintf(errorMsg, sizeof(errorMsg), "Model %d replace failed"); + throw LuaFunctionError(errorMsg, false); } - if (argStream.HasErrors()) - m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); - lua_pushboolean(luaVM, false); - return 1; + return true; } int CLuaEngineDefs::EngineRestoreModel(lua_State* luaVM) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index a67ecfc68d..b77b3e85fe 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -26,7 +26,7 @@ class CLuaEngineDefs : public CLuaDefs LUA_DECLARE(EngineImportTXD); LUA_DECLARE(EngineReplaceCOL); LUA_DECLARE(EngineRestoreCOL); - LUA_DECLARE(EngineReplaceModel); + static bool EngineReplaceModel(CClientDFF* dff, std::variant model, std::optional alphaTransparency); LUA_DECLARE(EngineRestoreModel); LUA_DECLARE(EngineRequestModel); LUA_DECLARE(EngineFreeModel); diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 08cb032e4c..068baf53a3 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -189,6 +189,9 @@ class CModelInfo virtual bool IsAlphaTransparencyEnabled() = 0; virtual void ResetAlphaTransparency() = 0; + virtual bool MakeClumpModel() = 0; + virtual bool MakeAtomicModel() = 0; + // ONLY use for CVehicleModelInfos virtual short GetAvailableVehicleMod(unsigned short usSlot) = 0; virtual bool IsUpgradeAvailable(eVehicleUpgradePosn posn) = 0; @@ -217,7 +220,7 @@ class CModelInfo virtual void SetVoice(const char* szVoiceType, const char* szVoice) = 0; // Custom collision related functions - virtual bool SetCustomModel(RpClump* pClump) = 0; + virtual bool SetCustomModel(RpClump* clump) = 0; virtual void RestoreOriginalModel() = 0; virtual void SetColModel(CColModel* pColModel) = 0; virtual void RestoreColModel() = 0; diff --git a/Client/sdk/game/CRenderWare.h b/Client/sdk/game/CRenderWare.h index bef9d8f79e..3eda3cc3e8 100644 --- a/Client/sdk/game/CRenderWare.h +++ b/Client/sdk/game/CRenderWare.h @@ -87,7 +87,7 @@ class CRenderWare virtual void DestroyTexture(RwTexture* pTex) = 0; virtual void ReplaceCollisions(CColModel* pColModel, unsigned short usModelID) = 0; virtual unsigned int LoadAtomics(RpClump* pClump, RpAtomicContainer* pAtomics) = 0; - virtual bool ReplaceAllAtomicsInModel(RpClump* pSrc, unsigned short usModelID) = 0; + virtual bool ReplaceAllAtomicsInModel(RpClump* newClump, std::uint16_t modelID, bool isClump = false) = 0; virtual void ReplaceAllAtomicsInClump(RpClump* pDst, RpAtomicContainer* pAtomics, unsigned int uiAtomics) = 0; virtual void ReplaceWheels(RpClump* pClump, RpAtomicContainer* pAtomics, unsigned int uiAtomics, const char* szWheel) = 0; virtual void RepositionAtomic(RpClump* pDst, RpClump* pSrc, const char* szName) = 0;