Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Client/game_sa/CModelInfoSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2087,6 +2087,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)
{
// Model IDs can be reused (engineRequestModel); do not let a previous model's stored default TXD
Expand Down
2 changes: 2 additions & 0 deletions Client/game_sa/CModelInfoSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
32 changes: 28 additions & 4 deletions Client/game_sa/CVehicleSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,37 +765,61 @@ 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;

DWORD dwFunc = FUNC_CVehicle_AddVehicleUpgrade;
__asm
{
mov ecx, dwThis
push dwModelID
push dwActualModelID
call dwFunc
}
}
}

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;
}
}

DWORD dwThis = (DWORD)m_pInterface;
DWORD dwFunc = FUNC_CVehicle_RemoveVehicleUpgrade;

__asm
{
mov ecx, dwThis
push dwModelID
push dwActualModelID
call dwFunc
}

// GTA SA only does this when CVehicle::ClearVehicleUpgradeFlags returns false.
// 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;
Expand Down
26 changes: 26 additions & 0 deletions Client/mods/deathmatch/logic/CClientModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ bool CClientModel::Allocate(ushort usParentID)
allocated = true;
}
break;
case eClientModelType::VEHICLE_UPGRADE:
{
if (CVehicleUpgrades::IsUpgrade(usParentID))
{
pModelInfo->MakeVehicleUpgradeModel(usParentID);
return true;
}
break;
}
default:
return false;
}
Expand Down Expand Up @@ -140,6 +149,7 @@ void CClientModel::RestoreEntitiesUsingThisModel()
case eClientModelType::CLUMP:
case eClientModelType::TIMED_OBJECT:
case eClientModelType::VEHICLE:
case eClientModelType::VEHICLE_UPGRADE:
RestoreDFF(pModelInfo);
return;
case eClientModelType::TXD:
Expand Down Expand Up @@ -251,6 +261,22 @@ void CClientModel::RestoreDFF(CModelInfo* pModelInfo)
[usParentID](auto& element) { element.SetModelBlocking(usParentID, 255, 255); });
break;
}
case eClientModelType::VEHICLE_UPGRADE:
{
CClientVehicleManager* pVehicleManager = g_pClientGame->GetManager()->GetVehicleManager();
const auto usParentID = static_cast<unsigned short>(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);
}
});
break;
}
}

g_pClientGame->GetManager()->GetDFFManager()->RestoreModel(modelId);
Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum class eClientModelType
OBJECT,
OBJECT_DAMAGEABLE,
VEHICLE,
VEHICLE_UPGRADE,
TIMED_OBJECT,
CLUMP,
TXD,
Expand Down
64 changes: 60 additions & 4 deletions Client/mods/deathmatch/logic/CVehicleUpgrades.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 short parentID = static_cast<unsigned short>(upgradeModelInfo->GetParentID());
if (IsUpgrade(static_cast<unsigned short>(parentID)))
{
us = static_cast<unsigned short>(parentID);
}
}

// No upgrades for trains/boats
if (vehicleType == CLIENTVEHICLE_TRAIN || vehicleType == CLIENTVEHICLE_BOAT)
return false;
Expand Down Expand Up @@ -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 short parentID = static_cast<unsigned short>(upgradeModelInfo->GetParentID());
if (IsUpgrade(static_cast<unsigned short>(parentID)))
{
us = static_cast<unsigned short>(parentID);
}
}

if (us == 1011 || us == 1012 || us == 1111 || us == 1112 || us == 1142 || /* bonet */
us == 1143 || us == 1144 || us == 1145)
{
Expand Down Expand Up @@ -611,18 +632,53 @@ 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 short parentID = static_cast<unsigned short>(pModelInfo->GetParentID());
if (parentID != 0 && IsUpgrade(static_cast<unsigned short>(parentID)))
{
CModelInfo* pParentModelInfo = g_pGame->GetModelInfo(static_cast<unsigned short>(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)
{
// 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;
}
}
}
}
}

pVehicle->AddVehicleUpgrade(usUpgrade);
}

// Add it to the slot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -967,6 +968,9 @@ int CLuaEngineDefs::EngineRequestModel(lua_State* luaVM)
case eClientModelType::VEHICLE:
iParentID = static_cast<int>(VehicleType::VT_LANDSTAL);
break;
case eClientModelType::VEHICLE_UPGRADE:
iParentID = defaultVehicleUpgradeId;
break;
default:
break;
}
Expand Down
2 changes: 2 additions & 0 deletions Client/sdk/game/CModelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down