From 16aea42ff45c0e70f83b5ba649ae1fe6d9b4c82a Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:00:21 +0100 Subject: [PATCH 01/20] initial commit --- Client/game_sa/CModelInfoSA.cpp | 17 +++ Client/game_sa/CModelInfoSA.h | 2 + Client/game_sa/CVehicleSA.cpp | 36 ++++- Client/mods/deathmatch/logic/CClientGame.cpp | 2 +- Client/mods/deathmatch/logic/CClientModel.cpp | 48 +++++++ Client/mods/deathmatch/logic/CClientModel.h | 1 + .../deathmatch/logic/CVehicleUpgrades.cpp | 130 +++++++++++++++++- .../mods/deathmatch/logic/CVehicleUpgrades.h | 7 +- .../logic/lua/CLuaFunctionParseHelpers.cpp | 1 + .../logic/luadefs/CLuaEngineDefs.cpp | 3 + Client/sdk/game/CModelInfo.h | 2 + 11 files changed, 239 insertions(+), 10 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 927d645be99..1fb072ddc47 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1850,6 +1850,23 @@ void CModelInfoSA::MakeVehicleAutomobile(ushort usBaseID) CopyStreamingInfoFromModel(usBaseID); } +void CModelInfoSA::MakeVehicleUpgradeModel(ushort usBaseID) +{ + CBaseModelInfoSAInterface* m_pInterface = new CBaseModelInfoSAInterface(); + + CBaseModelInfoSAInterface* pBaseObjectInfo = ppModelInfo[usBaseID]; + MemCpyFast(m_pInterface, pBaseObjectInfo, sizeof(CBaseModelInfoSAInterface)); + m_pInterface->usNumberOfRefs = 0; + m_pInterface->pRwObject = nullptr; + m_pInterface->usUnknown = 65535; + m_pInterface->usDynamicIndex = 65535; + + ppModelInfo[m_dwModelID] = m_pInterface; + + m_dwParentID = usBaseID; + CopyStreamingInfoFromModel(usBaseID); +} + void CModelInfoSA::DeallocateModel(void) { Remove(); diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index b778f808be1..f657c9dd6dc 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -471,12 +471,14 @@ class CModelInfoSA : public CModelInfo void SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; } RwObject* GetRwObject() { return m_pInterface ? m_pInterface->pRwObject : NULL; } + void SetRwObject(RwObject* pRwObject) { if (m_pInterface) m_pInterface->pRwObject = pRwObject; } // CModelInfoSA methods void MakePedModel(const char* szTexture); void MakeObjectModel(ushort usBaseModelID); void MakeObjectDamageableModel(std::uint16_t usBaseModelID) override; void MakeVehicleAutomobile(ushort usBaseModelID); + void MakeVehicleUpgradeModel(ushort usBaseModelID); void MakeTimedObjectModel(ushort usBaseModelID); void MakeClumpModel(ushort usBaseModelID); void DeallocateModel(void); diff --git a/Client/game_sa/CVehicleSA.cpp b/Client/game_sa/CVehicleSA.cpp index e5620f01439..90d40354289 100644 --- a/Client/game_sa/CVehicleSA.cpp +++ b/Client/game_sa/CVehicleSA.cpp @@ -765,7 +765,19 @@ void CVehicleSA::LockDoors(bool bLocked) void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) { - if (dwModelID >= 1000 && dwModelID <= 1193) + DWORD dwActualModelID = dwModelID; + + CModelInfo* pModelInfo = pGame->GetModelInfo(dwModelID); + if (pModelInfo && pModelInfo->GetParentID() != 0) + { + unsigned int parentID = pModelInfo->GetParentID(); + if (parentID >= 1000 && parentID <= 1193) + { + dwActualModelID = parentID; + } + } + + if (dwActualModelID >= 1000 && dwActualModelID <= 1193) { DWORD dwThis = (DWORD)m_pInterface; @@ -773,7 +785,7 @@ void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) __asm { mov ecx, dwThis - push dwModelID + push dwActualModelID call dwFunc } } @@ -781,13 +793,31 @@ void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) void CVehicleSA::RemoveVehicleUpgrade(DWORD dwModelID) { + DWORD dwActualModelID = dwModelID; + + CModelInfo* pModelInfo = pGame->GetModelInfo(dwModelID); + if (pModelInfo && pModelInfo->GetParentID() != 0) + { + unsigned int parentID = pModelInfo->GetParentID(); + if (parentID >= 1000 && parentID <= 1193) + { + dwActualModelID = parentID; + + CModelInfo* pParentModelInfo = pGame->GetModelInfo(parentID); + if (pParentModelInfo) + { + pParentModelInfo->Request(BLOCKING, "CVehicleSA::RemoveVehicleUpgrade restore"); + } + } + } + DWORD dwThis = (DWORD)m_pInterface; DWORD dwFunc = FUNC_CVehicle_RemoveVehicleUpgrade; __asm { mov ecx, dwThis - push dwModelID + push dwActualModelID call dwFunc } diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 0d057bf424f..674537ccb03 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -6906,7 +6906,7 @@ void CClientGame::Restream(std::optional option) if (option == RestreamOption::ALL || option == RestreamOption::OBJECTS) { static constexpr eClientModelType restreamTypes[] = {eClientModelType::OBJECT, eClientModelType::OBJECT_DAMAGEABLE, eClientModelType::TIMED_OBJECT, - eClientModelType::CLUMP}; + eClientModelType::CLUMP, eClientModelType::VEHICLE_UPGRADE}; for (eClientModelType type : restreamTypes) { diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index a301c5fed26..308e9c5228b 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -83,6 +83,15 @@ bool CClientModel::Allocate(ushort usParentID) return true; } break; + case eClientModelType::VEHICLE_UPGRADE: + { + if (CVehicleUpgrades::IsUpgrade(usParentID)) + { + pModelInfo->MakeVehicleUpgradeModel(usParentID); + return true; + } + break; + } default: return false; } @@ -97,6 +106,45 @@ bool CClientModel::Deallocate() SetParentResource(nullptr); + // If this is a custom vehicle upgrade model, clean it up from all vehicles + if (m_eModelType == eClientModelType::VEHICLE_UPGRADE) + { + CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true); + if (pModelInfo) + { + unsigned int parentID = pModelInfo->GetParentID(); + + CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); + if (pVehicleManager) + { + // STEP 1: Restore original RwObject FIRST (before any removal) + if (parentID != 0) + { + bool bRestored = false; + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd() && !bRestored; ++iter) + { + CClientVehicle* pVehicle = *iter; + if (pVehicle && pVehicle->GetUpgrades()) + { + pVehicle->GetUpgrades()->RestoreOriginalRwObject(static_cast(parentID)); + bRestored = true; + } + } + } + + // STEP 2: Remove upgrade WITHOUT restoring RwObject (it's being deallocated) + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) + { + CClientVehicle* pVehicle = *iter; + if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) + { + pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID, false); + } + } + } + } + } + CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true); if (!pModelInfo || !pModelInfo->IsValid()) return false; diff --git a/Client/mods/deathmatch/logic/CClientModel.h b/Client/mods/deathmatch/logic/CClientModel.h index 759890e0e57..0b3859226e8 100644 --- a/Client/mods/deathmatch/logic/CClientModel.h +++ b/Client/mods/deathmatch/logic/CClientModel.h @@ -20,6 +20,7 @@ enum class eClientModelType OBJECT, OBJECT_DAMAGEABLE, VEHICLE, + VEHICLE_UPGRADE, TIMED_OBJECT, CLUMP, TXD, diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 72872162f48..3ccfdfd2c8c 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -52,6 +52,16 @@ bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) unsigned short us = usUpgrade; eClientVehicleType vehicleType = m_pVehicle->GetVehicleType(); + auto* upgradeModelInfo = g_pGame->GetModelInfo(us); + if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) + { + unsigned int parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(static_cast(parentID))) + { + us = static_cast(parentID); + } + } + // No upgrades for trains/boats if (vehicleType == CLIENTVEHICLE_TRAIN || vehicleType == CLIENTVEHICLE_BOAT) return false; @@ -452,6 +462,17 @@ bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) bool CVehicleUpgrades::GetSlotFromUpgrade(unsigned short us, unsigned char& ucSlot) { + // Check if this is a custom upgrade model + auto* upgradeModelInfo = g_pGame->GetModelInfo(us); + if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) + { + unsigned int parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(static_cast(parentID))) + { + us = static_cast(parentID); + } + } + if (us == 1011 || us == 1012 || us == 1111 || us == 1112 || us == 1142 || /* bonet */ us == 1143 || us == 1144 || us == 1145) { @@ -611,18 +632,48 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) CVehicle* pVehicle = m_pVehicle->GetGameVehicle(); if (pVehicle) { - // Grab the upgrade model + // Load the upgrade model CModelInfo* pModelInfo = g_pGame->GetModelInfo(usUpgrade); if (pModelInfo) { if (!g_pGame->IsASyncLoadingEnabled() || !pModelInfo->IsLoaded()) { - // Request and load now pModelInfo->Request(BLOCKING, "CVehicleUpgrades::ForceAddUpgrade"); } - // Add the upgrade - pVehicle->AddVehicleUpgrade(usUpgrade); + + // If this is a custom model with parent ID, swap RwObjects + unsigned int parentID = pModelInfo->GetParentID(); + if (parentID != 0 && IsUpgrade(static_cast(parentID))) + { + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast(parentID)); + if (pParentModelInfo) + { + if (!g_pGame->IsASyncLoadingEnabled() || !pParentModelInfo->IsLoaded()) + { + pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::ForceAddUpgrade (parent)"); + } + + // Wait for both to be loaded + if (pModelInfo->IsLoaded() && pParentModelInfo->IsLoaded()) + { + RwObject* pCustomRwObject = pModelInfo->GetRwObject(); + RwObject* pParentRwObject = pParentModelInfo->GetRwObject(); + + if (pCustomRwObject && pParentRwObject) + { + // ALWAYS update the stored original (parent model may have been reloaded) + m_OriginalRwObjects[static_cast(parentID)] = pParentRwObject; + + // Swap RwObject + pParentModelInfo->SetRwObject(pCustomRwObject); + } + } + } + } } + + // Add the upgrade (uses parent ID internally) + pVehicle->AddVehicleUpgrade(usUpgrade); } // Add it to the slot @@ -646,6 +697,24 @@ void CVehicleUpgrades::RestreamVehicleUpgrades(unsigned short usUpgrade) } } +void CVehicleUpgrades::RestoreOriginalRwObject(unsigned short usParentID) +{ + auto it = m_OriginalRwObjects.find(usParentID); + if (it != m_OriginalRwObjects.end()) + { + // Instead of restoring the old pointer, FORCE RELOAD the parent model + // This ensures we get a fresh, valid RwObject + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); + if (pParentModelInfo) + { + // Force the parent model to reload its original geometry + pParentModelInfo->RemoveRef(); + pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::RestoreOriginalRwObject"); + } + m_OriginalRwObjects.erase(it); + } +} + bool CVehicleUpgrades::HasUpgrade(unsigned short usUpgrade) { unsigned char ucSlot = 0; @@ -658,7 +727,7 @@ bool CVehicleUpgrades::HasUpgrade(unsigned short usUpgrade) return false; } -bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade) +bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwObject) { if (HasUpgrade(usUpgrade)) { @@ -667,6 +736,46 @@ bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade) CVehicle* pVehicle = m_pVehicle->GetGameVehicle(); if (pVehicle) { + // Restore original RwObject if custom upgrade AND restore is enabled + if (bRestoreRwObject) + { + auto* upgradeModelInfo = g_pGame->GetModelInfo(usUpgrade); + if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) + { + unsigned int parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(static_cast(parentID))) + { + auto it = m_OriginalRwObjects.find(static_cast(parentID)); + if (it != m_OriginalRwObjects.end()) + { + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast(parentID)); + if (pParentModelInfo) + { + RwObject* pCurrentParentRwObject = pParentModelInfo->GetRwObject(); + if (pCurrentParentRwObject != it->second && it->second) + { + pParentModelInfo->SetRwObject(it->second); + } + } + m_OriginalRwObjects.erase(it); + } + } + } + } + else + { + // Just clear from map without restore (deallocation scenario) + auto* upgradeModelInfo = g_pGame->GetModelInfo(usUpgrade); + if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) + { + unsigned int parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(static_cast(parentID))) + { + m_OriginalRwObjects.erase(static_cast(parentID)); + } + } + } + pVehicle->RemoveVehicleUpgrade(usUpgrade); } @@ -717,6 +826,17 @@ void CVehicleUpgrades::ReAddAll() void CVehicleUpgrades::RemoveAll(bool bRipFromVehicle) { + // Restore all original RwObjects + for (auto& pair : m_OriginalRwObjects) + { + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(pair.first); + if (pParentModelInfo) + { + pParentModelInfo->SetRwObject(pair.second); + } + } + m_OriginalRwObjects.clear(); + unsigned char ucSlot = 0; for (; ucSlot < VEHICLE_UPGRADE_SLOTS; ucSlot++) { diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index 85aa20610d1..c82f4c61ecf 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -10,9 +10,12 @@ #pragma once +#include + #define VEHICLE_UPGRADE_SLOTS 17 class CClientVehicle; +struct RwObject; enum eVehicleUpgrade { @@ -37,7 +40,7 @@ class CVehicleUpgrades void AddAllUpgrades(); void ForceAddUpgrade(unsigned short usUpgrade); bool HasUpgrade(unsigned short usUpgrade); - bool RemoveUpgrade(unsigned short usUpgrade); + bool RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwObject = true); unsigned short GetSlotState(unsigned char ucSlot); const SSlotStates& GetSlotStates() { return m_SlotStates; } static const char* GetSlotName(unsigned char ucSlot); @@ -46,10 +49,12 @@ class CVehicleUpgrades void RemoveAll(bool bRipFromVehicle); void RestreamVehicleUpgrades(unsigned short usModel); + void RestoreOriginalRwObject(unsigned short usParentID); protected: SSlotStates m_SlotStates; CClientVehicle* m_pVehicle; ushort m_usLastLocalAddNitroType; CElapsedTime m_lastLocalAddNitroTimer; + std::map m_OriginalRwObjects; }; diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index f056360de43..eed5576186e 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -712,6 +712,7 @@ ADD_ENUM(eClientModelType::PED, "ped") ADD_ENUM(eClientModelType::OBJECT, "object") ADD_ENUM(eClientModelType::OBJECT_DAMAGEABLE, "object-damageable") ADD_ENUM(eClientModelType::VEHICLE, "vehicle") +ADD_ENUM(eClientModelType::VEHICLE_UPGRADE, "vehicle-upgrade") ADD_ENUM(eClientModelType::TIMED_OBJECT, "timed-object") ADD_ENUM(eClientModelType::CLUMP, "clump") IMPLEMENT_ENUM_CLASS_END("client-model-type") diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 0e0fc086d8d..50593c4d61e 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -954,6 +954,9 @@ int CLuaEngineDefs::EngineRequestModel(lua_State* luaVM) case eClientModelType::VEHICLE: usParentID = static_cast(VehicleType::VT_LANDSTAL); break; + case eClientModelType::VEHICLE_UPGRADE: + usParentID = 1025; + break; default: break; } diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 7e5cc8ec65e..e761236d4ad 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -240,10 +240,12 @@ class CModelInfo // Call this to make sure the custom vehicle models are being used after a load. virtual void MakeCustomModel() = 0; virtual RwObject* GetRwObject() = 0; + virtual void SetRwObject(RwObject* pRwObject) = 0; virtual void MakePedModel(const char* szTexture) = 0; virtual void MakeObjectModel(unsigned short usBaseID) = 0; virtual void MakeObjectDamageableModel(std::uint16_t baseID) = 0; virtual void MakeVehicleAutomobile(unsigned short usBaseID) = 0; + virtual void MakeVehicleUpgradeModel(unsigned short usBaseID) = 0; virtual void MakeTimedObjectModel(unsigned short usBaseID) = 0; virtual void MakeClumpModel(unsigned short usBaseID) = 0; From 12aa4324dc8fbd50147222b298cbb842cf185e6d Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:09:45 +0100 Subject: [PATCH 02/20] work --- Client/mods/deathmatch/logic/CClientModel.cpp | 34 ++-------- .../deathmatch/logic/CVehicleUpgrades.cpp | 66 ++++--------------- .../mods/deathmatch/logic/CVehicleUpgrades.h | 1 - 3 files changed, 17 insertions(+), 84 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 308e9c5228b..94e27c3ae15 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -109,37 +109,15 @@ bool CClientModel::Deallocate() // If this is a custom vehicle upgrade model, clean it up from all vehicles if (m_eModelType == eClientModelType::VEHICLE_UPGRADE) { - CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true); - if (pModelInfo) + CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); + if (pVehicleManager) { - unsigned int parentID = pModelInfo->GetParentID(); - - CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); - if (pVehicleManager) + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) { - // STEP 1: Restore original RwObject FIRST (before any removal) - if (parentID != 0) - { - bool bRestored = false; - for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd() && !bRestored; ++iter) - { - CClientVehicle* pVehicle = *iter; - if (pVehicle && pVehicle->GetUpgrades()) - { - pVehicle->GetUpgrades()->RestoreOriginalRwObject(static_cast(parentID)); - bRestored = true; - } - } - } - - // STEP 2: Remove upgrade WITHOUT restoring RwObject (it's being deallocated) - for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) + CClientVehicle* pVehicle = *iter; + if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) { - CClientVehicle* pVehicle = *iter; - if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) - { - pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID, false); - } + pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID, true); } } } diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 3ccfdfd2c8c..51f9bcaf4fd 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -661,10 +661,7 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) if (pCustomRwObject && pParentRwObject) { - // ALWAYS update the stored original (parent model may have been reloaded) - m_OriginalRwObjects[static_cast(parentID)] = pParentRwObject; - - // Swap RwObject + // Just swap - we'll set to NULL on restore pParentModelInfo->SetRwObject(pCustomRwObject); } } @@ -699,19 +696,15 @@ void CVehicleUpgrades::RestreamVehicleUpgrades(unsigned short usUpgrade) void CVehicleUpgrades::RestoreOriginalRwObject(unsigned short usParentID) { - auto it = m_OriginalRwObjects.find(usParentID); - if (it != m_OriginalRwObjects.end()) + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); + if (pParentModelInfo) { - // Instead of restoring the old pointer, FORCE RELOAD the parent model - // This ensures we get a fresh, valid RwObject - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); - if (pParentModelInfo) - { - // Force the parent model to reload its original geometry - pParentModelInfo->RemoveRef(); - pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::RestoreOriginalRwObject"); - } - m_OriginalRwObjects.erase(it); + // Set to NULL to clear any swapped pointer + pParentModelInfo->SetRwObject(NULL); + + // Force reload the parent model to get fresh RwObject + pParentModelInfo->RemoveRef(); + pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::RestoreOriginalRwObject"); } } @@ -736,7 +729,7 @@ bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwOb CVehicle* pVehicle = m_pVehicle->GetGameVehicle(); if (pVehicle) { - // Restore original RwObject if custom upgrade AND restore is enabled + // Restore parent model if custom upgrade if (bRestoreRwObject) { auto* upgradeModelInfo = g_pGame->GetModelInfo(usUpgrade); @@ -745,33 +738,7 @@ bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwOb unsigned int parentID = upgradeModelInfo->GetParentID(); if (IsUpgrade(static_cast(parentID))) { - auto it = m_OriginalRwObjects.find(static_cast(parentID)); - if (it != m_OriginalRwObjects.end()) - { - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast(parentID)); - if (pParentModelInfo) - { - RwObject* pCurrentParentRwObject = pParentModelInfo->GetRwObject(); - if (pCurrentParentRwObject != it->second && it->second) - { - pParentModelInfo->SetRwObject(it->second); - } - } - m_OriginalRwObjects.erase(it); - } - } - } - } - else - { - // Just clear from map without restore (deallocation scenario) - auto* upgradeModelInfo = g_pGame->GetModelInfo(usUpgrade); - if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) - { - unsigned int parentID = upgradeModelInfo->GetParentID(); - if (IsUpgrade(static_cast(parentID))) - { - m_OriginalRwObjects.erase(static_cast(parentID)); + RestoreOriginalRwObject(static_cast(parentID)); } } } @@ -826,17 +793,6 @@ void CVehicleUpgrades::ReAddAll() void CVehicleUpgrades::RemoveAll(bool bRipFromVehicle) { - // Restore all original RwObjects - for (auto& pair : m_OriginalRwObjects) - { - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(pair.first); - if (pParentModelInfo) - { - pParentModelInfo->SetRwObject(pair.second); - } - } - m_OriginalRwObjects.clear(); - unsigned char ucSlot = 0; for (; ucSlot < VEHICLE_UPGRADE_SLOTS; ucSlot++) { diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index c82f4c61ecf..09d5d4d061a 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -56,5 +56,4 @@ class CVehicleUpgrades CClientVehicle* m_pVehicle; ushort m_usLastLocalAddNitroType; CElapsedTime m_lastLocalAddNitroTimer; - std::map m_OriginalRwObjects; }; From 7c23db7affd1a88490da04b9ba3a5b902f9d489d Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:15:50 +0100 Subject: [PATCH 03/20] fix --- Client/game_sa/CVehicleSA.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Client/game_sa/CVehicleSA.cpp b/Client/game_sa/CVehicleSA.cpp index 90d40354289..64b32869cce 100644 --- a/Client/game_sa/CVehicleSA.cpp +++ b/Client/game_sa/CVehicleSA.cpp @@ -802,12 +802,6 @@ void CVehicleSA::RemoveVehicleUpgrade(DWORD dwModelID) if (parentID >= 1000 && parentID <= 1193) { dwActualModelID = parentID; - - CModelInfo* pParentModelInfo = pGame->GetModelInfo(parentID); - if (pParentModelInfo) - { - pParentModelInfo->Request(BLOCKING, "CVehicleSA::RemoveVehicleUpgrade restore"); - } } } @@ -825,7 +819,7 @@ void CVehicleSA::RemoveVehicleUpgrade(DWORD dwModelID) // In the case of hydraulics and nitro, this function does not return false and the upgrade is never removed from the array for (std::int16_t& upgrade : GetVehicleInterface()->m_upgrades) { - if (upgrade == dwModelID) + if (upgrade == dwModelID || upgrade == dwActualModelID) { upgrade = -1; break; From 967d82da6842e92db2730f9a542d2489d2588eaf Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:34:42 +0100 Subject: [PATCH 04/20] crashfix --- Client/mods/deathmatch/logic/CClientModel.cpp | 43 +++++++++++-------- .../deathmatch/logic/CVehicleUpgrades.cpp | 30 +------------ .../mods/deathmatch/logic/CVehicleUpgrades.h | 3 +- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 94e27c3ae15..b31cf27a37b 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -106,23 +106,6 @@ bool CClientModel::Deallocate() SetParentResource(nullptr); - // If this is a custom vehicle upgrade model, clean it up from all vehicles - if (m_eModelType == eClientModelType::VEHICLE_UPGRADE) - { - CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); - if (pVehicleManager) - { - for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) - { - CClientVehicle* pVehicle = *iter; - if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) - { - pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID, true); - } - } - } - } - CModelInfo* pModelInfo = g_pGame->GetModelInfo(m_iModelID, true); if (!pModelInfo || !pModelInfo->IsValid()) return false; @@ -150,6 +133,7 @@ void CClientModel::RestoreEntitiesUsingThisModel() case eClientModelType::CLUMP: case eClientModelType::TIMED_OBJECT: case eClientModelType::VEHICLE: + case eClientModelType::VEHICLE_UPGRADE: RestoreDFF(pModelInfo); return; case eClientModelType::TXD: @@ -244,6 +228,31 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) [=](auto& element) { element.SetModelBlocking(usParentID, 255, 255); }); break; } + case eClientModelType::VEHICLE_UPGRADE: + { + // Remove the custom upgrade from all vehicles using it + CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); + const auto usParentID = static_cast(g_pGame->GetModelInfo(m_iModelID)->GetParentID()); + + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) + { + CClientVehicle* pVehicle = *iter; + if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) + { + // Remove the custom upgrade + pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID); + + // Re-add the parent upgrade if it was there + if (usParentID >= 1000 && usParentID <= 1193) + { + pVehicle->GetUpgrades()->AddUpgrade(usParentID, false); + } + + callElementChangeEvent(*pVehicle, usParentID, m_iModelID); + } + } + break; + } } // Restore DFF/TXD diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 51f9bcaf4fd..1eada158b7d 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -694,20 +694,6 @@ void CVehicleUpgrades::RestreamVehicleUpgrades(unsigned short usUpgrade) } } -void CVehicleUpgrades::RestoreOriginalRwObject(unsigned short usParentID) -{ - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); - if (pParentModelInfo) - { - // Set to NULL to clear any swapped pointer - pParentModelInfo->SetRwObject(NULL); - - // Force reload the parent model to get fresh RwObject - pParentModelInfo->RemoveRef(); - pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::RestoreOriginalRwObject"); - } -} - bool CVehicleUpgrades::HasUpgrade(unsigned short usUpgrade) { unsigned char ucSlot = 0; @@ -720,7 +706,7 @@ bool CVehicleUpgrades::HasUpgrade(unsigned short usUpgrade) return false; } -bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwObject) +bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade) { if (HasUpgrade(usUpgrade)) { @@ -729,20 +715,6 @@ bool CVehicleUpgrades::RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwOb CVehicle* pVehicle = m_pVehicle->GetGameVehicle(); if (pVehicle) { - // Restore parent model if custom upgrade - if (bRestoreRwObject) - { - auto* upgradeModelInfo = g_pGame->GetModelInfo(usUpgrade); - if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) - { - unsigned int parentID = upgradeModelInfo->GetParentID(); - if (IsUpgrade(static_cast(parentID))) - { - RestoreOriginalRwObject(static_cast(parentID)); - } - } - } - pVehicle->RemoveVehicleUpgrade(usUpgrade); } diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index 09d5d4d061a..fa24a385066 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -40,7 +40,7 @@ class CVehicleUpgrades void AddAllUpgrades(); void ForceAddUpgrade(unsigned short usUpgrade); bool HasUpgrade(unsigned short usUpgrade); - bool RemoveUpgrade(unsigned short usUpgrade, bool bRestoreRwObject = true); + bool RemoveUpgrade(unsigned short usUpgrade); unsigned short GetSlotState(unsigned char ucSlot); const SSlotStates& GetSlotStates() { return m_SlotStates; } static const char* GetSlotName(unsigned char ucSlot); @@ -49,7 +49,6 @@ class CVehicleUpgrades void RemoveAll(bool bRipFromVehicle); void RestreamVehicleUpgrades(unsigned short usModel); - void RestoreOriginalRwObject(unsigned short usParentID); protected: SSlotStates m_SlotStates; From 55e0cd285879c202c76ec640315fdb8261273f14 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:35:32 +0100 Subject: [PATCH 05/20] fix --- Client/mods/deathmatch/logic/CVehicleUpgrades.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index fa24a385066..85aa20610d1 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -10,12 +10,9 @@ #pragma once -#include - #define VEHICLE_UPGRADE_SLOTS 17 class CClientVehicle; -struct RwObject; enum eVehicleUpgrade { From e4de653bf61bd67741b30e48502051764d83148d Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:36:45 +0100 Subject: [PATCH 06/20] fix --- Client/mods/deathmatch/logic/CClientGame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 674537ccb03..0d057bf424f 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -6906,7 +6906,7 @@ void CClientGame::Restream(std::optional option) if (option == RestreamOption::ALL || option == RestreamOption::OBJECTS) { static constexpr eClientModelType restreamTypes[] = {eClientModelType::OBJECT, eClientModelType::OBJECT_DAMAGEABLE, eClientModelType::TIMED_OBJECT, - eClientModelType::CLUMP, eClientModelType::VEHICLE_UPGRADE}; + eClientModelType::CLUMP}; for (eClientModelType type : restreamTypes) { From 488b7c073236c50630bbdf42b36cbcce4be6f829 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:54:42 +0100 Subject: [PATCH 07/20] fix --- Client/mods/deathmatch/logic/CVehicleUpgrades.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 1eada158b7d..36c08a66117 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -671,6 +671,13 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) // Add the upgrade (uses parent ID internally) pVehicle->AddVehicleUpgrade(usUpgrade); + + // If custom upgrade was added, force restream to apply the swapped RwObject + if (pModelInfo && pModelInfo->GetParentID() != 0) + { + pVehicle->RemoveVehicleUpgrade(usUpgrade); + pVehicle->AddVehicleUpgrade(usUpgrade); + } } // Add it to the slot From 9375397866a57385492418e6b6f663b494496571 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 14:59:59 +0100 Subject: [PATCH 08/20] fix --- Client/mods/deathmatch/logic/CVehicleUpgrades.cpp | 10 +++++++++- Client/mods/deathmatch/logic/CVehicleUpgrades.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 36c08a66117..885f0fdfa0f 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -10,6 +10,8 @@ #include +std::map s_OriginalParentRwObjects; + char szUpgradeNameEmpty[] = ""; struct SUpgradeName @@ -661,7 +663,13 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) if (pCustomRwObject && pParentRwObject) { - // Just swap - we'll set to NULL on restore + // Store original parent RwObject if not already stored + if (s_OriginalParentRwObjects.find(static_cast(parentID)) == s_OriginalParentRwObjects.end()) + { + s_OriginalParentRwObjects[static_cast(parentID)] = pParentRwObject; + } + + // Swap to custom RwObject pParentModelInfo->SetRwObject(pCustomRwObject); } } diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index 85aa20610d1..05367892c5d 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -12,6 +12,9 @@ #define VEHICLE_UPGRADE_SLOTS 17 +struct RwObject; +extern std::map s_OriginalParentRwObjects; + class CClientVehicle; enum eVehicleUpgrade From 3aa5ccc0cf48fe0d33a9b1fe6a227c612aa690a5 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 15:06:00 +0100 Subject: [PATCH 09/20] fix --- Client/mods/deathmatch/logic/CClientModel.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index b31cf27a37b..c2cf3977aa2 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -234,6 +234,34 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); const auto usParentID = static_cast(g_pGame->GetModelInfo(m_iModelID)->GetParentID()); + // Restore original parent RwObject before freeing custom model + if (usParentID >= 1000 && usParentID <= 1193) + { + auto it = s_OriginalParentRwObjects.find(usParentID); + if (it != s_OriginalParentRwObjects.end()) + { + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); + if (pParentModelInfo) + { + pParentModelInfo->SetRwObject(it->second); + s_OriginalParentRwObjects.erase(it); + } + } + + // Force restream all vehicles using this parent upgrade + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) + { + CClientVehicle* pVehicle = *iter; + if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(usParentID)) + { + if (pVehicle->IsStreamedIn()) + { + pVehicle->StreamOutForABit(); + } + } + } + } + for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) { CClientVehicle* pVehicle = *iter; From 203252e2331acb8fa9f27a41c4d848b95c5de27a Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 15:12:47 +0100 Subject: [PATCH 10/20] fix streaming issue --- Client/mods/deathmatch/logic/CClientModel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index c2cf3977aa2..336e84d362c 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -254,6 +254,14 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) CClientVehicle* pVehicle = *iter; if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(usParentID)) { + CVehicle* pGameVehicle = pVehicle->GetGameVehicle(); + if (pGameVehicle) + { + // Force reload upgrade with restored RwObject + pGameVehicle->RemoveVehicleUpgrade(usParentID); + pGameVehicle->AddVehicleUpgrade(usParentID); + } + if (pVehicle->IsStreamedIn()) { pVehicle->StreamOutForABit(); From 521b4a4a93b16316ff43b2ce01461530292d18e4 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 15:18:54 +0100 Subject: [PATCH 11/20] use unloadModelsAndCallEvents --- Client/mods/deathmatch/logic/CClientModel.cpp | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 336e84d362c..661188d769a 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -230,7 +230,6 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) } case eClientModelType::VEHICLE_UPGRADE: { - // Remove the custom upgrade from all vehicles using it CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); const auto usParentID = static_cast(g_pGame->GetModelInfo(m_iModelID)->GetParentID()); @@ -248,7 +247,7 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) } } - // Force restream all vehicles using this parent upgrade + // Restream all vehicles using this parent upgrade for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) { CClientVehicle* pVehicle = *iter; @@ -257,7 +256,6 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) CVehicle* pGameVehicle = pVehicle->GetGameVehicle(); if (pGameVehicle) { - // Force reload upgrade with restored RwObject pGameVehicle->RemoveVehicleUpgrade(usParentID); pGameVehicle->AddVehicleUpgrade(usParentID); } @@ -270,23 +268,15 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) } } - for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) - { - CClientVehicle* pVehicle = *iter; - if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(m_iModelID)) - { - // Remove the custom upgrade - pVehicle->GetUpgrades()->RemoveUpgrade(m_iModelID); - - // Re-add the parent upgrade if it was there + // Remove custom upgrade and restore parent + unloadModelsAndCallEvents(pVehicleManager->IterBegin(), pVehicleManager->IterEnd(), usParentID, + [=](auto& element) { + element.GetUpgrades()->RemoveUpgrade(m_iModelID); if (usParentID >= 1000 && usParentID <= 1193) { - pVehicle->GetUpgrades()->AddUpgrade(usParentID, false); + element.GetUpgrades()->AddUpgrade(usParentID, false); } - - callElementChangeEvent(*pVehicle, usParentID, m_iModelID); - } - } + }); break; } } From 4c3a72ca8460357c91a76a2bad2cf438a3c3eb09 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 15:31:20 +0100 Subject: [PATCH 12/20] make it more aggressive --- Client/mods/deathmatch/logic/CClientModel.cpp | 35 ------------------- .../deathmatch/logic/CVehicleUpgrades.cpp | 18 +++++----- .../mods/deathmatch/logic/CVehicleUpgrades.h | 3 -- 3 files changed, 9 insertions(+), 47 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 661188d769a..a3dce821323 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -233,41 +233,6 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager(); const auto usParentID = static_cast(g_pGame->GetModelInfo(m_iModelID)->GetParentID()); - // Restore original parent RwObject before freeing custom model - if (usParentID >= 1000 && usParentID <= 1193) - { - auto it = s_OriginalParentRwObjects.find(usParentID); - if (it != s_OriginalParentRwObjects.end()) - { - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(usParentID); - if (pParentModelInfo) - { - pParentModelInfo->SetRwObject(it->second); - s_OriginalParentRwObjects.erase(it); - } - } - - // Restream all vehicles using this parent upgrade - for (auto iter = pVehicleManager->IterBegin(); iter != pVehicleManager->IterEnd(); ++iter) - { - CClientVehicle* pVehicle = *iter; - if (pVehicle && pVehicle->GetUpgrades() && pVehicle->GetUpgrades()->HasUpgrade(usParentID)) - { - CVehicle* pGameVehicle = pVehicle->GetGameVehicle(); - if (pGameVehicle) - { - pGameVehicle->RemoveVehicleUpgrade(usParentID); - pGameVehicle->AddVehicleUpgrade(usParentID); - } - - if (pVehicle->IsStreamedIn()) - { - pVehicle->StreamOutForABit(); - } - } - } - } - // Remove custom upgrade and restore parent unloadModelsAndCallEvents(pVehicleManager->IterBegin(), pVehicleManager->IterEnd(), usParentID, [=](auto& element) { diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 885f0fdfa0f..bd08e2713cd 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -10,8 +10,6 @@ #include -std::map s_OriginalParentRwObjects; - char szUpgradeNameEmpty[] = ""; struct SUpgradeName @@ -663,14 +661,16 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) if (pCustomRwObject && pParentRwObject) { - // Store original parent RwObject if not already stored - if (s_OriginalParentRwObjects.find(static_cast(parentID)) == s_OriginalParentRwObjects.end()) - { - s_OriginalParentRwObjects[static_cast(parentID)] = pParentRwObject; - } - - // Swap to custom RwObject + // Temporarily swap to custom RwObject ONLY during AddVehicleUpgrade call pParentModelInfo->SetRwObject(pCustomRwObject); + pVehicle->AddVehicleUpgrade(usUpgrade); + pParentModelInfo->SetRwObject(pParentRwObject); + + // Early return since we already added the upgrade + m_SlotStates[ucSlot] = usUpgrade; + if (ucSlot == 12) + m_pVehicle->ResetWheelScale(); + return; } } } diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.h b/Client/mods/deathmatch/logic/CVehicleUpgrades.h index 05367892c5d..85aa20610d1 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.h +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.h @@ -12,9 +12,6 @@ #define VEHICLE_UPGRADE_SLOTS 17 -struct RwObject; -extern std::map s_OriginalParentRwObjects; - class CClientVehicle; enum eVehicleUpgrade From b336a10064eb761a14daf683f1a5a36e077d835e Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 14 Dec 2025 15:33:31 +0100 Subject: [PATCH 13/20] fix --- Client/mods/deathmatch/logic/CVehicleUpgrades.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index bd08e2713cd..b6f9f6a9856 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -670,22 +670,15 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) m_SlotStates[ucSlot] = usUpgrade; if (ucSlot == 12) m_pVehicle->ResetWheelScale(); + return; } } } } } - - // Add the upgrade (uses parent ID internally) - pVehicle->AddVehicleUpgrade(usUpgrade); - // If custom upgrade was added, force restream to apply the swapped RwObject - if (pModelInfo && pModelInfo->GetParentID() != 0) - { - pVehicle->RemoveVehicleUpgrade(usUpgrade); - pVehicle->AddVehicleUpgrade(usUpgrade); - } + pVehicle->AddVehicleUpgrade(usUpgrade); } // Add it to the slot From 45e6f731d1ef67729f2c496e8225075424d0c34d Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Tue, 6 Jan 2026 19:34:53 +0100 Subject: [PATCH 14/20] fixes --- Client/mods/deathmatch/logic/CVehicleUpgrades.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index b6f9f6a9856..7b68efd7d2a 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -55,7 +55,7 @@ bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) auto* upgradeModelInfo = g_pGame->GetModelInfo(us); if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) { - unsigned int parentID = upgradeModelInfo->GetParentID(); + unsigned short parentID = static_cast(upgradeModelInfo->GetParentID()); if (IsUpgrade(static_cast(parentID))) { us = static_cast(parentID); @@ -466,7 +466,7 @@ bool CVehicleUpgrades::GetSlotFromUpgrade(unsigned short us, unsigned char& ucSl auto* upgradeModelInfo = g_pGame->GetModelInfo(us); if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) { - unsigned int parentID = upgradeModelInfo->GetParentID(); + unsigned short parentID = static_cast(upgradeModelInfo->GetParentID()); if (IsUpgrade(static_cast(parentID))) { us = static_cast(parentID); @@ -642,7 +642,7 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) } // If this is a custom model with parent ID, swap RwObjects - unsigned int parentID = pModelInfo->GetParentID(); + unsigned short parentID = static_cast(pModelInfo->GetParentID()); if (parentID != 0 && IsUpgrade(static_cast(parentID))) { CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast(parentID)); From ab8b25058369cb7145e98b9022032898134d858c Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Tue, 6 Jan 2026 19:45:18 +0100 Subject: [PATCH 15/20] typo --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 9aa893c504c..a98f1a7c0ab 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -968,7 +968,7 @@ int CLuaEngineDefs::EngineRequestModel(lua_State* luaVM) iParentID = static_cast(VehicleType::VT_LANDSTAL); break; case eClientModelType::VEHICLE_UPGRADE: - usParentID = 1025; + iParentID = 1025; break; default: break; From ac25a5a6a9daa401df4f5983319746c4a6f3d61b Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Tue, 6 Jan 2026 19:46:48 +0100 Subject: [PATCH 16/20] typo --- Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index a98f1a7c0ab..951e60f6198 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -946,6 +946,7 @@ int CLuaEngineDefs::EngineRequestModel(lua_State* luaVM) constexpr int defaultClumpParentId = 3425; constexpr int defaultObjectParentId = 1337; constexpr int defaultDamageableObjectParentId = 994; + constexpr int defaultVehicleUpgradeId = 1025; switch (eModelType) { @@ -968,7 +969,7 @@ int CLuaEngineDefs::EngineRequestModel(lua_State* luaVM) iParentID = static_cast(VehicleType::VT_LANDSTAL); break; case eClientModelType::VEHICLE_UPGRADE: - iParentID = 1025; + iParentID = defaultVehicleUpgradeId; break; default: break; From ce69d4f694d12635b29cb987428750c1a98fb97f Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Wed, 7 Jan 2026 07:53:56 +0100 Subject: [PATCH 17/20] fixes --- .../mods/deathmatch/logic/CVehicleUpgrades.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 7b68efd7d2a..744fb018061 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -55,10 +55,10 @@ bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) auto* upgradeModelInfo = g_pGame->GetModelInfo(us); if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) { - unsigned short parentID = static_cast(upgradeModelInfo->GetParentID()); - if (IsUpgrade(static_cast(parentID))) + unsigned short parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(parentID)) { - us = static_cast(parentID); + us = parentID; } } @@ -466,10 +466,10 @@ bool CVehicleUpgrades::GetSlotFromUpgrade(unsigned short us, unsigned char& ucSl auto* upgradeModelInfo = g_pGame->GetModelInfo(us); if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) { - unsigned short parentID = static_cast(upgradeModelInfo->GetParentID()); - if (IsUpgrade(static_cast(parentID))) + unsigned short parentID = upgradeModelInfo->GetParentID(); + if (IsUpgrade(parentID)) { - us = static_cast(parentID); + us = parentID; } } @@ -643,9 +643,9 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) // If this is a custom model with parent ID, swap RwObjects unsigned short parentID = static_cast(pModelInfo->GetParentID()); - if (parentID != 0 && IsUpgrade(static_cast(parentID))) + if (parentID != 0 && IsUpgrade(parentID)) { - CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast(parentID)); + CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(parentID); if (pParentModelInfo) { if (!g_pGame->IsASyncLoadingEnabled() || !pParentModelInfo->IsLoaded()) From ce673f5dbbb24344e668ba92e01a8c09a70439f5 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 18 Jan 2026 12:27:15 +0100 Subject: [PATCH 18/20] Resolve conflicts & use MakeObjectModel --- Client/game_sa/CModelInfoSA.h | 6 ++++++ Client/mods/deathmatch/logic/CClientModel.cpp | 2 +- Client/sdk/game/CModelInfo.h | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 063bbb802da..6f712101d69 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -482,6 +482,12 @@ class CModelInfoSA : public CModelInfo return pInterface ? pInterface->pRwObject : NULL; } + void SetRwObject(RwObject* pRwObject) + { + if (m_pInterface) + m_pInterface->pRwObject = pRwObject; + } + // CModelInfoSA methods void MakePedModel(const char* szTexture); void MakeObjectModel(ushort usBaseModelID); diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 2f87d9a42b8..cd45f5a5915 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -91,7 +91,7 @@ bool CClientModel::Allocate(ushort usParentID) { if (CVehicleUpgrades::IsUpgrade(usParentID)) { - pModelInfo->MakeVehicleUpgradeModel(usParentID); + pModelInfo->MakeObjectModel(usParentID); return true; } break; diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index e761236d4ad..cc1b70a2808 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -245,7 +245,6 @@ class CModelInfo virtual void MakeObjectModel(unsigned short usBaseID) = 0; virtual void MakeObjectDamageableModel(std::uint16_t baseID) = 0; virtual void MakeVehicleAutomobile(unsigned short usBaseID) = 0; - virtual void MakeVehicleUpgradeModel(unsigned short usBaseID) = 0; virtual void MakeTimedObjectModel(unsigned short usBaseID) = 0; virtual void MakeClumpModel(unsigned short usBaseID) = 0; From e61482d09412736755dd52fecdaf6f6254db27a9 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Sun, 18 Jan 2026 12:48:53 +0100 Subject: [PATCH 19/20] Refactor vehicle upgrade logic to use GetCustomUpgradeParentModelID for consistency --- Client/game_sa/CVehicleSA.cpp | 26 +++++-------- .../deathmatch/logic/CVehicleUpgrades.cpp | 38 +++++++++---------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/Client/game_sa/CVehicleSA.cpp b/Client/game_sa/CVehicleSA.cpp index 7bfa09b9050..4aa916f79a3 100644 --- a/Client/game_sa/CVehicleSA.cpp +++ b/Client/game_sa/CVehicleSA.cpp @@ -779,20 +779,24 @@ void CVehicleSA::LockDoors(bool bLocked) } } -void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) +static DWORD GetCustomUpgradeParentModelID(DWORD dwModelID) { - DWORD dwActualModelID = dwModelID; - CModelInfo* pModelInfo = pGame->GetModelInfo(dwModelID); if (pModelInfo && pModelInfo->GetParentID() != 0) { unsigned int parentID = pModelInfo->GetParentID(); if (parentID >= 1000 && parentID <= 1193) { - dwActualModelID = parentID; + return parentID; } } - + return dwModelID; +} + +void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) +{ + DWORD dwActualModelID = GetCustomUpgradeParentModelID(dwModelID); + if (dwActualModelID >= 1000 && dwActualModelID <= 1193) { DWORD dwThis = (DWORD)m_pInterface; @@ -811,18 +815,8 @@ void CVehicleSA::AddVehicleUpgrade(DWORD dwModelID) void CVehicleSA::RemoveVehicleUpgrade(DWORD dwModelID) { - DWORD dwActualModelID = dwModelID; + DWORD dwActualModelID = GetCustomUpgradeParentModelID(dwModelID); - CModelInfo* pModelInfo = pGame->GetModelInfo(dwModelID); - if (pModelInfo && pModelInfo->GetParentID() != 0) - { - unsigned int parentID = pModelInfo->GetParentID(); - if (parentID >= 1000 && parentID <= 1193) - { - dwActualModelID = parentID; - } - } - DWORD dwThis = (DWORD)m_pInterface; DWORD dwFunc = FUNC_CVehicle_RemoveVehicleUpgrade; diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index 744fb018061..c2cd7a8b448 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -42,26 +42,32 @@ CVehicleUpgrades::CVehicleUpgrades(CClientVehicle* pVehicle) m_usLastLocalAddNitroType = 0; } +static unsigned short GetCustomUpgradeParentModelID(unsigned short usModelID) +{ + auto* upgradeModelInfo = g_pGame->GetModelInfo(usModelID); + if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) + { + unsigned short parentID = upgradeModelInfo->GetParentID(); + if (parentID >= 1000 && parentID <= 1193) + { + return parentID; + } + } + return usModelID; +} + bool CVehicleUpgrades::IsUpgrade(unsigned short usModel) { + usModel = GetCustomUpgradeParentModelID(usModel); + return (usModel >= 1000 && usModel <= 1193); } bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) { - unsigned short us = usUpgrade; + unsigned short us = GetCustomUpgradeParentModelID(usUpgrade); eClientVehicleType vehicleType = m_pVehicle->GetVehicleType(); - auto* upgradeModelInfo = g_pGame->GetModelInfo(us); - if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) - { - unsigned short parentID = upgradeModelInfo->GetParentID(); - if (IsUpgrade(parentID)) - { - us = parentID; - } - } - // No upgrades for trains/boats if (vehicleType == CLIENTVEHICLE_TRAIN || vehicleType == CLIENTVEHICLE_BOAT) return false; @@ -463,15 +469,7 @@ bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) bool CVehicleUpgrades::GetSlotFromUpgrade(unsigned short us, unsigned char& ucSlot) { // Check if this is a custom upgrade model - auto* upgradeModelInfo = g_pGame->GetModelInfo(us); - if (upgradeModelInfo && upgradeModelInfo->GetParentID() != 0) - { - unsigned short parentID = upgradeModelInfo->GetParentID(); - if (IsUpgrade(parentID)) - { - us = parentID; - } - } + us = GetCustomUpgradeParentModelID(us); if (us == 1011 || us == 1012 || us == 1111 || us == 1112 || us == 1142 || /* bonet */ us == 1143 || us == 1144 || us == 1145) From 437200bc6f92314c3cd24e952a97ab9a9c773d7b Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Wed, 21 Jan 2026 08:22:35 +0100 Subject: [PATCH 20/20] fix formatting --- Client/mods/deathmatch/logic/CClientModel.cpp | 17 +++++++++-------- .../mods/deathmatch/logic/CVehicleUpgrades.cpp | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientModel.cpp b/Client/mods/deathmatch/logic/CClientModel.cpp index 6caf61cf89f..251ecba080b 100644 --- a/Client/mods/deathmatch/logic/CClientModel.cpp +++ b/Client/mods/deathmatch/logic/CClientModel.cpp @@ -273,14 +273,15 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo) const auto usParentID = static_cast(g_pGame->GetModelInfo(m_iModelID)->GetParentID()); // Remove custom upgrade and restore parent - unloadModelsAndCallEvents(pVehicleManager->IterBegin(), pVehicleManager->IterEnd(), usParentID, - [=](auto& element) { - element.GetUpgrades()->RemoveUpgrade(m_iModelID); - if (usParentID >= 1000 && usParentID <= 1193) - { - element.GetUpgrades()->AddUpgrade(usParentID, false); - } - }); + unloadModelsAndCallEvents(pVehicleManager->IterBegin(), pVehicleManager->IterEnd(), usParentID, + [=](auto& element) + { + element.GetUpgrades()->RemoveUpgrade(m_iModelID); + if (usParentID >= 1000 && usParentID <= 1193) + { + element.GetUpgrades()->AddUpgrade(usParentID, false); + } + }); break; } } diff --git a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp index cc3654310a4..eee7be49856 100644 --- a/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp +++ b/Client/mods/deathmatch/logic/CVehicleUpgrades.cpp @@ -65,7 +65,7 @@ bool CVehicleUpgrades::IsUpgrade(unsigned short usModel) bool CVehicleUpgrades::IsUpgradeCompatible(unsigned short usUpgrade) { - unsigned short us = GetCustomUpgradeParentModelID(usUpgrade); + unsigned short us = GetCustomUpgradeParentModelID(usUpgrade); eClientVehicleType vehicleType = m_pVehicle->GetVehicleType(); // No upgrades for trains/boats @@ -637,7 +637,7 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) { pModelInfo->Request(BLOCKING, "CVehicleUpgrades::ForceAddUpgrade"); } - + // If this is a custom model with parent ID, swap RwObjects unsigned short parentID = static_cast(pModelInfo->GetParentID()); if (parentID != 0 && IsUpgrade(parentID)) @@ -649,20 +649,20 @@ void CVehicleUpgrades::ForceAddUpgrade(unsigned short usUpgrade) { pParentModelInfo->Request(BLOCKING, "CVehicleUpgrades::ForceAddUpgrade (parent)"); } - + // Wait for both to be loaded if (pModelInfo->IsLoaded() && pParentModelInfo->IsLoaded()) { RwObject* pCustomRwObject = pModelInfo->GetRwObject(); RwObject* pParentRwObject = pParentModelInfo->GetRwObject(); - + if (pCustomRwObject && pParentRwObject) { // Temporarily swap to custom RwObject ONLY during AddVehicleUpgrade call pParentModelInfo->SetRwObject(pCustomRwObject); pVehicle->AddVehicleUpgrade(usUpgrade); pParentModelInfo->SetRwObject(pParentRwObject); - + // Early return since we already added the upgrade m_SlotStates[ucSlot] = usUpgrade; if (ucSlot == 12)