Files
GTASource/game/modelinfo/VehicleModelInfoVariation.cpp

3634 lines
116 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
//
// VehicleModelInfoVariation.h
// Data file for vehicle customization
//
// Rockstar Games (c) 2011
#include "modelinfo/VehicleModelInfoVariation.h"
#include "modelinfo/VehicleModelInfoVariation_parser.h"
#include "bank/bank.h"
#include "bank/bkmgr.h"
#include "bank/combo.h"
#include "fragment/type.h"
#include "fwscene/stores/drawablestore.h"
#include "fwscene/stores/fragmentstore.h"
#include "control/Gen9ExclusiveAssets.h"
#include "modelinfo/VehicleModelInfoColors.h"
#include "modelinfo/VehicleModelInfoPlates.h"
#include "modelinfo/VehicleModelInfo.h"
#include "parser/manager.h"
#include "scene/Entity.h"
#include "scene/EntityIterator.h"
#include "shaders/CustomShaderEffectVehicle.h"
#include "streaming/populationstreaming.h"
#include "streaming/ScriptPreload.h"
#include "streaming/streaming.h"
#include "vehicles/vehicle.h"
#include "vehicles/vehicleDamage.h"
#include "vehicles/Automobile.h"
#include "vehicles/Bike.h"
#include "vehicles/Planes.h"
#include "Vehicles/VehicleFactory.h"
#include "Vehicles/VehicleGadgets.h"
PARAM(disableVehMods, "[vehicles] Disable vehicle mods (temp disabled as patch for BS#1017717, BS#1022990, etc.)");
VEHICLE_OPTIMISATIONS()
const char* exhaustBoneNames[NUM_EXHAUST_BONES] = {"exhaust","exhaust_2","exhaust_3","exhaust_4","exhaust_5","exhaust_6","exhaust_7","exhaust_8","exhaust_9","exhaust_10","exhaust_11","exhaust_12","exhaust_13","exhaust_14","exhaust_15","exhaust_16","exhaust_17","exhaust_18","exhaust_19","exhaust_20","exhaust_21","exhaust_22","exhaust_23","exhaust_24","exhaust_25","exhaust_26","exhaust_27","exhaust_28","exhaust_29","exhaust_30","exhaust_31","exhaust_32" };
FW_INSTANTIATE_CLASS_POOL(CVehicleStreamRequestGfx, CONFIGURED_FROM_FILE, atHashString("CVehicleStreamRequestGfx",0x54753c86));
FW_INSTANTIATE_CLASS_POOL(CVehicleStreamRenderGfx, CONFIGURED_FROM_FILE, atHashString("CVehicleStreamRenderGfx",0xc2b5f089));
AUTOID_IMPL(CVehicleStreamRequestGfx);
AUTOID_IMPL(CVehicleStreamRenderGfx);
atArray<CVehicleStreamRenderGfx*> CVehicleStreamRenderGfx::ms_renderRefs[FENCE_COUNT];
s32 CVehicleStreamRenderGfx::ms_fenceUpdateIdx = 0;
int CVehicleVariationInstance::ms_MaxVehicleStreamRequest = 0;
int CVehicleVariationInstance::ms_MaxVehicleStreamRender = 0;
CVehicleVariationInstance::CVehicleVariationInstance() :
m_color1(0),
m_color2(0),
m_color3(0),
m_color4(0),
m_colorType1(VCT_NONE),
m_colorType2(VCT_NONE),
m_baseColorIndex(-1),
m_specColorIndex(-1),
m_baseColorIndex2(-1),
m_smokeColR(255),
m_smokeColG(255),
m_smokeColB(255),
m_windowTint(0xff),
m_numMods(0),
m_kitIdx(INVALID_VEHICLE_KIT_INDEX),
m_renderGfx(NULL),
m_modMatricesStoredThisFrame(0),
m_canHaveRearWheels(false),
m_neonLOn(false),
m_neonROn(false),
m_neonFOn(false),
m_neonBOn(false),
m_xenonLightColor(0xff),
m_preloadData(NULL),
m_wheelType(VWT_INVALID),
m_modVariations(0)
{
for (s32 i = 0; i < VMT_MAX; ++i)
{
m_mods[i] = INVALID_MOD;
}
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
m_linkedMods[i] = INVALID_MOD;
}
ClearBonePositions();
ShowAllMods();
}
CVehicleVariationInstance::~CVehicleVariationInstance()
{
if (m_renderGfx)
{
gPopStreaming.RemoveStreamedVehicleVariation(m_renderGfx);
m_renderGfx->SetUsed(false);
}
CleanUpPreloadMods(NULL);
}
void CVehicleVariationInstance::CleanUpPreloadMods(CVehicle* pVeh)
{
// try to apply the requested mods on the vehicle, usually these are the preloaded mods and there's a slight
// chance they'll get freed here a frame before they get used again
if (pVeh && m_preloadData)
{
CVehicleStreamRequestGfx* req = pVeh->GetExtensionList().GetExtension<CVehicleStreamRequestGfx>();
if (req && req->HaveAllLoaded())
{
Assert(req->GetTargetEntity() == pVeh);
req->RequestsComplete();
pVeh->GetExtensionList().Destroy(req);
}
}
delete m_preloadData;
m_preloadData = NULL;
}
void CVehicleVariationInstance::ClearBonePositions()
{
for (s32 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
m_bonePositions[i] = VEC3_ZERO;
}
}
void CVehicleVariationInstance::UpdateBonePositions(CVehicle* pVehicle)
{
for (u8 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
s32 boneIdx = -1;
if (i < VMT_RENDERABLE)
boneIdx = GetBone(i);
else
boneIdx = GetBoneForLinkedMod(i - VMT_RENDERABLE);
if (boneIdx < 0)
continue;
s32 modBone = pVehicle->GetBoneIndex((eHierarchyId)boneIdx);
if ((GetVehicleRenderGfx() == NULL) || (boneIdx < 0) || (modBone < 0))
{
continue;
}
Vector3 bonePosition = VEC3_ZERO;
pVehicle->GetDefaultBonePosition(modBone, bonePosition);
SetBonePosition(i, bonePosition);
}
}
void CVehicleVariationInstance::ApplyDeformation(CVehicle* pParentVehicle, const void* basePtr)
{
if ((pParentVehicle == NULL) || (basePtr == NULL))
{
return;
}
UpdateBonePositions(pParentVehicle);
}
bool CVehicleVariationInstance::HasPreloadModsFinished()
{
if (!m_preloadData)
return true;
return m_preloadData->HasPreloadFinished();
}
void CVehicleVariationInstance::SetVehicleRenderGfx(CVehicleStreamRenderGfx* newRenderGfx)
{
if (m_renderGfx && newRenderGfx)
{
newRenderGfx->CopyMatrices(*m_renderGfx);
}
if (m_renderGfx)
{
gPopStreaming.RemoveStreamedVehicleVariation(m_renderGfx);
m_renderGfx->SetUsed(false);
}
m_renderGfx = newRenderGfx;
if (m_renderGfx)
{
gPopStreaming.AddStreamedVehicleVariation(m_renderGfx);
}
}
void CVehicleVariationInstance::InitSystem()
{
ms_MaxVehicleStreamRequest = fwConfigManager::GetInstance().GetSizeOfPool(ATSTRINGHASH("VehicleStreamRequest", 0x90d4bff4), CONFIGURED_FROM_FILE);
ms_MaxVehicleStreamRender = fwConfigManager::GetInstance().GetSizeOfPool(ATSTRINGHASH("VehicleStreamRender", 0xe11d5267), CONFIGURED_FROM_FILE);
CVehicleStreamRequestGfx::InitPool(ms_MaxVehicleStreamRequest, MEMBUCKET_STREAMING);
CVehicleStreamRenderGfx::InitPool(ms_MaxVehicleStreamRender, MEMBUCKET_STREAMING);
CVehicleStreamRenderGfx::Init();
#if __DEV
CVehicleStreamRenderGfx::GetPool()->RegisterPoolCallback(CVehicleStreamRenderGfx::PoolFullCallback);
#endif // __DEV
}
void CVehicleVariationInstance::ShutdownSystem()
{
CVehicleStreamRenderGfx::Shutdown();
CVehicleStreamRequestGfx::ShutdownPool();
CVehicleStreamRenderGfx::ShutdownPool();
}
void CVehicleVariationInstance::ProcessStreamRequests()
{
CVehicleStreamRequestGfx::Pool* pStreamReqPool = CVehicleStreamRequestGfx::GetPool();
if (pStreamReqPool->GetNoOfUsedSpaces() == 0)
return; // early out if no requests outstanding
CVehicleStreamRequestGfx* pStreamReq = NULL;
CVehicleStreamRequestGfx* pStreamReqNext = NULL;
s32 poolSize = pStreamReqPool->GetSize();
pStreamReqNext = pStreamReqPool->GetSlot(0);
s32 i = 0;
while(i < poolSize)
{
pStreamReq = pStreamReqNext;
// spin to next valid entry
while((++i < poolSize) && (pStreamReqPool->GetSlot(i) == NULL));
if (i != poolSize)
pStreamReqNext = pStreamReqPool->GetSlot(i);
// do any required processing on each interior
if (pStreamReq && pStreamReq->HaveAllLoaded())
{
// apply the files we requested and which have now all loaded to the target vehicle
CVehicle* pVehicle = pStreamReq->GetTargetEntity();
pStreamReq->RequestsComplete();
pVehicle->GetExtensionList().Destroy(pStreamReq); // will remove from pool & from extension list
}
}
}
void CVehicleVariationInstance::RequestVehicleModFiles(CVehicle* pVehicle, s32 streamFlags)
{
pVehicle->GetExtensionList().Destroy(CVehicleStreamRequestGfx::GetAutoId()); // strip all mod stream requests for this entity
CVehicleStreamRequestGfx* pNewStreamGfx = rage_new CVehicleStreamRequestGfx;
Assert(pNewStreamGfx);
pNewStreamGfx->SetTargetEntity(pVehicle);
s32 parentTxd = CVehicleModelInfo::GetCommonTxd();
s32 modelParentTxd = pVehicle->GetVehicleModelInfo()->GetAssetParentTxdIndex();
if (modelParentTxd != -1)
parentTxd = modelParentTxd;
const CVehicleVariationInstance& var = pVehicle->GetVariationInstance();
const CVehicleKit* kit = var.GetKit();
Assert(kit);
for (s32 i = 0; i < VMT_RENDERABLE; ++i)
{
u32 modEntry = var.GetMods()[i];
if (modEntry == INVALID_MOD)
continue;
const CVehicleModVisible& mod = kit->GetVisibleMods()[(eVehicleModType)modEntry];
pNewStreamGfx->AddFrag((u8)i, mod.GetNameHashString(), streamFlags, parentTxd);
}
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
u32 modEntry = var.GetLinkedMods()[i];
if (modEntry == INVALID_MOD)
continue;
const CVehicleModLink& mod = kit->GetLinkMods()[modEntry];
pNewStreamGfx->AddFrag((u8)(VMT_RENDERABLE + i), mod.GetNameHashString(), streamFlags, parentTxd);
}
eVehicleWheelType wheelType = var.GetWheelType(pVehicle);
u32 modEntry = var.GetMods()[VMT_WHEELS];
if (modEntry != INVALID_MOD && modEntry < CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType].GetCount())
{
bool variation = var.HasVariation(0);
const CVehicleWheel& wheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][modEntry];
pNewStreamGfx->AddDrawable(VMT_WHEELS, variation ? wheel.GetVariationHashString() : wheel.GetNameHashString(), streamFlags, CVehicleModelInfo::GetCommonTxd());
}
const u32 modelNameHash = pVehicle->GetModelNameHash();
if ((pVehicle->GetVehicleType() == VEHICLE_TYPE_BIKE) || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))
{
u32 modEntry = var.GetMods()[VMT_WHEELS_REAR_OR_HYDRAULICS];
if (modEntry != INVALID_MOD && modEntry < CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType].GetCount())
{
bool variation = var.HasVariation(1);
const CVehicleWheel& wheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][modEntry];
pNewStreamGfx->AddDrawable(VMT_WHEELS_REAR_OR_HYDRAULICS, variation ? wheel.GetVariationHashString() : wheel.GetNameHashString(), streamFlags, CVehicleModelInfo::GetCommonTxd());
}
}
pNewStreamGfx->PushRequest(); // activate this set of requests
}
const CVehicleKit* CVehicleVariationInstance::GetKit() const
{
#if !__FINAL
XPARAM(disableVehMods);
if(PARAM_disableVehMods.Get())
{
return NULL;
}
#endif
if (m_kitIdx == INVALID_VEHICLE_KIT_ID)
return NULL;
return &CVehicleModelInfo::GetVehicleColours()->m_Kits[m_kitIdx];
}
const CVehicle* pDebugVehicle = NULL;
const CVehicleKit* pDebugKit = NULL;
u32 CVehicleVariationInstance::GetNumModsForType(eVehicleModType modSlot, const CVehicle* pVehicle) const
{
pDebugVehicle = pVehicle;
const u32 modelNameHash = pVehicle->GetModelNameHash();
if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
{
eVehicleWheelType wheelType = GetWheelType(pVehicle);
s32 numWheels = 0;
const s32 totalNumWheels = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType].GetCount();
for (s32 i = 0; i < totalNumWheels; ++i)
{
if( CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][i].IsRearWheel() ||
(modSlot==VMT_WHEELS_REAR_OR_HYDRAULICS && ((modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2)))) // tornado6: all front wheels can be used as rear wheel
numWheels = (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS) ? numWheels + 1 : numWheels;
else
numWheels = (modSlot == VMT_WHEELS) ? numWheels + 1 : numWheels;
}
return numWheels;
}
const CVehicleKit* kit = GetKit();
pDebugKit = kit;
if (!kit)
return 0;
if (modSlot < VMT_RENDERABLE)
{
u32 count = 0;
for (s32 i = 0; i < kit->GetVisibleMods().GetCount(); ++i)
{
if (kit->GetVisibleMods()[i].GetType() == modSlot)
count++;
}
return count;
}
if (modSlot < VMT_TOGGLE_MODS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS)
{
u32 count = 0;
for (s32 i = 0; i < kit->GetStatMods().GetCount(); ++i)
{
if (kit->GetStatMods()[i].GetType() == modSlot)
count++;
}
return count;
}
// the rest are toggle mods
return 1;
}
bool CVehicleVariationInstance::IsGen9ExclusiveVehicleMod(eVehicleModType modSlot, u8 modIndex, const CVehicle* pVehicle) const
{
const CVehicleKit *pVehicleModKit = GetKit();
if (!Verifyf(pVehicleModKit, "CVehicleVariationInstance::IsGen9ExclusiveVehicleMod - this vehicle has no mod kit: %s", pVehicle->GetModelName()))
return false;
u8 globalModIndex = GetGlobalModIndex(modSlot, modIndex, pVehicle);
return CGen9ExclusiveAssets::IsGen9ExclusiveVehicleMod(pVehicleModKit, modSlot, globalModIndex);
}
void CVehicleVariationInstance::SetModIndexForType(eVehicleModType modSlot, u8 modIndex, CVehicle* pVehicle, bool variation, u32 streamingFlags)
{
if (!Verifyf(GetKit(), "Trying to set a mod on vehicle with no mod kit: %s", pVehicle->GetModelName()))
return;
u8 globalModIndex = GetGlobalModIndex(modSlot, modIndex, pVehicle);
SetModIndex(modSlot, globalModIndex, pVehicle, variation, streamingFlags);
}
u8 CVehicleVariationInstance::GetGlobalModIndex(eVehicleModType modSlot, u8 modIndex, const CVehicle* pVehicle) const
{
if (!Verifyf(GetKit(), "Trying to set a mod on vehicle with no mod kit: %s", pVehicle->GetModelName()))
return INVALID_MOD;
u8 globalModIndex = INVALID_MOD;
if (modIndex != INVALID_MOD)
{
const u32 modelNameHash = pVehicle->GetModelNameHash();
if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
{
u8 count = (u8)-1;
eVehicleWheelType wheelType = GetWheelType(pVehicle);
const s32 totalNumWheels = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType].GetCount();
for (s32 i = 0; i < totalNumWheels; ++i)
{
if (CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][i].IsRearWheel() ||
(modSlot==VMT_WHEELS_REAR_OR_HYDRAULICS && ((modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
count = (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS) ? count + 1 : count;
else
count = (modSlot == VMT_WHEELS) ? count + 1 : count;
if (count == modIndex)
{
globalModIndex = (u8)i;
break;
}
}
}
else
{
const CVehicleKit* kit = GetKit();
if (!kit)
return INVALID_MOD;
// modIndex is per type, so count the mods of this type and find out the index into the mod array of the kit
if (modSlot < VMT_RENDERABLE)
{
u8 count = (u8)-1;
for (s32 i = 0; i < kit->GetVisibleMods().GetCount(); ++i)
{
if (kit->GetVisibleMods()[i].GetType() == modSlot)
count++;
if (count == modIndex)
{
globalModIndex = (u8)i;
break;
}
}
}
else if (modSlot < VMT_TOGGLE_MODS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS)
{
u8 count = (u8)-1;
for (s32 i = 0; i < kit->GetStatMods().GetCount(); ++i)
{
if (kit->GetStatMods()[i].GetType() == modSlot)
count++;
if (count == modIndex)
{
globalModIndex = (u8)i;
break;
}
}
}
else
{
// Assertf(false, "Use ToggleMod() for other types of mods");
return modIndex;
}
}
}
return globalModIndex;
}
void CVehicleVariationInstance::SetPreloadModForType(eVehicleModType modSlot, u8 modIndex, CVehicle* pVehicle)
{
if (!Verifyf(GetKit(), "Trying to preload a mod on vehicle with no mod kit: %s", pVehicle->GetModelName()))
return;
u8 globalModIndex = GetGlobalModIndex(modSlot, modIndex, pVehicle);
if (globalModIndex == INVALID_MOD)
return;
const u32 modelNameHash = pVehicle->GetModelNameHash();
if (modSlot < VMT_RENDERABLE)
Assert(globalModIndex < CVehicleModelInfo::GetVehicleColours()->m_Kits[m_kitIdx].GetVisibleMods().GetCount());
else if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
Assert(globalModIndex < CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)].GetCount());
else
{
Assertf(false, "Trying to preload non renderable mod! slot(%d), index(%d) on vehicle <%s>", modSlot, modIndex, pVehicle->GetModelName());
return;
}
const CVehicleVariationInstance& var = pVehicle->GetVariationInstance();
const CVehicleKit* kit = var.GetKit();
Assert(kit);
s32 parentTxd = CVehicleModelInfo::GetCommonTxd();
s32 modelParentTxd = pVehicle->GetVehicleModelInfo()->GetAssetParentTxdIndex();
if (modelParentTxd != -1)
parentTxd = modelParentTxd;
if (!m_preloadData)
m_preloadData = rage_new CScriptPreloadData(0, 0, 0, VMT_RENDERABLE + MAX_LINKED_MODS, 32, true);
if (modSlot < VMT_RENDERABLE)
{
const CVehicleModVisible& mod = kit->GetVisibleMods()[globalModIndex];
s32 fragSlot = -1;
bool slotFound = true;
strLocalIndex idxFragStoreSlot = g_FragmentStore.FindSlot(mod.GetNameHash());
if(mod.GetNameHash() != MID_DUMMY_MOD_FRAG_MODEL)
{
Assertf(idxFragStoreSlot.IsValid(), "Cannot find frag slot for missing mod '%s' on vehicle '%s' - is it definitely in the .rpf?", mod.GetNameHashString().TryGetCStr(), pVehicle->GetModelName());
}
slotFound &= m_preloadData->AddFrag(idxFragStoreSlot, parentTxd, fragSlot);
// preload any linked mods too
for (s32 i = 0; i < mod.GetNumLinkedModels(); ++i)
{
if (!Verifyf(i < MAX_LINKED_MODS, "Too many linked mods in mod '%s' on vehicle '%s'", mod.GetNameHashString().TryGetCStr(), pVehicle->GetModelName()))
break;
u8 linkedModIndex = 0;
const CVehicleModLink* linkedMod = kit->FindLinkMod(mod.GetLinkedModelHash(i), linkedModIndex);
if (Verifyf(linkedMod && linkedModIndex != INVALID_MOD, "Couldn't find linked mod '%s' for mod '%s'", mod.GetLinkedModelHashString(i).GetCStr(), mod.GetNameHashString().GetCStr()))
{
slotFound &= m_preloadData->AddFrag(g_FragmentStore.FindSlot(mod.GetLinkedModelHash(i)), parentTxd, fragSlot);
}
}
Assertf(slotFound || idxFragStoreSlot.IsInvalid(), "Out of frag slots in vehicle preload data!");
}
else if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
{
s32 slot = -1;
bool slotFound = true;
const CVehicleWheel& wheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)][globalModIndex];
slotFound &= m_preloadData->AddDrawable(g_DrawableStore.FindSlot(wheel.GetNameHash()), CVehicleModelInfo::GetCommonTxd(), slot);
if (wheel.HasVariation())
slotFound &= m_preloadData->AddDrawable(g_DrawableStore.FindSlot(wheel.GetVariationHash()), CVehicleModelInfo::GetCommonTxd(), slot);
Assertf(slotFound, "Out of drawable slots in vehicle preload data!");
}
}
u8 CVehicleVariationInstance::GetModIndexForType(eVehicleModType modSlot, const CVehicle* pVehicle, bool& variation) const
{
variation = false;
u8 modIndex = INVALID_MOD;
if (m_mods[modSlot] == INVALID_MOD)
return INVALID_MOD;
const u32 modelNameHash = pVehicle->GetModelNameHash();
if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
{
u8 count = (u8)-1;
for (s32 i = 0; i <= m_mods[modSlot]; ++i)
{
int numWheelMods = CVehicleModelInfo::GetVehicleColours()->m_Wheels[ GetWheelType(pVehicle) ].GetCount();
if( numWheelMods > i )
{
if(CVehicleModelInfo::GetVehicleColours()->m_Wheels[ GetWheelType(pVehicle)][i].IsRearWheel())
{
count = (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS) ? count + 1 : count;
}
else
{
count = (modSlot == VMT_WHEELS) ? count + 1 : count;
}
}
}
modIndex = count;
variation = HasVariation(modSlot - VMT_WHEELS);
}
else if (m_mods[modSlot] != INVALID_MOD)
{
const CVehicleKit* kit = GetKit();
if (!kit)
return INVALID_MOD;
if (modSlot < VMT_RENDERABLE)
{
u8 count = (u8)-1;
for (s32 i = 0; i <= m_mods[modSlot]; ++i)
{
if (kit->GetVisibleMods()[i].GetType() == modSlot)
count++;
}
modIndex = count;
}
else if (modSlot < VMT_TOGGLE_MODS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS)
{
u8 count = (u8)-1;
for (s32 i = 0; i <= m_mods[modSlot]; ++i)
{
if (kit->GetStatMods()[i].GetType() == modSlot)
count++;
}
modIndex = count;
}
else
{
#if __BANK
// these are toggle mods so there are 2 options on and off
for( s32 i = 0; i <= m_mods[ modSlot ]; ++i )
{
if( kit->GetStatMods()[ i ].GetType() == modSlot )
{
modIndex = 2;
}
}
#else
Assertf(false, "Use ToggleMod() for other types of mods");
return INVALID_MOD;
#endif
}
}
return modIndex;
}
void CVehicleVariationInstance::SetModIndex(eVehicleModType modSlot, u8 modIndex, CVehicle* pVehicle, bool variation, u32 streamingFlags)
{
const CVehicleKit* kit = GetKit();
if (!Verifyf(kit, "Trying to set a mod on vehicle with no mod kit: %s", pVehicle->GetModelName()))
return;
if (!CGen9ExclusiveAssets::CanCreateVehicleMod(kit, modSlot, modIndex, true))
{
return;
}
const u32 modelNameHash = pVehicle->GetModelNameHash();
#if __ASSERT
if (modSlot < VMT_RENDERABLE)
Assertf(modIndex == INVALID_MOD || modIndex < kit->GetVisibleMods().GetCount(), "modSlot=%d modIndex=%u GetVisibleMods().GetCount()=%d", modSlot, modIndex, kit->GetVisibleMods().GetCount());
else if (modSlot < VMT_TOGGLE_MODS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && !(pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
Assertf(modIndex == INVALID_MOD || modIndex < kit->GetStatMods().GetCount(), "modSlot=%d modIndex=%u GetStatMods().GetCount()=%d", modSlot, modIndex, kit->GetStatMods().GetCount());
else if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
Assertf(modIndex == INVALID_MOD || modIndex < CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)].GetCount(), "modSlot=%d modIndex=%u wheels.GetCount()=%d", modSlot, modIndex, CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)].GetCount());
else
{
ToggleMod( modSlot, modIndex != INVALID_MOD );
//Assertf(false, "Use ToggleMod() for other types of mods");
return;
}
#endif
if (m_mods[modSlot] == INVALID_MOD && modIndex != INVALID_MOD)
{
ENABLE_FRAG_OPTIMIZATION_ONLY(pVehicle->GiveFragCacheEntry();)
m_numMods++;
}
if (m_mods[modSlot] != INVALID_MOD && modIndex == INVALID_MOD)
m_numMods--;
// need to get bone index of old mod
s32 boneIndexOld = GetModBoneIndex((u8)modSlot);
u8 oldMod = m_mods[modSlot];
m_mods[modSlot] = modIndex;
s32 boneIndexNew = GetModBoneIndex((u8)modSlot);
// turn extras on for old mod and off for new mod
if (modSlot < VMT_RENDERABLE)
{
if (boneIndexOld >= VEH_EXTRA_1 && boneIndexOld <= VEH_LAST_EXTRA)
{
if (oldMod != INVALID_MOD && kit->GetVisibleMods()[oldMod].GetTurnOffExtra())
{
// if we just removed a mod that had turned off an extra, restore it now
pVehicle->TurnOffExtra((eHierarchyId)boneIndexOld, false);
}
}
if (boneIndexNew >= VEH_EXTRA_1 && boneIndexNew <= VEH_LAST_EXTRA)
{
if (modIndex != INVALID_MOD && kit->GetVisibleMods()[modIndex].GetTurnOffExtra())
{
// if we add a mod that turns off an extra, do it now
pVehicle->TurnOffExtra((eHierarchyId)boneIndexNew, true);
}
}
if (oldMod != INVALID_MOD)
{
int iCount = kit->GetVisibleMods()[oldMod].GetNumBonesToTurnOff();
for (s32 i = 0; i < iCount; ++i)
{
s32 bone = kit->GetVisibleMods()[oldMod].GetBoneToTurnOff(i);
if (bone == -1)
continue;
if (bone >= VEH_EXTRA_1 && bone <= VEH_LAST_EXTRA)
{
// if we just removed a mod that had turned off an extra, restore it now
pVehicle->TurnOffExtra((eHierarchyId)bone, false);
}
}
}
if (modIndex != INVALID_MOD)
{
int iCount = kit->GetVisibleMods()[modIndex].GetNumBonesToTurnOff();
for (s32 i = 0; i < iCount; ++i)
{
s32 bone = kit->GetVisibleMods()[modIndex].GetBoneToTurnOff(i);
if (bone == -1)
continue;
if (bone >= VEH_EXTRA_1 && bone <= VEH_LAST_EXTRA)
{
// if we add a mod that turns off an extra, do it now
pVehicle->TurnOffExtra((eHierarchyId)bone, true);
}
}
}
// turn off any collision bones the old mod might have turned on
if (oldMod != INVALID_MOD)
{
bool hasExtendedModCollision = pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_EXTENDED_COLLISION_MODS );
CVehicleModVisible::eVehicleModBone collisionBone = kit->GetVisibleMods()[oldMod].GetCollisionBone();
if( ( hasExtendedModCollision && collisionBone != -1 ) ||
( collisionBone >= CVehicleModVisible::mod_col_1 && collisionBone <= CVehicleModVisible::mod_col_16 ) )
{
SetModCollision(pVehicle, collisionBone, false);
}
}
// turn on collision the new mod might need
if (modIndex != INVALID_MOD)
{
bool hasExtendedModCollision = pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_EXTENDED_COLLISION_MODS );
CVehicleModVisible::eVehicleModBone collisionBone = kit->GetVisibleMods()[modIndex].GetCollisionBone();
if( ( hasExtendedModCollision && collisionBone != -1 ) ||
( collisionBone >= CVehicleModVisible::mod_col_1 && collisionBone <= CVehicleModVisible::mod_col_16 ) )
{
SetModCollision(pVehicle, collisionBone, true);
}
}
}
if(modSlot == VMT_ENGINE)
{
float fDriveForce = pVehicle->pHandling->m_fInitialDriveForce;
//HACK
if(pVehicle->GetModelIndex() == MI_BIKE_SANCHEZ || pVehicle->GetModelIndex() == MI_BIKE_SANCHEZ2 )
{
if(NetworkInterface::IsGameInProgress())
{
float adjustmentMultiplier = 1.0f + (SANCHEZ_MP_DRIVE_FORCE_ADJUSTMENT/100.0f);
fDriveForce = pVehicle->pHandling->m_fInitialDriveForce * adjustmentMultiplier;
}
}
#if __BANK
//static u32 s_maxEngineModifier = 100;
//if( pVehicle->GetVariationInstance().GetEngineModifier() > s_maxEngineModifier )
//{
// atHashString dlcPackName = pVehicle->GetVehicleModelInfo()->GetVehicleDLCPack();
// atHashString s_currentDLCPackName( "dlc_mpChristmas2018CRC", 0x99443AE5 );
// if( dlcPackName == s_currentDLCPackName )
// {
// Assertf( false, "CVehicleVariationInstance::SetModIndex Engine mod for vehicle '%s' is too high %d", pVehicle->GetDebugName(), pVehicle->GetVariationInstance().GetEngineModifier() );
// }
//}
#endif // #if __BANK
pVehicle->m_Transmission.SetDriveForce( fDriveForce * (1.0f + (CVehicle::ms_fEngineVarianceMaxModifier * (pVehicle->GetVariationInstance().GetEngineModifier()/100.0f))) );
if( MI_CAR_BANSHEE2.IsValid() && pVehicle->GetModelIndex() == MI_CAR_BANSHEE2.GetModelIndex() )
{
bool dummy = false;
if( pVehicle->GetVariationInstance().GetModIndexForType(VMT_ENGINE, pVehicle, dummy) != INVALID_MOD )
{
float percentageIncrease = 4.0f + ( 4.0f * ( pVehicle->GetVariationInstance().GetModIndexForType(VMT_ENGINE, pVehicle, dummy) + 1 ) );
CVehicleFactory::ModifyVehicleTopSpeed( pVehicle, percentageIncrease );
}
}
if( CFlyingHandlingData* pFlyingHandling = pVehicle->pHandling->GetFlyingHandlingData() )
{
if( pFlyingHandling->m_fInitialThrustFallOff == 0.0f )
{
pFlyingHandling->m_fInitialThrustFallOff = pFlyingHandling->m_fThrustFallOff;
}
if( pFlyingHandling->m_fInitialThrust == 0.0f )
{
pFlyingHandling->m_fInitialThrust = pFlyingHandling->m_fThrust;
}
float increase = pVehicle->GetModelIndex() == MI_PLANE_MICROLIGHT && pVehicle->GetVariationInstance().GetModIndex( VMT_EXHAUST ) != INVALID_MOD ? 1.0f : 0.0f;
increase += ( pVehicle->GetVariationInstance().GetEngineModifier() / 100.0f );
pFlyingHandling->m_fThrust = pFlyingHandling->m_fInitialThrust * (1.0f + ( CVehicle::ms_fEngineVarianceMaxModifier * increase ) );
pFlyingHandling->m_fThrustFallOff = pFlyingHandling->m_fInitialThrustFallOff * (1.0f - ( CVehicle::ms_fThrustDropOffVarianceMaxModifier * increase ) );
}
}
static bool sbEnableMicrolightVisiblePropellerMods = false;
if( sbEnableMicrolightVisiblePropellerMods &&
modSlot == VMT_EXHAUST &&
pVehicle->GetModelIndex() == MI_PLANE_MICROLIGHT )
{
if( CFlyingHandlingData* pFlyingHandling = pVehicle->pHandling->GetFlyingHandlingData() )
{
if( pFlyingHandling->m_fInitialThrustFallOff == 0.0f )
{
pFlyingHandling->m_fInitialThrustFallOff = pFlyingHandling->m_fThrustFallOff;
}
if( pFlyingHandling->m_fInitialThrust == 0.0f )
{
pFlyingHandling->m_fInitialThrust = pFlyingHandling->m_fThrust;
}
float increase = pVehicle->GetVariationInstance().GetModIndex( VMT_EXHAUST ) != INVALID_MOD ? 1.0f : 0.0f;
increase += ( pVehicle->GetVariationInstance().GetEngineModifier() / 100.0f );
pFlyingHandling->m_fThrust = pFlyingHandling->m_fInitialThrust * ( 1.0f + ( CVehicle::ms_fEngineVarianceMaxModifier * increase ) );
pFlyingHandling->m_fThrustFallOff = pFlyingHandling->m_fInitialThrustFallOff * (1.0f - ( CVehicle::ms_fThrustDropOffVarianceMaxModifier * increase ) );
if( sbEnableMicrolightVisiblePropellerMods )
{
u8 modIndex = pVehicle->GetVariationInstance().GetModIndex( VMT_EXHAUST );
for( int i = 0; i < PLANE_NUM_PROPELLERS; i++ )
{
bool enablePropeller = false;
if( ( modIndex == INVALID_MOD && i == 0 ) ||
( modIndex == i ) )
{
enablePropeller = true;
}
static_cast< CPlane* >( pVehicle )->EnableIndividualPropeller( i, enablePropeller );
}
static_cast< CPlane* >( pVehicle )->UpdatePropellerCollision();
}
}
}
if(modSlot == VMT_ICE)
{
//Gen9 HSW Top Speed upgrades when ICE mod is equipped
if(pVehicle->HasExpandedMods() && pVehicle->pHandling && pVehicle->pHandling->GetCarHandlingData())
{
// NOTE: The modIndex passed into this function is the globalModIndex, but we want the modIndex for type here.
bool variation = false;
int appliedModIndex = (int)pVehicle->GetVariationInstance().GetModIndexForType(VMT_ICE, pVehicle, variation);
for (int i = 0; i < pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData.GetCount(); i++)
{
int advancedDataModSlot = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[i].m_Slot;
// NOTE: ICE is the mod slot for the HSW engine upgrades
if (advancedDataModSlot == VMT_ICE)
{
if (appliedModIndex == pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[i].m_Index)
{
float percentageIncrease = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[i].m_Value;
CVehicleFactory::ModifyVehicleTopSpeed(pVehicle, percentageIncrease);
}
}
}
}
}
if(modSlot == VMT_BRAKES)
{
if( CFlyingHandlingData* pFlyingHandling = pVehicle->pHandling->GetFlyingHandlingData() )
{
float increase = ( pVehicle->GetVariationInstance().GetBrakesModifier() / 100.0f );
if( pFlyingHandling->m_fInitialYawMult == 0.0f )
{
pFlyingHandling->m_fInitialYawMult = pFlyingHandling->m_fYawMult;
}
if( pFlyingHandling->m_fInitialRollMult == 0.0f )
{
pFlyingHandling->m_fInitialRollMult = pFlyingHandling->m_fRollMult;
}
if( pFlyingHandling->m_fInitialPitchMult == 0.0f )
{
pFlyingHandling->m_fInitialPitchMult = pFlyingHandling->m_fPitchMult;
}
pFlyingHandling->m_fYawMult = pFlyingHandling->m_fInitialYawMult * (1.0f + ( CVehicle::ms_fPlaneHandlingVarianceMaxModifier * increase ) );
pFlyingHandling->m_fRollMult = pFlyingHandling->m_fInitialRollMult * (1.0f + ( CVehicle::ms_fPlaneHandlingVarianceMaxModifier * increase ) );
pFlyingHandling->m_fPitchMult = pFlyingHandling->m_fInitialPitchMult * (1.0f + ( CVehicle::ms_fPlaneHandlingVarianceMaxModifier * increase ) );
}
}
if(modSlot == VMT_GEARBOX)
{
// NOTE: dont mod gerbox on CVT vehicles
bool bDontModGearbox = (MI_CAR_CYCLONE2.IsValid() && (pVehicle->GetModelIndex() == MI_CAR_CYCLONE2.GetModelIndex())) ? true : false;
if (m_mods[modSlot] != INVALID_MOD && !bDontModGearbox )
{
pVehicle->m_Transmission.SetupTransmission(pVehicle->m_Transmission.GetDriveForce(), pVehicle->m_Transmission.GetDriveMaxFlatVelocity(), pVehicle->m_Transmission.GetDriveMaxVelocity(), pVehicle->pHandling->m_nInitialDriveGears + 1, pVehicle);
}
else
{
pVehicle->m_Transmission.SetupTransmission(pVehicle->m_Transmission.GetDriveForce(), pVehicle->m_Transmission.GetDriveMaxFlatVelocity(), pVehicle->m_Transmission.GetDriveMaxVelocity(), pVehicle->pHandling->m_nInitialDriveGears, pVehicle);
}
if( MI_CAR_BANSHEE2.IsValid() && pVehicle->GetModelIndex() == MI_CAR_BANSHEE2.GetModelIndex() )
{
bool dummy = false;
if( pVehicle->GetVariationInstance().GetModIndexForType(VMT_ENGINE, pVehicle, dummy) != INVALID_MOD )
{
float percentageIncrease = 4.0f + ( 4.0f * ( pVehicle->GetVariationInstance().GetModIndexForType(VMT_ENGINE, pVehicle, dummy) + 1 ) );
CVehicleFactory::ModifyVehicleTopSpeed( pVehicle, percentageIncrease );
}
}
}
if(modSlot == VMT_SUSPENSION)
{
if(pVehicle->InheritsFromAutomobile())
{
CAutomobile *pAutomobile = static_cast<CAutomobile*>(pVehicle);
float suspensionModifier = Clamp( (float)pVehicle->GetVariationInstance().GetSuspensionModifier(), 0.0f, 100.0f );
pAutomobile->SetFakeSuspensionLoweringAmount((CVehicle::ms_fSuspensionVarianceMaxModifier * (suspensionModifier/100.0f)));
}
}
if(modSlot == VMT_HYDRAULICS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS/*hack, reuse VMT_WHEELS_REAR_OR_HYDRAULICS for VMT_HYDRAULICS*/)
{
// tornado6 uses WHEELS_REAR and not HYDRAULICS mod:
if((pVehicle->GetVehicleType()==VEHICLE_TYPE_CAR) && (modelNameHash!=MID_TORNADO6) && (modelNameHash!=MID_IMPALER2) && (modelNameHash!=MID_IMPALER4) && (modelNameHash!=MID_PEYOTE2))
{
CAutomobile *pAutomobile = static_cast<CAutomobile*>(pVehicle);
float hydraulicsModifier = (pVehicle->GetVariationInstance().GetHydraulicsModifier()/100.0f);
if(hydraulicsModifier > 0.0f)
{
float upperBound = CVehicle::ms_fHydraulicVarianceUpperBoundMinModifier + ( ( CVehicle::ms_fHydraulicVarianceUpperBoundMaxModifier - CVehicle::ms_fHydraulicVarianceUpperBoundMinModifier ) * hydraulicsModifier );
float lowerBound = CVehicle::ms_fHydraulicVarianceLowerBoundMinModifier + ( ( CVehicle::ms_fHydraulicVarianceLowerBoundMaxModifier - CVehicle::ms_fHydraulicVarianceLowerBoundMinModifier ) * hydraulicsModifier );
float rate = CVehicle::ms_fHydraulicRateMin + ( ( CVehicle::ms_fHydraulicRateMax - CVehicle::ms_fHydraulicRateMin ) * hydraulicsModifier );
pAutomobile->SetHydraulicsBounds( upperBound, -lowerBound );
pAutomobile->SetHydraulicsRate( rate );
}
}
}
if(modSlot == VMT_SPOILER)
{
if(pVehicle->InheritsFromAutomobile())
{
// Don't apply downforce values for VMT_SPOILER modifications linked to a weapon (not actually spoilers)
bool bSpoilerModLinkedToWeapon = modIndex != INVALID_MOD ? kit->GetVisibleMods()[modIndex].GetWeaponSlot() != -1 : false;
//Make sure we reset any dynamic spoilers to prevent bits disappearing as mods are changed.
for(int i = 0; i < pVehicle->GetNumberOfVehicleGadgets(); i++)
{
if(pVehicle->GetVehicleGadget(i)->GetType() == VGT_DYNAMIC_SPOILER)
{
((CVehicleGadgetDynamicSpoiler*)pVehicle->GetVehicleGadget(i))->ResetSpoilerBones();
}
}
if (!bSpoilerModLinkedToWeapon &&
!pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_SPOILER_MOD_DOESNT_INCREASE_GRIP ) &&
( !pVehicle->pHandling->GetCarHandlingData() ||
!( pVehicle->pHandling->GetCarHandlingData()->aFlags & CF_USE_DOWNFORCE_BIAS ) ) )
{
if(oldMod != INVALID_MOD && modIndex == INVALID_MOD) // Remove Spoiler
{
CAutomobile *pAutomobile = static_cast<CAutomobile*>(pVehicle);
for(int wheelIndex = 0; wheelIndex < pAutomobile->GetNumWheels(); ++wheelIndex)
{
CWheel* pWheel = pAutomobile->GetWheel(wheelIndex);
pWheel->m_fDownforceMult = CWheel::ms_fDownforceMult;
}
}
else // Add spoiler
{
CAutomobile *pAutomobile = static_cast<CAutomobile*>(pVehicle);
for(int wheelIndex = 0; wheelIndex < pAutomobile->GetNumWheels(); ++wheelIndex)
{
CWheel* pWheel = pAutomobile->GetWheel(wheelIndex);
// Tell the wheels we have a spoiler so we can increase down force
pWheel->m_fDownforceMult = CWheel::ms_fDownforceMultSpoiler;
}
}
}
else
{
if( pVehicle->pHandling->GetCarHandlingData() &&
pVehicle->pHandling->GetCarHandlingData()->aFlags & CF_USE_DOWNFORCE_BIAS )
{
float fDownforceModifier = 1.0f;
for( int i = 0; i < pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData.GetCount(); i++ )
{
int advancedDataModSlot = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Slot;
if( ( advancedDataModSlot == -1 || advancedDataModSlot == (int)modSlot ) &&
modIndex == pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Index )
{
fDownforceModifier = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Value;
break;
}
}
pVehicle->m_fDownforceModifierRear = fDownforceModifier;
}
}
}
}
if( modSlot == VMT_BUMPER_F )
{
if( pVehicle->pHandling->GetCarHandlingData() &&
pVehicle->pHandling->GetCarHandlingData()->aFlags & CF_USE_DOWNFORCE_BIAS )
{
float fDownforceModifier = 1.0f;
for( int i = 0; i < pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData.GetCount(); i++ )
{
int advancedDataModSlot = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Slot;
if( ( advancedDataModSlot == -1 || advancedDataModSlot == (int)modSlot ) &&
modIndex == pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Index )
{
fDownforceModifier = pVehicle->pHandling->GetCarHandlingData()->m_AdvancedData[ i ].m_Value;
break;
}
}
pVehicle->m_fDownforceModifierFront = fDownforceModifier;
}
}
if (modSlot < VMT_RENDERABLE)
{
//First get the bone indices from the vehicle model info
int lightBoneIds[VEHICLE_LIGHT_COUNT];
int totallighBoneIds = VEHICLE_LIGHT_COUNT;
for(int i=0; i<VEHICLE_LIGHT_COUNT; i++)
{
lightBoneIds[i] = pVehicle->GetBoneIndex((eHierarchyId)(VEH_HEADLIGHT_L+i));
}
// remove old linked mods
if (oldMod != INVALID_MOD)
{
const CVehicleModVisible& mod = kit->GetVisibleMods()[oldMod];
for (s32 i = 0; i < mod.GetNumLinkedModels(); ++i)
{
u8 linkedModIndex = 0;
const CVehicleModLink* linkedMod = kit->FindLinkMod(mod.GetLinkedModelHash(i), linkedModIndex);
if (Verifyf(linkedMod && linkedModIndex != INVALID_MOD, "Couldn't find linked mod '%s' for mod '%s'", mod.GetNameHashString().GetCStr(), mod.GetLinkedModelHashString(i).GetCStr()))
{
for (s32 f = 0; f < MAX_LINKED_MODS; ++f)
{
if (m_linkedMods[f] == linkedModIndex)
{
// turn extra on if we turned it off when this mod was set
s32 boneIndex = GetModBoneIndex((u8)(VMT_RENDERABLE + f));
m_linkedMods[f] = INVALID_MOD;
if (boneIndex >= VEH_EXTRA_1 && boneIndex <= VEH_LAST_EXTRA && linkedMod->GetTurnOffExtra())
{
pVehicle->TurnOffExtra((eHierarchyId)boneIndex, false);
}
}
}
}
}
//Reset all the lights broken by the existing mod
int iCount = mod.GetNumBonesToTurnOff();
for (s32 f = 0; f < iCount; ++f)
{
s32 bone = mod.GetBoneToTurnOff(f);
if (bone == -1)
continue;
int boneIdx = pVehicle->GetBoneIndex((eHierarchyId)bone);
if (Verifyf(boneIdx != -1, "Invalid bone index in vehicle variation instance on slot %i, bone %d (%s)", modSlot, boneIdx, pVehicle->GetModelName()))
{
for(int lightBoneIdx = 0; lightBoneIdx<totallighBoneIds; lightBoneIdx++)
{
if(boneIdx == lightBoneIds[lightBoneIdx])
{
//reset the light set by this mod
pVehicle->GetVehicleDamage()->SetLightState(lightBoneIdx+VEH_HEADLIGHT_L, false);
}
}
}
}
}
// NOTE: B*7594679 hack. dont allow linked mod on granger2 VMT_CHASSIS
bool bAllowMod = true;
#if RSG_GEN9
if (pVehicle->GetModelIndex() == MI_CAR_GRANGER2 && modSlot == VMT_CHASSIS)
{
bAllowMod = false;
}
#endif
// add new linked mods
if (modIndex != INVALID_MOD && bAllowMod )
{
const CVehicleModVisible& mod = kit->GetVisibleMods()[modIndex];
for (s32 i = 0; i < mod.GetNumLinkedModels(); ++i)
{
u8 linkedModIndex = 0;
const CVehicleModLink* linkedMod = kit->FindLinkMod(mod.GetLinkedModelHash(i), linkedModIndex);
if (Verifyf(linkedMod && linkedModIndex != INVALID_MOD, "Couldn't find linked mod '%s' for mod '%s'", mod.GetLinkedModelHashString(i).GetCStr(), mod.GetNameHashString().GetCStr()))
{
u8 index;
if (SetLinkedModIndex(linkedModIndex, index))
{
// turn off extra if required
s32 boneIndex = GetModBoneIndex(VMT_RENDERABLE + index);
if (boneIndex >= VEH_EXTRA_1 && boneIndex <= VEH_LAST_EXTRA && linkedMod->GetTurnOffExtra())
{
pVehicle->TurnOffExtra((eHierarchyId)boneIndex, true);
}
}
}
}
//Now break all the light invalidated by the new mod
int iCount = mod.GetNumBonesToTurnOff();
for (s32 f = 0; f < iCount; ++f)
{
s32 bone = mod.GetBoneToTurnOff(f);
if (bone == -1)
continue;
int boneIdx = pVehicle->GetBoneIndex((eHierarchyId)bone);
if (Verifyf(boneIdx != -1, "Invalid bone index in vehicle variation instance on slot %i, bone %d (%s)", modSlot, boneIdx, pVehicle->GetModelName()))
{
for(int lightBoneIdx = 0; lightBoneIdx<totallighBoneIds; lightBoneIdx++)
{
if(boneIdx == lightBoneIds[lightBoneIdx])
{
//break light if mod does not support it
pVehicle->GetVehicleDamage()->SetLightState(lightBoneIdx+VEH_HEADLIGHT_L, true);
}
}
}
}
}
}
if (modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
{
u32 bitIndex = modSlot - VMT_WHEELS;
if (!variation || modIndex == INVALID_MOD || (modIndex >= CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)].GetCount()))
DisableVariation(bitIndex);
else
{
bool hasVariation = CVehicleModelInfo::GetVehicleColours()->m_Wheels[GetWheelType(pVehicle)][modIndex].HasVariation();
if (hasVariation)
EnableVariation(bitIndex);
else
DisableVariation(bitIndex);
}
}
if ((modSlot == VMT_LIVERY_MOD) && (modIndex == INVALID_MOD))
{
pVehicle->SetLiveryModFrag(strLocalIndex(strLocalIndex::INVALID_INDEX));
}
if( modSlot == VMT_CHASSIS2 )
{
if( pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_RAMMING_SCOOP_MOD ) )
{
for( u32 i = VEH_FIRST_SCOOP_MOD; i <= VEH_LAST_SCOOP_MOD; i++ )
{
u32 collisionBoneIndex = modIndex != INVALID_MOD ? kit->GetVisibleMods()[ modIndex ].GetCollisionBone() : (u32)-1;
bool enableCollision = i == collisionBoneIndex;
pVehicle->GetVariationInstance().SetModCollision( pVehicle, i, enableCollision );
}
}
}
// request new assets
if (modSlot < VMT_RENDERABLE || modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))))
RequestVehicleModFiles(pVehicle, streamingFlags);
if ((modSlot == VMT_WHEELS || (modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS && (pVehicle->InheritsFromBike() || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2)))) && m_renderGfx)
m_renderGfx->RestoreWheelRimRadius(pVehicle);
if ( pVehicle->GetVehicleType() == VEHICLE_TYPE_BIKE ||
(pVehicle->IsReverseTrike() && (modelNameHash!=MID_STRYDER)) || // B*6144657 - Stryder: Oversized back tire when custom wheels are applied to trike
(pVehicle->IsTrike()) ||
(modelNameHash==MID_TORNADO6) ||
(modelNameHash==MID_IMPALER2) ||
(modelNameHash==MID_IMPALER4) ||
(modelNameHash==MID_PEYOTE2) )
{
m_canHaveRearWheels = true;
}
if(oldMod != m_mods[modSlot])
{
pVehicle->GetVehicleAudioEntity()->OnModChanged(modSlot, modIndex);
bool bShouldEquipBestWeapon = false;
// Recreate the vehicle weapon manager when certain types of mods are applied.
if (CVehicleWeaponMgr::IsValidModTypeForVehicleWeapon(modSlot))
{
bool bVehicleWeaponManagerExisted = pVehicle->GetVehicleWeaponMgr() != NULL;
s32 iNumVehicleWeapons = bVehicleWeaponManagerExisted ? pVehicle->GetVehicleWeaponMgr()->GetNumVehicleWeapons() : 0;
pVehicle->RecreateVehicleWeaponMgr();
if (!pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_EQUIP_UNARMED_ON_ENTER))
{
// If we new have a vehicle weapon manager, equip one of the new vehicle weapons.
if (!bVehicleWeaponManagerExisted && pVehicle->GetVehicleWeaponMgr())
{
bShouldEquipBestWeapon = true;
}
// If we no longer have a vehicle weapon manager, equip best weapon (which clears any selected vehicle weapon index)
else if (bVehicleWeaponManagerExisted && !pVehicle->GetVehicleWeaponMgr())
{
bShouldEquipBestWeapon = true;
}
// If we now have less vehicle weapons than we had available before we recreated the manager, equip best weapon (which clears any invalid selected vehicle weapon index)
else if (pVehicle->GetVehicleWeaponMgr() && iNumVehicleWeapons > pVehicle->GetVehicleWeaponMgr()->GetNumVehicleWeapons())
{
bShouldEquipBestWeapon = true;
}
}
}
pVehicle->InitialiseWeaponBlades();
pVehicle->InitialiseSpikeCollision();
pVehicle->InitialiseRammingScoops();
pVehicle->InitialiseRamps();
pVehicle->InitialiseRammingBars();
// If a chassis mod will disable driveby, reset the weapon selection of all players in vehicle (as it may no longer be valid)
if ((modSlot == VMT_CHASSIS || modSlot == VMT_ROOF || modSlot == VMT_WING_L) && modIndex != INVALID_MOD && pVehicle->ContainsPlayer())
{
const CVehicleModVisible& mod = kit->GetVisibleMods()[modIndex];
if (mod.IsDriveByDisabled() || mod.IsProjectileDriveByDisabled())
{
bShouldEquipBestWeapon = true;
}
}
if (bShouldEquipBestWeapon)
{
for (s32 i = 0; i < pVehicle->GetSeatManager()->GetMaxSeats(); i++)
{
CPed* pPed = pVehicle->GetSeatManager()->GetPedInSeat(i);
if (pPed && pPed->IsPlayer() && pPed->GetWeaponManager())
{
pPed->GetWeaponManager()->EquipBestWeapon();
}
}
}
}
UpdateBonePositions(pVehicle);
}
bool CVehicleVariationInstance::SetLinkedModIndex(u8 modIndex, u8& index)
{
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
if (m_linkedMods[i] == INVALID_MOD)
{
index = (u8)i;
m_linkedMods[i] = modIndex;
return true;
}
}
Assertf(false, "Tried to set more than %d linked mods!", MAX_LINKED_MODS);
return false;
}
s32 CVehicleVariationInstance::GetModBoneIndex(u8 modSlot) const
{
const CVehicleKit* kit = GetKit();
Assert(kit);
if (!Verifyf(kit, "No kit found on vehicle but trying to render mods!"))
return -1;
if (modSlot < VMT_RENDERABLE)
{
u32 modEntry = GetMods()[modSlot];
if (modEntry == INVALID_MOD)
return -1;
const CVehicleModVisible& mod = kit->GetVisibleMods()[modEntry];
return mod.GetBone();
}
else
{
modSlot -= VMT_RENDERABLE;
if (modSlot < MAX_LINKED_MODS)
{
u32 modEntry = GetLinkedMods()[modSlot];
if (modEntry == INVALID_MOD)
return -1;
const CVehicleModLink& mod = kit->GetLinkMods()[modEntry];
return mod.GetBone();
}
}
return -1;
}
void CVehicleVariationInstance::ToggleMod(eVehicleModType modSlot, bool toggleOn)
{
if (Verifyf(modSlot >= VMT_TOGGLE_MODS && modSlot < VMT_MISC_MODS, "Can't toggle mods of type %d", modSlot))
{
m_mods[modSlot] = toggleOn ? 1 : INVALID_MOD;
}
}
bool CVehicleVariationInstance::IsToggleModOn(eVehicleModType modSlot) const
{
if (Verifyf(modSlot >= VMT_TOGGLE_MODS && modSlot < VMT_MISC_MODS, "Mod type %d is not a toggle mod", modSlot))
{
return m_mods[modSlot] != INVALID_MOD;
}
return false;
}
void CVehicleVariationInstance::ClearMods()
{
for (s32 i = 0; i < VMT_MAX; ++i)
{
m_mods[i] = INVALID_MOD;
}
m_numMods = 0;
}
#if GTA_REPLAY
//When we jump around on the timeline we change the vehicle variation but this doesnt reset
//the linked mods so end up filling the array.
void CVehicleVariationInstance::ClearLinkedModsForReplay()
{
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
m_linkedMods[i] = INVALID_MOD;
}
}
#endif //GTA_REPLAY
u32 CVehicleVariationInstance::GetTotalWeightModifier() const
{
u32 totalWeight = 0;
Assert(GetKit());
for (s32 i = 0; i < VMT_MAX; ++i)
{
if (m_mods[i] == INVALID_MOD)
continue;
if (i < VMT_RENDERABLE)
{
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
totalWeight += mod.GetWeightModifier();
}
else if (i < VMT_TOGGLE_MODS || i == VMT_WHEELS_REAR_OR_HYDRAULICS)
{
const CVehicleModStat& mod = GetKit()->GetStatMods()[m_mods[i]];
totalWeight += mod.GetWeightModifier();
}
else
{
// toggle mods have no weight
}
}
return totalWeight;
}
float CVehicleVariationInstance::GetArmourDamageMultiplier() const
{
u32 currentModifier = GetArmourModifier();
if (currentModifier == 0)
{
return 1.0f;
}
float armorPct = (float)currentModifier / 100.0f;
float multiplier = 1.0f - (CVehicleDamage::ms_fArmorModMagnitude * armorPct);
return multiplier;
}
bool CVehicleVariationInstance::HideModOnBone(const CVehicle* veh, s32 boneIndex, s8& slotTurnedOff)
{
slotTurnedOff = -1;
if (!GetKit() || !veh)
return false;
bool ret = false;
for (s32 i = 0; i < VMT_RENDERABLE; ++i)
{
if (m_mods[i] == INVALID_MOD)
continue;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
s32 modBone = veh->GetBoneIndex((eHierarchyId)mod.GetBone());
if (modBone != boneIndex)
continue;
// turn off this mod
m_renderableIsVisible &= ~(BIT_64(i));
#if __BANK
if( slotTurnedOff != -1 )
{
Warningf("Vehicle: %s\tMod: %d is on the same bone as Mod: %d", veh->GetModelName(), i, slotTurnedOff );
}
#endif
Assign(slotTurnedOff, i);
ret = true;
}
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
if (m_linkedMods[i] == INVALID_MOD)
continue;
const CVehicleModLink& mod = GetKit()->GetLinkMods()[m_linkedMods[i]];
s32 modBone = veh->GetBoneIndex((eHierarchyId)mod.GetBone());
if (modBone != boneIndex)
continue;
// turn off this mod
m_renderableIsVisible &= ~(BIT_64(VMT_RENDERABLE + i));
ret = true;
}
return ret;
}
void CVehicleVariationInstance::ShowModOnBone(const CVehicle* veh, s32 boneIndex)
{
if (!GetKit() || !veh)
return;
for (s32 i = 0; i < VMT_RENDERABLE; ++i)
{
if (m_mods[i] == INVALID_MOD)
continue;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
s32 modBone = veh->GetBoneIndex((eHierarchyId)mod.GetBone());
if (modBone != boneIndex)
continue;
// turn off this mod
m_renderableIsVisible |= BIT_64(i);
}
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
if (m_linkedMods[i] == INVALID_MOD)
continue;
const CVehicleModLink& mod = GetKit()->GetLinkMods()[m_linkedMods[i]];
s32 modBone = veh->GetBoneIndex((eHierarchyId)mod.GetBone());
if (modBone != boneIndex)
continue;
// turn off this mod
m_renderableIsVisible |= BIT_64(VMT_RENDERABLE + i);
}
}
bool CVehicleVariationInstance::IsBoneModed(eHierarchyId hierarchID)
{
for(s32 i = 0; i < VMT_RENDERABLE; ++i)
{
if(m_mods[i] == INVALID_MOD)
continue;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
if((eHierarchyId)mod.GetBone() == hierarchID)
{
return (m_renderableIsVisible & BIT_64(i)) != 0;
}
for(int b = 0; b < mod.GetNumBonesToTurnOff(); ++b)
{
if((eHierarchyId)mod.GetBoneToTurnOff(b) == hierarchID)
{
return true;
}
}
}
return false;
}
u8 CVehicleVariationInstance::GetModSlotFromBone(eHierarchyId hierarchID)
{
for(u8 i = 0; i < VMT_RENDERABLE; ++i)
{
if(m_mods[i] == INVALID_MOD)
continue;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
if((eHierarchyId)mod.GetBone() == hierarchID)
{
return i;
}
}
for (u8 i = 0; i < MAX_LINKED_MODS; ++i)
{
if (m_linkedMods[i] == INVALID_MOD)
continue;
const CVehicleModLink& mod = GetKit()->GetLinkMods()[m_linkedMods[i]];
if((eHierarchyId)mod.GetBone() == hierarchID)
{
return VMT_RENDERABLE + i;
}
}
return INVALID_MOD;
}
bool CVehicleVariationInstance::IsBoneTurnedOff(const CVehicle* veh, s32 boneIndex) const
{
if (!GetKit())
return false;
for (s32 i = 0; i < VMT_RENDERABLE; ++i)
{
if (m_mods[i] == INVALID_MOD)
continue;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[i]];
for (u32 f = 0; f < mod.GetNumBonesToTurnOff(); ++f)
{
if (veh->GetBoneIndex((eHierarchyId)mod.GetBoneToTurnOff(f)) == boneIndex)
return true;
}
}
return false;
}
s32 CVehicleVariationInstance::GetBone(u8 slot) const
{
if (!GetKit() || slot >= VMT_RENDERABLE || m_mods[slot] == INVALID_MOD)
return -1;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[slot]];
return (s32)mod.GetBone();
}
s32 CVehicleVariationInstance::GetBoneForLinkedMod(u8 slot) const
{
if (!GetKit() || slot >= MAX_LINKED_MODS || m_linkedMods[slot] == INVALID_MOD)
return -1;
const CVehicleModLink& mod = GetKit()->GetLinkMods()[m_linkedMods[slot]];
return (s32)mod.GetBone();
}
s32 CVehicleVariationInstance::GetBoneToTurnOffForSlot(u8 slot, u32 index) const
{
if (!GetKit() || slot >= VMT_RENDERABLE || m_mods[slot] == INVALID_MOD)
return -1;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[slot]];
if (index >= mod.GetNumBonesToTurnOff())
return -1;
return (s32)mod.GetBoneToTurnOff(index);
}
s32 CVehicleVariationInstance::GetNumBonesToTurnOffForSlot(u8 slot) const
{
if (!GetKit() || slot >= VMT_RENDERABLE || m_mods[slot] == INVALID_MOD)
return -1;
if( m_mods[slot] < GetKit()->GetVisibleMods().GetCount() )
{
if( const CVehicleModVisible* mod = &GetKit()->GetVisibleMods()[m_mods[slot]] )
{
return (s32)mod->GetNumBonesToTurnOff();
}
}
return 0;
}
bool CVehicleVariationInstance::ShouldDrawOriginalForSlot(CVehicle* veh, u8 slot) const
{
if (!GetKit() || slot >= VMT_RENDERABLE || m_mods[slot] == INVALID_MOD)
return false;
const CVehicleModVisible& mod = GetKit()->GetVisibleMods()[m_mods[slot]];
s32 modBone = mod.GetBone();
s32 boneIdx = veh->GetBoneIndex((eHierarchyId)modBone);
return !IsBoneTurnedOff(veh, boneIdx);
}
const atArray<CVehicleModColor>* GetColArray(eVehicleColorType type, bool base)
{
if (!CVehicleModelInfo::GetVehicleModColors())
return NULL;
const atArray<CVehicleModColor>* colArray = NULL;
switch (type)
{
case VCT_METALLIC:
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_metallic;
break;
case VCT_CLASSIC:
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_classic;
break;
case VCT_PEARLESCENT:
if (base)
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_pearlescent.m_baseCols;
else
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_pearlescent.m_specCols;
break;
case VCT_MATTE:
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_matte;
break;
case VCT_METALS:
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_metals;
break;
case VCT_CHROME:
colArray = &CVehicleModelInfo::GetVehicleModColors()->m_chrome;
break;
case VCT_CHAMELEON:
colArray = CVehicleModelInfo::GetVehicleModColorsGen9() ? &CVehicleModelInfo::GetVehicleModColorsGen9()->m_chameleon : NULL;
break;
case VCT_NONE:
default:
colArray = NULL;
};
return colArray;
}
void CVehicleVariationInstance::SetModColor1(eVehicleColorType type, int baseColIndex, int specColIndex)
{
m_colorType1 = type;
m_baseColorIndex = baseColIndex;
m_specColorIndex = specColIndex;
// set base and spec color
const atArray<CVehicleModColor>* colArray = GetColArray(m_colorType1, true);
if (colArray)
{
if (baseColIndex >= 0 && baseColIndex < colArray->GetCount())
{
m_color1 = (*colArray)[baseColIndex].col;
m_color3 = (*colArray)[baseColIndex].spec;
}
}
// if current type is pearlescent, set a custom spec color
if (m_colorType1 == VCT_PEARLESCENT)
{
const atArray<CVehicleModColor>* colArray = GetColArray(m_colorType1, false);
if (colArray)
{
if (specColIndex >= 0 && specColIndex < colArray->GetCount())
{
m_color3 = (*colArray)[specColIndex].col;
}
}
}
}
void CVehicleVariationInstance::SetModColor2(eVehicleColorType type, int baseColIndex)
{
m_colorType2 = type;
m_baseColorIndex2 = baseColIndex;
// color 2 doesn't support pearlescent with custom spec color because we only support one spec in the shader, and this comes from color 1
const atArray<CVehicleModColor>* colArray = GetColArray(m_colorType2, true);
if (colArray)
{
if (baseColIndex >= 0 && baseColIndex < colArray->GetCount())
{
m_color2 = (*colArray)[baseColIndex].col;
// ignore spec on color 2, we don't support that in the shader so use spec from color 1
}
}
}
void CVehicleVariationInstance::GetModColor1(eVehicleColorType& type, int& baseColIndex, int& specColIndex)
{
type = m_colorType1;
baseColIndex = m_baseColorIndex;
specColIndex = m_specColorIndex;
}
void CVehicleVariationInstance::GetModColor2(eVehicleColorType& type, int& baseColIndex)
{
type = m_colorType2;
baseColIndex = m_baseColorIndex2;
}
const char* CVehicleVariationInstance::GetModColor1Name(bool spec) const
{
const atArray<CVehicleModColor>* colArray = GetColArray(m_colorType1, !spec);
if (colArray)
{
if (m_colorType1 == VCT_PEARLESCENT && spec && m_specColorIndex > -1 && m_specColorIndex < colArray->GetCount())
return (*colArray)[m_specColorIndex].name;
else if (m_baseColorIndex > -1 && m_baseColorIndex < colArray->GetCount())
return (*colArray)[m_baseColorIndex].name;
}
return NULL;
}
const char* CVehicleVariationInstance::GetModColor2Name() const
{
const atArray<CVehicleModColor>* colArray = GetColArray(m_colorType2, true);
if (colArray)
{
if (m_baseColorIndex2 > -1 && m_baseColorIndex2 < colArray->GetCount())
return (*colArray)[m_baseColorIndex2].name;
}
return NULL;
}
eVehicleWheelType CVehicleVariationInstance::GetWheelType(const CVehicle* pVeh) const
{
if (!pVeh)
return VWT_SPORT;
if (m_wheelType >= 0 && m_wheelType < VWT_MAX)
return (eVehicleWheelType)m_wheelType;
return pVeh->GetVehicleModelInfo()->GetWheelType();
}
void CVehicleVariationInstance::SetWheelType(eVehicleWheelType type)
{
m_wheelType = (s8)type;
if (m_mods[VMT_WHEELS] != INVALID_MOD && m_mods[VMT_WHEELS] >= CVehicleModelInfo::GetVehicleColours()->m_Wheels[m_wheelType].GetCount())
m_mods[VMT_WHEELS] = 0;
if (m_mods[VMT_WHEELS_REAR_OR_HYDRAULICS] != INVALID_MOD && m_mods[VMT_WHEELS_REAR_OR_HYDRAULICS] >= CVehicleModelInfo::GetVehicleColours()->m_Wheels[m_wheelType].GetCount())
m_mods[VMT_WHEELS_REAR_OR_HYDRAULICS] = 0;
}
bool CVehicleVariationInstance::HaveModsStreamedIn(CVehicle* pVeh)
{
if (!pVeh)
return true;
CVehicleStreamRequestGfx* req = pVeh->GetExtensionList().GetExtension<CVehicleStreamRequestGfx>();
if (req)
return false;
return true;
}
void CVehicleVariationInstance::SetModCollision(CVehicle* pVeh, u32 slot, bool on)
{
if (!pVeh)
return;
fragInst* pFragInst = pVeh->GetVehicleFragInst();
Assertf(pFragInst, "Vehicle '%s' has no fraginst!", pVeh->GetModelName());
phBoundComposite* pBoundComp = static_cast<phBoundComposite*>(pFragInst->GetArchetype()->GetBound());
const s32 boneIdx = pVeh->GetBoneIndex((eHierarchyId)slot);
if (boneIdx != -1)
{
int group = pFragInst->GetGroupFromBoneIndex(boneIdx);
if (group > -1)
{
fragPhysicsLOD* physicsLOD = pFragInst->GetTypePhysics();
fragTypeGroup* pGroup = physicsLOD->GetGroup(group);
int child = pGroup->GetChildFragmentIndex();
for (s32 f = 0; f < pGroup->GetNumChildren(); ++f)
{
pBoundComp->SetIncludeFlags(child + f, on ? ArchetypeFlags::GTA_VEHICLE_INCLUDE_TYPES : 0);
}
}
}
}
void CVehicleVariationInstance::PreRender2()
{
m_modMatricesStoredThisFrame.ClearAllFlags();
}
bool CVehicleVariationInstance::HasModMatrixBeenStoredThisFrame(u8 i) const
{
vehicleAssert(i < 64);
return (m_modMatricesStoredThisFrame.IsFlagSet(BIT_64(i)));
}
void CVehicleVariationInstance::SetModMatrixStoredThisFrame(u8 i)
{
vehicleAssert(i < 64);
m_modMatricesStoredThisFrame.SetFlag(BIT_64(i));
}
u32 CVehicleVariationInstance::GetModifier(eVehicleModType modSlot) const
{
if (!GetKit())
return 0;
if ((modSlot < VMT_STAT_MODS || modSlot >= VMT_TOGGLE_MODS) && modSlot != VMT_WHEELS_REAR_OR_HYDRAULICS)
{
Assertf(false, "Mod type %d has no modifier value", modSlot);
return 0;
}
if (m_mods[modSlot] == INVALID_MOD)
return 0;
const CVehicleModStat& mod = GetKit()->GetStatMods()[m_mods[modSlot]];
return mod.GetModifier();
}
u32 CVehicleVariationInstance::GetWeightModifer(eVehicleModType modSlot) const
{
if ((modSlot < VMT_STAT_MODS || modSlot >= VMT_TOGGLE_MODS) && modSlot != VMT_WHEELS_REAR_OR_HYDRAULICS)
{
Assertf(false, "Mod type %d has no weight modifier value", modSlot);
return 0;
}
if (m_mods[modSlot] == INVALID_MOD)
return 0;
const CVehicleModStat& mod = GetKit()->GetStatMods()[m_mods[modSlot]];
return mod.GetWeightModifier();
}
u32 CVehicleVariationInstance::GetIdentifierHashForType(eVehicleModType modSlot, u32 index) const
{
const CVehicleKit* kit = GetKit();
if (!kit)
{
Assertf(false, "Vehicle has no mod kit assigned!");
return 0;
}
if ((modSlot < VMT_STAT_MODS || modSlot >= VMT_TOGGLE_MODS) && modSlot != VMT_WHEELS_REAR_OR_HYDRAULICS)
{
Assertf(false, "Mod type %d has no modifier value", modSlot);
return 0;
}
u32 modifier = 0;
s32 count = -1;
for (s32 i = 0; i < kit->GetStatMods().GetCount(); ++i)
{
if (kit->GetStatMods()[i].GetType() == modSlot)
count++;
if (count == (s32)index)
{
modifier = kit->GetStatMods()[i].GetIdentifierHash();
break;
}
}
return modifier;
}
u32 CVehicleVariationInstance::GetModifierForType(eVehicleModType modSlot, u32 index) const
{
const CVehicleKit* kit = GetKit();
if (!kit)
{
Assertf(false, "Vehicle has no mod kit assigned!");
return 0;
}
if ((modSlot < VMT_STAT_MODS || modSlot >= VMT_TOGGLE_MODS) && modSlot != VMT_WHEELS_REAR_OR_HYDRAULICS)
{
Assertf(false, "Mod type %d has no modifier value", modSlot);
return 0;
}
u32 modifier = 0;
s32 count = -1;
for (s32 i = 0; i < kit->GetStatMods().GetCount(); ++i)
{
if (kit->GetStatMods()[i].GetType() == modSlot)
count++;
if (count == (s32)index)
{
modifier = kit->GetStatMods()[i].GetModifier();
break;
}
}
return modifier;
}
const char* CVehicleVariationInstance::GetModShopLabelForType(eVehicleModType modSlot, u32 modIndex, CVehicle* pVehicle) const
{
u8 globalModIndex = GetGlobalModIndex(modSlot, (u8)modIndex, pVehicle);
const CVehicleKit* kit = GetKit();
if (!kit)
{
Assertf(false, "Vehicle has no mod kit assigned!");
return NULL;
}
if (modSlot >= VMT_RENDERABLE && modSlot != VMT_WHEELS && modSlot != VMT_WHEELS_REAR_OR_HYDRAULICS)
{
Assertf(false, "Mod type %d has no text label", modSlot);
return NULL;
}
const char* label = NULL;
if (modSlot < VMT_RENDERABLE)
{
int maxModIndex = kit->GetVisibleMods().size();
if( globalModIndex >= maxModIndex )
{
Assertf(false, "globalModIndex >= kit->GetVisibleMods().size().. globalModIndex = %d, kit->GetVisibleMods().size() = %d", globalModIndex, maxModIndex);
return NULL;
}
label = kit->GetVisibleMods()[globalModIndex].GetModShopLabel();
// s32 count = -1;
// for (s32 i = 0; i < kit->GetVisibleMods().GetCount(); ++i)
// {
// if (kit->GetVisibleMods()[i].GetType() == modSlot)
// count++;
//
// if (count == (s32)index)
// {
// label = kit->GetVisibleMods()[i].GetModShopLabel();
// break;
// }
// }
}
else if (modSlot == VMT_WHEELS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS)
{
eVehicleWheelType wheelType = GetWheelType(pVehicle);
if (globalModIndex < CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType].GetCount())
{
const CVehicleWheel& modWheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][globalModIndex];
label = modWheel.GetModShopLabel();
}
}
return label;
}
float CVehicleVariationInstance::GetAudioApply(eVehicleModType modSlot) const
{
if (!GetKit())
return 1.f;
if (modSlot >= VMT_TOGGLE_MODS)
{
Assertf(false, "Mod type %d has no audio apply modifier value", modSlot);
return 1.f;
}
if (m_mods[modSlot] == INVALID_MOD)
return 1.f;
if (modSlot < VMT_RENDERABLE)
return GetKit()->GetVisibleMods()[m_mods[modSlot]].GetAudioApply();
const CVehicleModStat& mod = GetKit()->GetStatMods()[m_mods[modSlot]];
return mod.GetAudioApply();
}
void CVehicleVariationInstance::SetXenonLightColor(u8 col)
{
Assert((col < CVehicleModelInfo::GetVehicleColours()->m_XenonLightColors.GetCount()) || (col==0xFF));
m_xenonLightColor = (col < CVehicleModelInfo::GetVehicleColours()->m_XenonLightColors.GetCount()) ? col : /*invalid*/0xff;
}
CVehicleModelInfoVariation::CVehicleModelInfoVariation()
{
#if __BANK
m_vehicleModelEditingIndex = 0;
m_vehicleCopyFromIndex = 0;
m_vehicleModelBankGroup = NULL;
m_vehicleCurrentModelBankGroup = NULL;
#endif
}
CVehicleModelInfoVariation::~CVehicleModelInfoVariation()
{
#if __BANK
DeleteWidgetNames();
bkBank* rootBank = BANKMGR.FindBank("Vehicles");
if (rootBank && m_vehicleModelBankGroup)
rootBank->DeleteGroup(*m_vehicleModelBankGroup);
if (rootBank && m_vehicleCurrentModelBankGroup)
rootBank->DeleteGroup(*m_vehicleCurrentModelBankGroup);
#endif
}
CVehicleVariationData::CVehicleVariationData() :
m_modelName("NOT SET"),
m_lightSettingsIndex(0),
m_sirenSettingsIndex(0),
m_lightSettings(INVALID_VEHICLE_LIGHT_SETTINGS_ID),
m_sirenSettings(INVALID_SIREN_SETTINGS_ID)
#if __BANK
, m_selectedColorBit(0)
#endif
{
}
void CVehicleModelInfoVariation::OnPostLoad()
{
for (s32 i = 0; i < m_variationData.GetCount(); ++i)
{
m_variationData[i].OnPostLoad();
}
}
void CVehicleModelInfoVariation::OnPreSave()
{
for (s32 i = 0; i < m_variationData.GetCount(); ++i)
{
m_variationData[i].OnPreSave();
}
}
#if __BANK
s32 QSortVehicleNames(const char* const* lhs, const char* const* rhs)
{
return stricmp(*lhs, *rhs);
}
void CVehicleModelInfoVariation::AddWidgets(bkBank& bank)
{
// copy out the model names for widgets
DeleteWidgetNames();
m_vehicleNames.Reserve(m_variationData.GetCount());
for (s32 i = 0; i < m_variationData.GetCount(); ++i)
{
m_vehicleNames.Push(StringDuplicate(m_variationData[i].GetModelName()));
}
m_vehicleNames.QSort(0, -1, QSortVehicleNames);
// build array of vehicle name indices into the unsorted list
// not very nice but needed to show the vehicles in alphabetical order in rag
m_vehicleIndices.Resize(m_vehicleNames.GetCount());
for (s32 i = 0; i < m_vehicleNames.GetCount(); ++i)
{
for (s32 f = 0; f < m_variationData.GetCount(); ++f)
{
if (!strcmp(m_vehicleNames[i], m_variationData[f].GetModelName()))
{
m_vehicleIndices[i] = (u16)f;
break;
}
}
}
m_vehicleModelBankGroup = bank.PushGroup("Vehicles", false);
bank.AddCombo("Current Vehicle Model", &m_vehicleModelEditingIndex, m_vehicleNames.GetCount(), m_vehicleNames.GetElements(), datCallback(MFA(CVehicleModelInfoVariation::RefreshVehicleWidgets), this));
bank.PopGroup();
}
void CVehicleModelInfoVariation::DeleteWidgetNames()
{
for (s32 i = 0; i < m_vehicleNames.GetCount(); ++i)
{
if (m_vehicleNames[i])
{
delete[] m_vehicleNames[i];
m_vehicleNames[i] = NULL;
}
}
m_vehicleNames.Reset();
m_vehicleIndices.Reset();
}
void CVehicleModelInfoVariation::RefreshVehicleWidgets()
{
s32 actualEditingIndex = m_vehicleIndices[m_vehicleModelEditingIndex];
if (actualEditingIndex >= m_variationData.GetCount())
actualEditingIndex = m_variationData.GetCount()-1;
// delete the old widgets
bkBank* rootBank = BANKMGR.FindBank("Vehicles");
if (rootBank && m_vehicleCurrentModelBankGroup)
rootBank->DeleteGroup(*m_vehicleCurrentModelBankGroup);
m_vehicleCurrentModelBankGroup = NULL;
if (m_variationData.GetCount() > 0)
{
if (rootBank && m_vehicleModelBankGroup)
{
m_vehicleCurrentModelBankGroup = m_vehicleModelBankGroup->AddGroup(m_variationData[actualEditingIndex].GetModelName(), true);
rootBank->SetCurrentGroup(*m_vehicleCurrentModelBankGroup);
rootBank->AddButton("Add Color", datCallback(MFA(CVehicleModelInfoVariation::WidgetAddVehicleColor), this));
m_variationData[actualEditingIndex].AddWidgets(*rootBank,
CVehicleModelInfo::GetVehicleColours()->GetColorNames(),
CVehicleModelInfo::GetVehicleColours()->GetLightNames(),
CVehicleModelInfo::GetVehicleColours()->GetSirenNames(),
CVehicleModelInfo::GetVehicleColours()->GetLicensePlateData());
rootBank->AddCombo("Vehicle to copy colors from", &m_vehicleCopyFromIndex, m_vehicleNames.GetCount(), m_vehicleNames.GetElements());
rootBank->AddButton("Copy Colors", datCallback(MFA(CVehicleModelInfoVariation::WidgetCopyColorsFromVehicle), this));
rootBank->UnSetCurrentGroup(*m_vehicleCurrentModelBankGroup);
}
}
else
{
m_vehicleModelEditingIndex = 0;
m_vehicleCopyFromIndex = 0;
}
}
void CVehicleModelInfoVariation::ReshuffleColors(s32 editingIndex)
{
for(s32 i = 0; i < m_variationData.GetCount(); ++i)
{
for (s32 j = 0; j < m_variationData[i].GetNumColors(); ++j)
{
CVehicleModelColorIndices& c = m_variationData[i].GetColor(j);
if (c.m_indices[0] >= editingIndex && editingIndex > 0)
c.m_indices[0]--;
if (c.m_indices[1] >= editingIndex && editingIndex > 0)
c.m_indices[1]--;
if (c.m_indices[2] >= editingIndex && editingIndex > 0)
c.m_indices[2]--;
if (c.m_indices[3] >= editingIndex && editingIndex > 0)
c.m_indices[3]--;
}
}
}
void CVehicleModelInfoVariation::ReshuffleLights(s32 editingIndex)
{
for(s32 i = 0; i < m_variationData.GetCount(); ++i)
{
u32 setting = m_variationData[i].GetLightSettings();
if (setting >= editingIndex && editingIndex > 0)
{
setting--;
m_variationData[i].SetLightSettings(setting);
}
}
}
void CVehicleModelInfoVariation::ReshuffleSirens(s32 editingIndex)
{
for(s32 i = 0; i < m_variationData.GetCount(); ++i)
{
u32 setting = m_variationData[i].GetSirenSettings();
if (setting >= editingIndex && editingIndex > 0)
{
setting--;
m_variationData[i].SetSirenSettings(setting);
}
}
}
void CVehicleModelInfoVariation::WidgetAddVehicleColor()
{
u8 currentColor = (u8)CVehicleModelInfo::GetVehicleColours()->GetColorEditingIndex();
s32 actualEditingIndex = (s32)m_vehicleIndices[m_vehicleModelEditingIndex];
CVehicleModelColorIndices& c = m_variationData[actualEditingIndex].AddColor();
// set to the current selected color
c.m_indices[0] = currentColor;
c.m_indices[1] = currentColor;
c.m_indices[2] = 0;
c.m_indices[3] = 0;
// refresh the widgets
RefreshVehicleWidgets();
}
void CVehicleModelInfoVariation::WidgetCopyColorsFromVehicle()
{
s32 actualCopyFromIndex = (s32)m_vehicleIndices[m_vehicleCopyFromIndex];
s32 actualEditingIndex = (s32)m_vehicleIndices[m_vehicleModelEditingIndex];
if (m_variationData.GetCount() > 0 && m_variationData.GetCount() > actualCopyFromIndex && m_variationData.GetCount() > actualEditingIndex)
{
while(m_variationData[actualEditingIndex].GetNumColors() > 0)
{
m_variationData[actualEditingIndex].WidgetRemoveVehicleColor(m_variationData[actualEditingIndex].GetNumColors() - 1);
}
if (m_variationData[actualCopyFromIndex].GetNumColors() > 0)
{
for(int i=0;i<m_variationData[actualCopyFromIndex].GetNumColors();i++)
{
m_variationData[actualEditingIndex].AddColor() = m_variationData[actualCopyFromIndex].GetColor(i);
}
}
}
// refresh the widgets
RefreshVehicleWidgets();
}
void CVehicleVariationData::AddWidgets(bkBank& bank, const atArray<const char *>& colorNames, const atArray<const char *>& lightNames, const atArray<const char *>& sirenNames, const CVehicleModelInfoPlates &plateInfo)
{
char colIdxName[64];
if (m_colors.GetCount() > m_selectedColorBit.GetNumBits() )
{
// grow the selected color bitset (and keep the existing selected bit if there was one)
int selectedBit = m_selectedColorBit.GetFirstBitIndex();
m_selectedColorBit.Init(m_colors.GetCount());
if (selectedBit >= 0)
m_selectedColorBit.Set(selectedBit, true);
}
bank.PushGroup("Colors");
for (s32 i = 0; i < m_colors.GetCount(); ++i)
{
CVehicleModelColorIndices & c = m_colors[i];
formatf(colIdxName, "Colour %d", i);
bank.AddTitle(colIdxName);
bank.AddToggle("Current Selection", &m_selectedColorBit, (u8) i, datCallback(MFA1(CVehicleVariationData::WidgetChangedColorSelection),this,(CallbackData)(size_t)i));
formatf(colIdxName, "Remove %d", i);
bank.AddButton(colIdxName, datCallback(CFA2(CVehicleVariationData::WidgetRemoveVehicleColorCB),this));
bank.AddCombo("Slot0",&c.m_indices[0],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddCombo("Slot1",&c.m_indices[1],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddCombo("Slot2",&c.m_indices[2],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddCombo("Slot3",&c.m_indices[3],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddCombo("Slot4",&c.m_indices[4],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddCombo("Slot5",&c.m_indices[5],colorNames.GetCount(), const_cast<const char**>(colorNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)(size_t)i));
bank.AddToggle("Enabled for livery 1", &c.m_liveries[0], datCallback(MFA1(CVehicleVariationData::WidgetChangedColorSelection), this, 0));
bank.AddToggle("Enabled for livery 2", &c.m_liveries[1], datCallback(MFA1(CVehicleVariationData::WidgetChangedColorSelection), this, 0));
bank.AddToggle("Enabled for livery 3", &c.m_liveries[2], datCallback(MFA1(CVehicleVariationData::WidgetChangedColorSelection), this, 0));
bank.AddToggle("Enabled for livery 4", &c.m_liveries[3], datCallback(MFA1(CVehicleVariationData::WidgetChangedColorSelection), this, 0));
}
bank.PopGroup();
bank.PushGroup("License Plate Probabilities");
m_plateProbabilities.AddWidgets(bank, plateInfo);
bank.PopGroup();
bank.PushGroup("Light Settings");
bank.AddCombo("Settings",&m_lightSettingsIndex,lightNames.GetCount(), const_cast<const char**>(lightNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)-1));
bank.PopGroup();
bank.PushGroup("Siren Settings");
bank.AddCombo("Settings",&m_sirenSettingsIndex,sirenNames.GetCount(), const_cast<const char**>(sirenNames.GetElements()), datCallback(MFA1(CVehicleVariationData::WidgetChangedVehicleColor),this,(CallbackData)-1));
bank.PopGroup();
}
void CVehicleVariationData::WidgetRemoveVehicleColorCB(CallbackData obj, CallbackData clientData)
{
bkWidget * button = (bkWidget*)clientData;
int colorIndex = 0;
if (button)
{
Assert(strncmp(button->GetTitle(),"Remove ",7)==0);
colorIndex = atoi(button->GetTitle()+7);
}
((CVehicleVariationData*)obj)->WidgetRemoveVehicleColor(colorIndex);
}
void CVehicleVariationData::WidgetRemoveVehicleColor(const int colorIndex)
{
if (colorIndex>=0 && colorIndex < m_colors.GetCount() && m_colors.GetCount()>0)
{
m_colors.Delete(colorIndex);
for(int i=colorIndex;i<m_selectedColorBit.GetNumBits()-1;i++)
m_selectedColorBit.Set(i,m_selectedColorBit.GetAndClear(i+1));
CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfoFromName( m_modelName, NULL );
modelinfoAssertf(pModelInfo, "ModelInfo doesn't exist");
if( pModelInfo )
{
pModelInfo->UpdateModelColors(*this, 256);
}
CVehicleModelInfo::RefreshVehicleWidgets();
}
}
void CVehicleVariationData::WidgetChangedVehicleColor(CallbackData variationIndexCD)
{
s32 variationIndex = (s32)(size_t)variationIndexCD;
CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfoFromName( m_modelName , NULL);
modelinfoAssertf(pModelInfo, "ModelInfo doesn't exist");
if( pModelInfo )
{
pModelInfo->UpdateModelColors(*this, 256);
}
if (variationIndex >= 0 /*&& m_selectedColorBit.IsSet(variationIndex)*/)
{
CEntityIterator entityIterator( IterateVehicles );
CEntity* pEntity = entityIterator.GetNext();
while(pEntity)
{
CVehicle * pVehicle = static_cast<CVehicle*>(pEntity);
if (pVehicle->GetVehicleModelInfo() == pModelInfo)
{
pVehicle->SetBodyColour1( pModelInfo->GetPossibleColours(0,variationIndex) );
pVehicle->SetBodyColour2( pModelInfo->GetPossibleColours(1,variationIndex) );
pVehicle->SetBodyColour3( pModelInfo->GetPossibleColours(2,variationIndex) );
pVehicle->SetBodyColour4( pModelInfo->GetPossibleColours(3,variationIndex) );
pVehicle->SetBodyColour5( pModelInfo->GetPossibleColours(4,variationIndex) );
pVehicle->SetBodyColour6( pModelInfo->GetPossibleColours(5,variationIndex) );
// let shaders know, that body colours changed
pVehicle->UpdateBodyColourRemapping();
}
pEntity = entityIterator.GetNext();
}
}
}
void CVehicleVariationData::WidgetChangedColorSelection(CallbackData variationIndex)
{
u32 selectedIndex = (u32)(size_t)variationIndex;
// turn off all the other bits and turn this bit on (if it was set and not reset)
const bool turningBitOn = m_selectedColorBit.IsSet(selectedIndex);
m_selectedColorBit.Reset();
m_selectedColorBit.Set(selectedIndex, turningBitOn);
// set all vehicles of this type to this color (or reset the possible color array in the model info if no bits are set)
WidgetChangedVehicleColor(variationIndex);
}
CVehicleModelColorIndices& CVehicleVariationData::AddColor()
{
CVehicleModelColorIndices& c = m_colors.Grow();
// grow the selected color bitset
int selectedBit = m_selectedColorBit.GetFirstBitIndex();
if (m_selectedColorBit.GetNumBits() < m_colors.GetCount())
m_selectedColorBit.Init(m_colors.GetCount());
if (selectedBit == -1)
// if nothing selected then make this new color the selected one
selectedBit = m_colors.GetCount()-1;
m_selectedColorBit.Set(selectedBit, true);
return c;
}
#endif // __BANK
void CVehicleVariationData::OnPostLoad()
{
Assert(m_lightSettings != INVALID_VEHICLE_LIGHT_SETTINGS_ID);
if(m_lightSettings == INVALID_VEHICLE_LIGHT_SETTINGS_ID)
{
if(m_lightSettingsIndex < CVehicleModelInfo::GetVehicleColours()->m_Lights.GetCount())
m_lightSettings = CVehicleModelInfo::GetVehicleColours()->m_Lights[m_lightSettingsIndex].id;
}
else
{
m_lightSettingsIndex = CVehicleModelInfo::GetVehicleColours()->GetLightIndexById(m_lightSettings);
}
Assert(m_sirenSettings != INVALID_SIREN_SETTINGS_ID);
if(m_sirenSettings == INVALID_SIREN_SETTINGS_ID)
{
if(m_sirenSettingsIndex < CVehicleModelInfo::GetVehicleColours()->m_Sirens.GetCount())
m_sirenSettings = CVehicleModelInfo::GetVehicleColours()->m_Sirens[m_sirenSettingsIndex].id;
}
else
{
m_sirenSettingsIndex = CVehicleModelInfo::GetVehicleColours()->GetSirenIndexById(m_sirenSettings);
}
}
void CVehicleVariationData::OnPreSave()
{
if(m_sirenSettingsIndex < CVehicleModelInfo::GetVehicleColours()->m_Sirens.GetCount())
m_sirenSettings = CVehicleModelInfo::GetVehicleColours()->m_Sirens[m_sirenSettingsIndex].id;
else
m_sirenSettings = INVALID_VEHICLE_LIGHT_SETTINGS_ID;
if(m_lightSettingsIndex < CVehicleModelInfo::GetVehicleColours()->m_Lights.GetCount())
m_lightSettings = CVehicleModelInfo::GetVehicleColours()->m_Lights[m_lightSettingsIndex].id;
else
m_lightSettings = INVALID_SIREN_SETTINGS_ID;
}
CVehicleStreamRequestGfx::CVehicleStreamRequestGfx() : m_pTargetEntity(NULL), m_state(VS_INIT), m_bDelayCompletion(false)
{
m_reqFrags.UseAsArray();
m_reqDrawables.UseAsArray();
}
CVehicleStreamRequestGfx::~CVehicleStreamRequestGfx()
{
Release();
Assert(m_state == VS_FREED);
}
s32 CVehicleStreamRequestGfx::AddFrag(u8 modSlot, const strStreamingObjectName fragName, s32 streamFlags, s32 parentTxd)
{
strLocalIndex fragSlotId = strLocalIndex(g_FragmentStore.FindSlot(fragName));
if (fragSlotId != -1)
{
// this check is pretty nasty, but the vehicle artists share mods between vehicles and the parent id WILL change
// causing streaming refcounting issues. this way we just don't reparent the mod asset and they'll have to make sure
// the parenting hierarchy matches for the shared mods.
if (g_FragmentStore.GetParentTxdForSlot(fragSlotId) == strLocalIndex(STRLOCALINDEX_INVALID))
{
g_FragmentStore.SetParentTxdForSlot(fragSlotId, strLocalIndex(parentTxd));
}
m_reqFrags.SetRequest(modSlot, fragSlotId.Get(), g_FragmentStore.GetStreamingModuleId(), streamFlags);
}
else
{
if(fragName.GetHash() != MID_DUMMY_MOD_FRAG_MODEL)
{
vehicleAssertf(false, "Streamed frag request for vehicle is not found : %s", fragName.GetCStr());
}
}
return fragSlotId.Get();
}
s32 CVehicleStreamRequestGfx::AddDrawable(eVehicleModType modSlot, const strStreamingObjectName drawableName, s32 streamFlags, s32 parentTxd)
{
strLocalIndex drawableSlotId = strLocalIndex(g_DrawableStore.FindSlot(drawableName));
if (drawableSlotId != -1)
{
g_DrawableStore.SetParentTxdForSlot(drawableSlotId, strLocalIndex(parentTxd));
// this will only be called for wheels
m_reqDrawables.SetRequest(modSlot - VMT_WHEELS, drawableSlotId.Get(), g_DrawableStore.GetStreamingModuleId(), streamFlags);
}
else
{
vehicleAssertf(false, "Streamed drawable request for vehicle is not found : %s", drawableName.GetCStr());
}
return drawableSlotId.Get();
}
void CVehicleStreamRequestGfx::Release()
{
m_reqFrags.ReleaseAll();
m_reqDrawables.ReleaseAll();
m_state = VS_FREED;
}
// requests have completed - add refs to the slots we wanted and free up the initial requests
void CVehicleStreamRequestGfx::RequestsComplete()
{
Assert(m_state == VS_REQ);
CVehicle* pTarget = GetTargetEntity();
if (Verifyf(CVehicleStreamRenderGfx::GetPool()->GetNoOfFreeSpaces() > 0, "No space left in CVehicleStreamRenderGfx pool. Too many modded cars created!"))
{
pTarget->GetVariationInstance().SetVehicleRenderGfx(rage_new CVehicleStreamRenderGfx(this, pTarget));
}
else
{
#if !__NO_OUTPUT
Displayf("Ran out of pool entries for CVehicleStreamRenderGfx frame %d", fwTimer::GetFrameCount());
for (s32 i = 0; i < CVehicleStreamRenderGfx::GetPool()->GetSize(); ++i)
{
CVehicleStreamRenderGfx* gfx = CVehicleStreamRenderGfx::GetPool()->GetSlot(i);
if (gfx)
{
Displayf("CVehicleStreamRenderGfx %d / %d: %s (%p) (%s) (%d refs) (frame %d)\n", i, CVehicleStreamRenderGfx::GetPool()->GetSize(), gfx->m_targetEntity ? gfx->m_targetEntity->GetModelName() : "NULL", gfx->m_targetEntity.Get(), gfx->m_used ? "used" : "not used", gfx->m_refCount, gfx->m_frameCreated);
if (gfx->m_targetEntity)
{
CVehicle* pCurrVehicle = gfx->m_targetEntity;
Displayf("Vehicle owned by %d (%s) \n",pCurrVehicle->GetOwnedBy(), (pCurrVehicle->GetOwnedBy()==ENTITY_OWNEDBY_SCRIPT) ? "SCRIPT" : "OTHER");
}
}
else
{
Displayf("CVehicleStreamRenderGfx %d / %d: null slot\n", i, CVehicleStreamRenderGfx::GetPool()->GetSize());
}
}
#endif // __NO_OUTPUT
if (pTarget->GetVariationInstance().GetVehicleRenderGfx())
{
pTarget->GetVariationInstance().SetVehicleRenderGfx(NULL);
}
// remove any mods to render the original vehicle (instead of missing geometry)
pTarget->GetVariationInstance().ClearMods();
}
// detect loading of livery mods & use to set livery override ptr
if (pTarget)
{
u32 modIndex = pTarget->GetVariationInstance().GetModIndex(VMT_LIVERY_MOD);
if (modIndex != INVALID_MOD)
{
CVehicleStreamRenderGfx* pGfx = pTarget->GetVariationInstance().GetVehicleRenderGfx();
if (AssertVerify(pGfx))
{
strLocalIndex fragSlotIdx = pGfx->GetFragIndex(VMT_LIVERY_MOD);
if (fragSlotIdx != strIndex::INVALID_INDEX)
{
grmShaderGroup *pShaderGroup = g_FragmentStore.Get(fragSlotIdx)->GetCommonDrawable()->GetShaderGroupPtr();
fwTxd const* pTxd = pShaderGroup ? pShaderGroup->GetTexDict() : NULL; // frag's internal txd
Assertf(pTxd,"vehicle %s, livery mod %d, does not have a .txd embedded in the mod frag", pTarget->GetModelName(),modIndex);
if (pTxd && pTxd->GetCount() > 0)
{
pTarget->SetLiveryModFrag(fragSlotIdx);
}
}
}
}
}
m_state = VS_USE;
}
void CVehicleStreamRequestGfx::SetTargetEntity(CVehicle* pEntity) {
Assert(pEntity);
// add this request extension to the extension list
pEntity->GetExtensionList().Add(*this);
m_pTargetEntity = pEntity;
}
CVehicleStreamRenderGfx::CVehicleStreamRenderGfx()
{
for(s32 i=0; i<VMT_RENDERABLE; i++)
{
m_varModVisible[i] = NULL;
}
for(s32 i=0; i<MAX_LINKED_MODS; i++)
{
m_varModLinked[i] = NULL;
}
m_varWheel[0] = m_varWheel[1] = NULL;
m_usingWheelVariation[0] = m_usingWheelVariation[1] = false;
for (s32 i = 0; i < NUM_EXHAUST_BONES; ++i)
{
m_exhaustBones[i].m_boneIndex = -1;
m_exhaustBones[i].m_modIndex = INVALID_MOD;
}
for (s32 i = 0; i < NUM_EXTRALIGHT_BONES; ++i)
{
m_extraLightBones[i].m_boneIndex = -1;
m_extraLightBones[i].m_modIndex = INVALID_MOD;
}
m_refCount = 0;
m_used = true;
m_wheelTyreRadiusScale[0] = 1.f;
m_wheelTyreWidthScale[0] = 1.f;
m_wheelTyreRadiusScale[1] = 1.f;
m_wheelTyreWidthScale[1] = 1.f;
#if !__NO_OUTPUT
m_targetEntity = NULL;
m_frameCreated = fwTimer::GetFrameCount();
#endif
}
const CVehicleVariationInstance* pDebugVar = NULL;
CVehicleStreamRenderGfx::CVehicleStreamRenderGfx(CVehicleStreamRequestGfx* pReq, CVehicle* targetEntity)
{
Assert(pReq);
m_wheelTyreRadiusScale[0] = 1.f;
m_wheelTyreWidthScale[0] = 1.f;
m_wheelTyreRadiusScale[1] = 1.f;
m_wheelTyreWidthScale[1] = 1.f;
m_usingWheelVariation[0] = m_usingWheelVariation[1] = false;
for (s32 i = 0; i < NUM_EXHAUST_BONES; ++i)
{
m_exhaustBones[i].m_boneIndex = -1;
m_exhaustBones[i].m_modIndex = INVALID_MOD;
}
for (s32 i = 0; i < NUM_EXTRALIGHT_BONES; ++i)
{
m_extraLightBones[i].m_boneIndex = -1;
m_extraLightBones[i].m_modIndex = INVALID_MOD;
}
if (pReq)
{
CVehicleModelInfo *pVMI = targetEntity->GetVehicleModelInfo();
const CVehicleVariationInstance& var = targetEntity->GetVariationInstance();
pDebugVar = &var;
const CVehicleKit* kit = var.GetKit();
Assert(kit);
for (s32 i = 0; i < VMT_RENDERABLE; ++i)
{
m_varModVisible[i] = NULL;
u32 modEntry = var.GetMods()[i];
if (modEntry == INVALID_MOD)
continue;
m_varModVisible[i] = (CVehicleModVisible*) &(kit->GetVisibleMods()[modEntry]);
}
for (s32 i = 0; i < MAX_LINKED_MODS; ++i)
{
m_varModLinked[i] = NULL;
u32 modEntry = var.GetLinkedMods()[i];
if (modEntry == INVALID_MOD)
continue;
m_varModLinked[i] = (CVehicleModLink*) &(kit->GetLinkMods()[modEntry]);
}
for(s32 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
strLocalIndex fragSlotId = strLocalIndex(pReq->GetFragId((u8)i));
if (fragSlotId.IsValid())
{
g_FragmentStore.AddRef(fragSlotId, REF_OTHER);
strLocalIndex* pVarFragIdx=NULL;
CCustomShaderEffectVehicleType** ppVarFragCseType=NULL;
if(i < VMT_RENDERABLE)
{ // renderable mod:
Assert(m_varModVisible[i]);
pVarFragIdx = &(m_varModVisible[i]->m_VariationFragIdx);
ppVarFragCseType = &(m_varModVisible[i]->m_VariationFragCseType);
}
else
{ // linked mod:
Assert(m_varModLinked[i-VMT_RENDERABLE]);
pVarFragIdx = &(m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragIdx);
ppVarFragCseType = &(m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragCseType);
}
Assert(pVarFragIdx);
Assert(ppVarFragCseType);
CCustomShaderEffectVehicleType* pVarFragCseType = *ppVarFragCseType;
#if __ASSERT
if(pVarFragCseType)
{
Assert(*pVarFragIdx == fragSlotId);
}
#endif
*pVarFragIdx = fragSlotId;
fragType* type = this->GetFrag((u8)i);
Assert(type);
if(type)
{
if(pVarFragCseType)
{
pVarFragCseType->AddRef();
}
else
{
fragDrawable* drawable = type->GetCommonDrawable();
if(drawable)
{
#if __ASSERT
extern char* CCustomShaderEffectBase_EntityName;
CCustomShaderEffectBase_EntityName = (char*)g_FragmentStore.GetName(fragSlotId);
#endif
CCustomShaderEffectVehicleType *pVehicleCSE = NULL;
if(drawable->GetClonedShaderGroupPtr())
{
pVehicleCSE = (CCustomShaderEffectVehicleType*)CCustomShaderEffectBaseType::SetupForDrawable(drawable, MI_TYPE_VEHICLE, NULL,
0, -1, drawable->GetClonedShaderGroupPtr());
}
else
{
pVehicleCSE = (CCustomShaderEffectVehicleType*)CCustomShaderEffectBaseType::SetupForDrawable(drawable, MI_TYPE_VEHICLE, NULL,
0, -1, NULL);
drawable->CloneShaderGroup(); // BS#2551385: clone drawable's shaderGroup for next use
}
Assert(pVehicleCSE);
*ppVarFragCseType = pVehicleCSE;
}
}
// check all frags for any additional exhaust bones they might add (should only check exhaust frags really)
const crSkeletonData& skelData = type->GetSkeletonData();
for (s32 f = 0; f < skelData.GetNumBones(); ++f)
{
const crBoneData* boneData = skelData.GetBoneData(f);
if (!boneData)
continue;
// exhaust bones
for (s32 g = 0 ; g < NUM_EXHAUST_BONES; ++g)
{
if (m_exhaustBones[g].m_boneIndex == -1 && !stricmp(boneData->GetName(), exhaustBoneNames[g]))
{
m_exhaustBones[g].m_boneIndex = (s8)boneData->GetIndex();
m_exhaustBones[g].m_modIndex = (u8)i;
break;
}
}
// extra light bones
CompileTimeAssert(NUM_EXTRALIGHT_BONES == 4);
const char *postfix = strstr(boneData->GetName(), "_ng");
if (m_extraLightBones[0].m_boneIndex == -1 && strstr(boneData->GetName(), "extralight_1") != NULL && postfix == NULL)
{
m_extraLightBones[0].m_boneIndex = (s8)boneData->GetIndex();
m_extraLightBones[0].m_modIndex = (u8)i;
}
else if (m_extraLightBones[1].m_boneIndex == -1 && strstr(boneData->GetName(), "extralight_2") != NULL && postfix == NULL)
{
m_extraLightBones[1].m_boneIndex = (s8)boneData->GetIndex();
m_extraLightBones[1].m_modIndex = (u8)i;
}
else if (m_extraLightBones[2].m_boneIndex == -1 && strstr(boneData->GetName(), "extralight_3") != NULL && postfix == NULL)
{
m_extraLightBones[2].m_boneIndex = (s8)boneData->GetIndex();
m_extraLightBones[2].m_modIndex = (u8)i;
}
else if (m_extraLightBones[3].m_boneIndex == -1 && strstr(boneData->GetName(), "extralight_4") != NULL && postfix == NULL)
{
m_extraLightBones[3].m_boneIndex = (s8)boneData->GetIndex();
m_extraLightBones[3].m_modIndex = (u8)i;
}
}
rmcDrawable* drawable = type->GetCommonDrawable();
if (drawable)
{
drawable->GetLodGroup().SetLodThresh(0, pVMI->GetVehicleLodDistance(VLT_LOD0));
drawable->GetLodGroup().SetLodThresh(1, pVMI->GetVehicleLodDistance(VLT_LOD1));
}
}
}
}
eVehicleWheelType wheelType = var.GetWheelType(targetEntity);
m_varWheel[0] = NULL;
u8 wheelEntry0 = var.GetMods()[VMT_WHEELS];
if(wheelEntry0 != INVALID_MOD)
{
m_varWheel[0] = &CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][ wheelEntry0 ];
}
const u32 modelNameHash = targetEntity->GetModelNameHash();
m_varWheel[1] = NULL;
if((targetEntity->GetVehicleType() == VEHICLE_TYPE_BIKE) || (modelNameHash==MID_TORNADO6) || (modelNameHash==MID_IMPALER2) || (modelNameHash==MID_IMPALER4) || (modelNameHash==MID_PEYOTE2))
{
u8 wheelEntry1 = var.GetMods()[VMT_WHEELS_REAR_OR_HYDRAULICS];
if(wheelEntry1 != INVALID_MOD)
{
m_varWheel[1] = &CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][ wheelEntry1 ];
}
}
bool overrideRimRadius = false;
if(m_varWheel[0])
{
m_usingWheelVariation[0] = var.HasVariation(0);
s32* varWheelDrawableIdx = m_usingWheelVariation[0] ? &m_varWheel[0]->m_VariationDrawableVarIdx : &m_varWheel[0]->m_VariationDrawableIdx;
CCustomShaderEffectVehicleType** varWheelCseType = m_usingWheelVariation[0] ? &m_varWheel[0]->m_VariationDrawableVarCseType : &m_varWheel[0]->m_VariationDrawableCseType;
strLocalIndex drawableSlotId = strLocalIndex(pReq->GetDrawableId(VMT_WHEELS));
if(drawableSlotId.IsValid())
{
g_DrawableStore.AddRef(drawableSlotId, REF_OTHER);
#if __ASSERT
if(*varWheelCseType)
{
Assert(*varWheelDrawableIdx == drawableSlotId.Get()); // must be the same drawable
}
#endif
*varWheelDrawableIdx = drawableSlotId.Get();
rmcDrawable* drawable = this->GetDrawable(VMT_WHEELS);
Assertf(drawable, "%s: Wheel drawable is missing! drawblIdx: %d cseType: %p", targetEntity->GetModelName(), *varWheelDrawableIdx, *varWheelCseType);
if(drawable)
{
drawable->GetLodGroup().SetLodThresh(0, pVMI->GetVehicleLodDistance(VLT_LOD0));
drawable->GetLodGroup().SetLodThresh(1, pVMI->GetVehicleLodDistance(VLT_LOD1));
if(*varWheelCseType)
{
(*varWheelCseType)->AddRef();
}
else
{
#if __ASSERT
extern char* CCustomShaderEffectBase_EntityName;
CCustomShaderEffectBase_EntityName = (char*)g_DrawableStore.GetName(drawableSlotId);
#endif
CCustomShaderEffectVehicleType *pVehicleCSE = (CCustomShaderEffectVehicleType*)CCustomShaderEffectBaseType::SetupForDrawable(drawable, MI_TYPE_VEHICLE, NULL);
Assert(pVehicleCSE);
(*varWheelCseType) = pVehicleCSE;
}
// compute and store wheel scale needed for this drawable
// first wheel is enough, all matrices are set up to account for front/rear differences
Vector3 bbMin, bbMax;
drawable->GetLodGroup().GetBoundingBox(bbMin, bbMax);
const CVehicleStructure* vehStruct = targetEntity->GetVehicleModelInfo()->GetStructure();
m_wheelTyreRadiusScale[0] = vehStruct->m_fWheelTyreRadius[0] / bbMax.z;
m_wheelTyreWidthScale[0] = vehStruct->m_fWheelTyreWidth[0] / (bbMax.x * 2.f);
// infernus2 has custom scale on rear wheel:
if(pVMI->GetModelNameHash() == MID_INFERNUS2)
{
u32 rearIndex = 0;
for (rearIndex = 0; rearIndex < NUM_VEH_CWHEELS_MAX; ++rearIndex)
{
if (vehStruct->m_isRearWheel & (1 << rearIndex))
break;
}
if (rearIndex >= NUM_VEH_CWHEELS_MAX)
rearIndex = 0;
m_wheelTyreRadiusScale[1] = vehStruct->m_fWheelTyreRadius[rearIndex] / bbMax.z;
m_wheelTyreWidthScale[1] = vehStruct->m_fWheelTyreWidth[rearIndex] / (bbMax.x * 2.f);
}
overrideRimRadius = true;
}
}
else
{
Assert(*varWheelDrawableIdx == -1);
Assert(*varWheelCseType == NULL);
*varWheelDrawableIdx = -1;
*varWheelCseType = NULL;
}
}
if(m_varWheel[1])
{
m_usingWheelVariation[1] = var.HasVariation(1);
s32* varWheelDrawableIdx = m_usingWheelVariation[1] ? &m_varWheel[1]->m_VariationDrawableVarIdx : &m_varWheel[1]->m_VariationDrawableIdx;
CCustomShaderEffectVehicleType** varWheelCseType = m_usingWheelVariation[1] ? &m_varWheel[1]->m_VariationDrawableVarCseType : &m_varWheel[1]->m_VariationDrawableCseType;
strLocalIndex drawableSlotId = strLocalIndex(pReq->GetDrawableId(VMT_WHEELS_REAR_OR_HYDRAULICS));
if (drawableSlotId.IsValid())
{
g_DrawableStore.AddRef(drawableSlotId, REF_OTHER);
#if __ASSERT
if(*varWheelCseType)
{
Assert(*varWheelDrawableIdx == drawableSlotId.Get());
}
#endif
*varWheelDrawableIdx = drawableSlotId.Get();
rmcDrawable* drawable = this->GetDrawable(VMT_WHEELS_REAR_OR_HYDRAULICS);
Assertf(drawable, "%s: Rear wheel drawable is missing! drawblIdx: %d cseType: %p", targetEntity->GetModelName(), *varWheelDrawableIdx, *varWheelCseType);
if(drawable)
{
drawable->GetLodGroup().SetLodThresh(0, pVMI->GetVehicleLodDistance(VLT_LOD0));
drawable->GetLodGroup().SetLodThresh(1, pVMI->GetVehicleLodDistance(VLT_LOD1));
if(*varWheelCseType)
{
(*varWheelCseType)->AddRef();
}
else
{
CCustomShaderEffectVehicleType *pVehicleCSE = (CCustomShaderEffectVehicleType*)CCustomShaderEffectBaseType::SetupForDrawable(drawable, MI_TYPE_VEHICLE, NULL);
Assert(pVehicleCSE);
*varWheelCseType = pVehicleCSE;
}
// compute and store wheel scale needed for this drawable
// we have an explicit rear wheel so store that value as well
Vector3 bbMin, bbMax;
drawable->GetLodGroup().GetBoundingBox(bbMin, bbMax);
const CVehicleStructure* vehStruct = targetEntity->GetVehicleModelInfo()->GetStructure();
// find a rear wheel so we get correct scaling, and hopefully we don't have moddable rear wheels of different sizes
u32 rearIndex = 0;
for (rearIndex = 0; rearIndex < NUM_VEH_CWHEELS_MAX; ++rearIndex)
if (vehStruct->m_isRearWheel & (1 << rearIndex))
break;
if (rearIndex >= NUM_VEH_CWHEELS_MAX)
rearIndex = 0;
m_wheelTyreRadiusScale[1] = vehStruct->m_fWheelTyreRadius[rearIndex] / bbMax.z;
m_wheelTyreWidthScale[1] = vehStruct->m_fWheelTyreWidth[rearIndex] / (bbMax.x * 2.f);
overrideRimRadius = true;
}
}
else
{
Assert(*varWheelDrawableIdx == -1);
Assert(*varWheelCseType == NULL);
*varWheelDrawableIdx = -1;
*varWheelCseType = NULL;
}
}
if (overrideRimRadius)
OverrideWheelRimRadius(targetEntity);
}
m_refCount = 0;
m_used = true;
StoreModMatrices(targetEntity);
#if !__NO_OUTPUT
m_targetEntity = targetEntity;
m_frameCreated = fwTimer::GetFrameCount();
#endif
pDebugVar = NULL;
}
CVehicleStreamRenderGfx::~CVehicleStreamRenderGfx()
{
ReleaseGfx();
}
void CVehicleStreamRenderGfx::ReleaseGfx()
{
for(s32 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
strLocalIndex* pVarFragIdx=NULL;
CCustomShaderEffectVehicleType** ppVarFragCseType=NULL;
if(i < VMT_RENDERABLE)
{ // renderable mod:
if(!m_varModVisible[i])
continue;
pVarFragIdx = &(m_varModVisible[i]->m_VariationFragIdx);
ppVarFragCseType = &(m_varModVisible[i]->m_VariationFragCseType);
}
else
{ // linked mod:
if(!m_varModLinked[i-VMT_RENDERABLE])
continue;
pVarFragIdx = &(m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragIdx);
ppVarFragCseType = &(m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragCseType);
}
Assert(pVarFragIdx);
Assert(ppVarFragCseType);
CCustomShaderEffectVehicleType* pVarFragCseType = *ppVarFragCseType;
if(pVarFragCseType)
{
if(pVarFragCseType->GetRefCount()==1)
{
fragType* type = this->GetFrag((u8)i);
if(type)
{
rmcDrawable* drawable = type->GetCommonDrawable();
if(drawable)
{
pVarFragCseType->RestoreModelInfoDrawable(drawable);
}
}
pVarFragCseType->RemoveRef();
(*ppVarFragCseType) = NULL;
pVarFragCseType = NULL;
}
else
{
pVarFragCseType->RemoveRef();
}
}
if((*pVarFragIdx) != -1)
{
g_FragmentStore.RemoveRef(*pVarFragIdx, REF_OTHER);
if(!pVarFragCseType)
{
(*pVarFragIdx) = -1;
}
}
}
if(m_varWheel[0])
{
s32* varWheelDrawableIdx = m_usingWheelVariation[0] ? &m_varWheel[0]->m_VariationDrawableVarIdx : &m_varWheel[0]->m_VariationDrawableIdx;
CCustomShaderEffectVehicleType** varWheelCseType = m_usingWheelVariation[0] ? &m_varWheel[0]->m_VariationDrawableVarCseType : &m_varWheel[0]->m_VariationDrawableCseType;
if (*varWheelCseType)
{
if((*varWheelCseType)->GetRefCount()==1)
{
rmcDrawable *drawable = this->GetDrawable(VMT_WHEELS);
if(drawable)
{
(*varWheelCseType)->RestoreModelInfoDrawable(drawable);
}
(*varWheelCseType)->RemoveRef();
*varWheelCseType = NULL;
}
else
{
(*varWheelCseType)->RemoveRef();
}
}
if (*varWheelDrawableIdx != -1)
{
g_DrawableStore.RemoveRef(strLocalIndex(*varWheelDrawableIdx), REF_OTHER);
if(!(*varWheelCseType))
{
*varWheelDrawableIdx = -1;
}
}
}
if(m_varWheel[1])
{
s32* varWheelDrawableIdx = m_usingWheelVariation[1] ? &m_varWheel[1]->m_VariationDrawableVarIdx : &m_varWheel[1]->m_VariationDrawableIdx;
CCustomShaderEffectVehicleType** varWheelCseType = m_usingWheelVariation[1] ? &m_varWheel[1]->m_VariationDrawableVarCseType : &m_varWheel[1]->m_VariationDrawableCseType;
if(*varWheelCseType)
{
if((*varWheelCseType)->GetRefCount()==1)
{
rmcDrawable *drawable = this->GetDrawable(VMT_WHEELS_REAR_OR_HYDRAULICS);
if(drawable)
{
(*varWheelCseType)->RestoreModelInfoDrawable(drawable);
}
(*varWheelCseType)->RemoveRef();
*varWheelCseType = NULL;
}
else
{
(*varWheelCseType)->RemoveRef();
}
}
if(*varWheelDrawableIdx != -1)
{
g_DrawableStore.RemoveRef(strLocalIndex(*varWheelDrawableIdx), REF_OTHER);
if(!(*varWheelCseType))
{
*varWheelDrawableIdx= -1;
}
}
}
}
void CVehicleStreamRenderGfx::AddRefsToGfx()
{
for (s32 i = 0; i < ms_renderRefs[ms_fenceUpdateIdx].GetCount(); ++i)
{
if (ms_renderRefs[ms_fenceUpdateIdx][i] == this)
return;
}
for (int i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
strLocalIndex fragIdx = strLocalIndex(-1);
if(i < VMT_RENDERABLE)
{ // renderable mod:
if(m_varModVisible[i])
{
fragIdx = m_varModVisible[i]->m_VariationFragIdx;
}
}
else
{ // linked mod:
if(m_varModLinked[i-VMT_RENDERABLE])
{
fragIdx = m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragIdx;
}
}
if (fragIdx != -1)
{
g_FragmentStore.AddRef(fragIdx, REF_RENDER);
gDrawListMgr->AddRefCountedModuleIndex(fragIdx.Get(), &g_FragmentStore);
}
}
if(m_varWheel[0])
{
strLocalIndex dwbIdx = m_usingWheelVariation[0] ? strLocalIndex(m_varWheel[0]->m_VariationDrawableVarIdx) : strLocalIndex(m_varWheel[0]->m_VariationDrawableIdx);
if (dwbIdx != -1)
{
g_DrawableStore.AddRef(dwbIdx, REF_RENDER);
gDrawListMgr->AddRefCountedModuleIndex(dwbIdx.Get(), &g_DrawableStore);
}
}
AddRef();
ms_renderRefs[ms_fenceUpdateIdx].Push(this);
}
#if __DEV
bool CVehicleStreamRenderGfx::ValidateStreamingIndexHasBeenCached()
{
bool indexInCache = true;
if(m_varWheel[0])
{
s32 dwbIdx = m_usingWheelVariation[0] ? m_varWheel[0]->m_VariationDrawableVarIdx : m_varWheel[0]->m_VariationDrawableIdx;
if (dwbIdx != -1)
{
//strIndex dwbStrIndex = g_DrawableStore.GetStreamingIndex(dwbIdx);
indexInCache = gDrawListMgr->ValidateModuleIndexHasBeenCached(dwbIdx, &g_DrawableStore);
}
}
for (int i = 0; indexInCache && i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
s32 fragIdx = -1;
if(i < VMT_RENDERABLE)
{ // renderable mod:
if(m_varModVisible[i])
{
fragIdx = m_varModVisible[i]->m_VariationFragIdx.Get();
}
}
else
{ // linked mod:
if(m_varModLinked[i-VMT_RENDERABLE])
{
fragIdx = m_varModLinked[i-VMT_RENDERABLE]->m_VariationFragIdx.Get();
}
}
if (fragIdx != -1)
{
//strIndex fragStrIndex = g_FragmentStore.GetStreamingIndex(fragIdx);
indexInCache = gDrawListMgr->ValidateModuleIndexHasBeenCached(fragIdx, &g_FragmentStore);
}
}
return indexInCache;
}
#endif
void CVehicleStreamRenderGfx::Init()
{
for (int i = 0; i < FENCE_COUNT; ++i)
{
ms_renderRefs[i].Reserve(CVehicleVariationInstance::ms_MaxVehicleStreamRender);
}
}
void CVehicleStreamRenderGfx::Shutdown()
{
for (int i = 0; i < FENCE_COUNT; ++i)
{
ms_renderRefs[i].Reset();
}
}
void CVehicleStreamRenderGfx::SetUsed(bool val)
{
m_used = val;
if (!m_used && !m_refCount)
delete this;
}
void CVehicleStreamRenderGfx::RemoveRefs()
{
s32 newUpdateIdx = (ms_fenceUpdateIdx + 1) % FENCE_COUNT;
for (s32 i = 0; i < ms_renderRefs[newUpdateIdx].GetCount(); ++i)
{
CVehicleStreamRenderGfx* gfx = ms_renderRefs[newUpdateIdx][i];
if (!gfx)
continue;
gfx->m_refCount--;
}
ms_renderRefs[newUpdateIdx].Resize(0);
ms_fenceUpdateIdx = newUpdateIdx;
// free unused entries
CVehicleStreamRenderGfx::Pool* pool = CVehicleStreamRenderGfx::GetPool();
if (pool)
{
for (s32 i = 0; i < pool->GetSize(); ++i)
{
CVehicleStreamRenderGfx* gfx = pool->GetSlot(i);
if (!gfx)
continue;
if (!gfx->m_used && !gfx->m_refCount)
delete gfx;
}
}
}
void CVehicleStreamRenderGfx::CopyMatrices(const CVehicleStreamRenderGfx& rOther)
{
for (u8 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
m_boneMatrices[i] = rOther.m_boneMatrices[i];
}
}
u8 CVehicleStreamRenderGfx::GetExhaustModIndex(eHierarchyId id) const
{
if (id < VEH_FIRST_EXHAUST || id > VEH_LAST_EXHAUST)
return INVALID_MOD;
Assert(id - VEH_FIRST_EXHAUST < NUM_EXHAUST_BONES);
return m_exhaustBones[id - VEH_FIRST_EXHAUST].m_modIndex;
}
s8 CVehicleStreamRenderGfx::GetExhaustBoneIndex(eHierarchyId id) const
{
if (id < VEH_FIRST_EXHAUST || id > VEH_LAST_EXHAUST)
return -1;
Assert(id - VEH_FIRST_EXHAUST < NUM_EXHAUST_BONES);
return m_exhaustBones[id - VEH_FIRST_EXHAUST].m_boneIndex;
}
u8 CVehicleStreamRenderGfx::GetExtraLightModIndex(eHierarchyId id) const
{
if (id < VEH_EXTRALIGHT_1 || id > VEH_EXTRALIGHT_4)
return INVALID_MOD;
Assert(id - VEH_EXTRALIGHT_1 < NUM_EXTRALIGHT_BONES);
return m_extraLightBones[id - VEH_EXTRALIGHT_1].m_modIndex;
}
s8 CVehicleStreamRenderGfx::GetExtraLightBoneIndex(eHierarchyId id) const
{
if (id < VEH_EXTRALIGHT_1 || id > VEH_EXTRALIGHT_4)
return -1;
Assert(id - VEH_EXTRALIGHT_1 < NUM_EXTRALIGHT_BONES);
return m_extraLightBones[id - VEH_EXTRALIGHT_1].m_boneIndex;
}
u8 CVehicleStreamRenderGfx::GetExtraLightCount() const
{
u8 count = 0;
for (s32 i = 0; i < NUM_EXTRALIGHT_BONES; ++i)
if (m_extraLightBones[i].m_boneIndex != -1)
count++;
return count;
}
fragType* CVehicleStreamRenderGfx::GetFrag(u8 modSlot) const
{
if(!Verifyf(modSlot < VMT_RENDERABLE + MAX_LINKED_MODS, "Invalid modSlot (%u)", modSlot))
{
return NULL;
}
strLocalIndex fragidx = strLocalIndex(-1);
if(modSlot < VMT_RENDERABLE)
{
if(m_varModVisible[modSlot])
{
fragidx = m_varModVisible[modSlot]->m_VariationFragIdx;
}
}
else
{
if(m_varModLinked[modSlot-VMT_RENDERABLE])
{
fragidx = m_varModLinked[modSlot-VMT_RENDERABLE]->m_VariationFragIdx;
}
}
if (fragidx.IsValid())
{
return g_FragmentStore.Get(fragidx);
}
return NULL;
}
CCustomShaderEffectVehicleType* CVehicleStreamRenderGfx::GetFragCSEType(u8 modSlot) const
{
if(!Verifyf(modSlot < VMT_RENDERABLE + MAX_LINKED_MODS, "Invalid modSlot (%u)", modSlot))
{
return NULL;
}
CCustomShaderEffectVehicleType *type=NULL;
if(modSlot < VMT_RENDERABLE)
{
if(m_varModVisible[modSlot])
{
type = m_varModVisible[modSlot]->m_VariationFragCseType;
}
}
else
{
if(m_varModLinked[modSlot-VMT_RENDERABLE])
{
type = m_varModLinked[modSlot-VMT_RENDERABLE]->m_VariationFragCseType;
}
}
return type;
}
strLocalIndex CVehicleStreamRenderGfx::GetFragIndex(u8 modSlot) const
{
if(!Verifyf(modSlot < VMT_RENDERABLE + MAX_LINKED_MODS, "Invalid modSlot (%u)", modSlot))
{
return strLocalIndex(-1);
}
s32 fragidx =-1;
if(modSlot < VMT_RENDERABLE)
{
if(m_varModVisible[modSlot])
{
fragidx = m_varModVisible[modSlot]->m_VariationFragIdx.Get();
}
}
else
{
if(m_varModLinked[modSlot-VMT_RENDERABLE])
{
fragidx = m_varModLinked[modSlot-VMT_RENDERABLE]->m_VariationFragIdx.Get();
}
}
return strLocalIndex(fragidx);
}
rmcDrawable* CVehicleStreamRenderGfx::GetDrawable(eVehicleModType modSlot) const
{
Assert(modSlot == VMT_WHEELS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS);
CVehicleWheel *wheel = m_varWheel[modSlot-VMT_WHEELS];
if(wheel)
{
strLocalIndex drawableIdx = m_usingWheelVariation[modSlot-VMT_WHEELS] ? strLocalIndex(wheel->m_VariationDrawableVarIdx) : strLocalIndex(wheel->m_VariationDrawableIdx);
if (drawableIdx.IsValid())
{
return g_DrawableStore.Get(drawableIdx);
}
}
return NULL;
}
CCustomShaderEffectVehicleType* CVehicleStreamRenderGfx::GetDrawableCSEType(eVehicleModType modSlot) const
{
Assert(modSlot == VMT_WHEELS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS);
CVehicleWheel *wheel = m_varWheel[modSlot-VMT_WHEELS];
if(wheel)
{
return m_usingWheelVariation[modSlot-VMT_WHEELS] ? wheel->m_VariationDrawableVarCseType : wheel->m_VariationDrawableCseType;
}
return NULL;
}
strLocalIndex CVehicleStreamRenderGfx::GetDrawableIndex(eVehicleModType modSlot) const
{
Assert(modSlot == VMT_WHEELS || modSlot == VMT_WHEELS_REAR_OR_HYDRAULICS);
CVehicleWheel *wheel = m_varWheel[modSlot-VMT_WHEELS];
if (wheel)
{
return m_usingWheelVariation[modSlot-VMT_WHEELS] ? strLocalIndex(wheel->m_VariationDrawableVarIdx) : strLocalIndex(wheel->m_VariationDrawableIdx);
}
return strLocalIndex(-1);
}
static float sfMinTyreThickness = 0.05f;
// overrides the rim radius on each wheel with the custom one from our mod wheel
void CVehicleStreamRenderGfx::OverrideWheelRimRadius(CVehicle* targetEntity) const
{
CVehicleStructure* vehStruct = targetEntity->GetVehicleModelInfo()->GetStructure();
CVehicleModelInfo* vmi = targetEntity->GetVehicleModelInfo();
const CVehicleVariationInstance& var = targetEntity->GetVariationInstance();
eVehicleWheelType wheelType = var.GetWheelType(targetEntity);
float frontRimRadius = 0.f;
float rearRimRadius = 0.f;
bool overrideFrontRadius = false;
bool overrideRearRadius = false;
if (var.HasCustomWheels())
{
u32 modEntry = var.GetMods()[VMT_WHEELS];
const CVehicleWheel& modWheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][modEntry];
frontRimRadius = modWheel.GetRimRadius() * m_wheelTyreRadiusScale[0];
frontRimRadius = Min(frontRimRadius, vmi->GetTyreRadius(true) - sfMinTyreThickness);
overrideFrontRadius = true;
// infernus2 has custom scale on rear wheel:
if(targetEntity->GetModelNameHash() == MID_INFERNUS2)
{
rearRimRadius = modWheel.GetRimRadius() * m_wheelTyreRadiusScale[1];
overrideRearRadius = true;
}
else
{
rearRimRadius = frontRimRadius * vmi->GetTyreRadius(false) / vmi->GetTyreRadius(true);
}
rearRimRadius = Min(rearRimRadius, vmi->GetTyreRadius(false) - sfMinTyreThickness);
}
// override calculated rear wheel radius with a specific one, if we have custom rear wheels
if (var.HasCustomRearWheels())
{
u32 modEntry = var.GetMods()[VMT_WHEELS_REAR_OR_HYDRAULICS];
const CVehicleWheel& modWheel = CVehicleModelInfo::GetVehicleColours()->m_Wheels[wheelType][modEntry];
rearRimRadius = modWheel.GetRimRadius();
rearRimRadius *= m_wheelTyreRadiusScale[1]; // scale to vehicle space
rearRimRadius = Min(rearRimRadius, vmi->GetTyreRadius(false) - sfMinTyreThickness);
overrideRearRadius = true;
}
// override the rim radius for each wheel
s32 numWheels = targetEntity->GetNumWheels();
for (s32 i = 0; i < numWheels; ++i)
{
CWheel* wheel = targetEntity->GetWheel(i);
if (wheel)
{
if (wheel->GetConfigFlags().IsFlagSet(WCF_REARWHEEL))
{
if ( ( !var.CanHaveRearWheels() && overrideFrontRadius ) || overrideRearRadius)
wheel->SetWheelRimRadius(rearRimRadius);
else
rearRimRadius = wheel->GetWheelRimRadius();
}
else
{
if (overrideFrontRadius)
wheel->SetWheelRimRadius(frontRimRadius);
else
frontRimRadius = wheel->GetWheelRimRadius();
}
}
}
// override the rim radius for each wheel instance too
for (s32 i = 0; i < NUM_VEH_CWHEELS_MAX; ++i)
{
const bool isFrontWheel = CVehicleModelInfo::ms_aSetupWheelIds[i] == VEH_WHEEL_LF || CVehicleModelInfo::ms_aSetupWheelIds[i] == VEH_WHEEL_RF;
vehStruct->m_fWheelRimRadius[i] = isFrontWheel ? frontRimRadius : rearRimRadius;
}
}
// restores the wheel rim radius to the one initially stored in the model info (done when mod wheel is removed and replaced with original one)
void CVehicleStreamRenderGfx::RestoreWheelRimRadius(CVehicle* targetEntity) const
{
CVehicleStructure* vehStruct = targetEntity->GetVehicleModelInfo()->GetStructure();
CVehicleModelInfo* vmi = targetEntity->GetVehicleModelInfo();
s32 numWheels = targetEntity->GetNumWheels();
for (s32 i = 0; i < numWheels; ++i)
{
CWheel* wheel = targetEntity->GetWheel(i);
wheel->SetWheelRimRadius(wheel->GetConfigFlags().IsFlagSet(WCF_REARWHEEL) ? vmi->GetRimRadius(false) : vmi->GetRimRadius(true));
}
for (s32 i = 0; i < NUM_VEH_CWHEELS_MAX; ++i)
{
const bool isFrontWheel = CVehicleModelInfo::ms_aSetupWheelIds[i] == VEH_WHEEL_LF || CVehicleModelInfo::ms_aSetupWheelIds[i] == VEH_WHEEL_RF;
vehStruct->m_fWheelRimRadius[i] = isFrontWheel ? vmi->GetRimRadius(true) : vmi->GetRimRadius(false);
}
}
void CVehicleStreamRenderGfx::StoreModMatrices(CVehicle* pVeh)
{
CVehicleVariationInstance& var = pVeh->GetVariationInstance();
for (u8 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
if (var.HasModMatrixBeenStoredThisFrame(i))
continue;
s32 bone = -1;
if (i < VMT_RENDERABLE)
{
if (var.GetMods()[i] == INVALID_MOD)
continue;
bone = var.GetBone(i);
}
else
{
u8 linkSlot = i - VMT_RENDERABLE;
if (linkSlot < MAX_LINKED_MODS)
{
bone = var.GetBoneForLinkedMod(linkSlot);
}
}
if (bone == -1)
continue;
int boneIdx = pVeh->GetBoneIndex((eHierarchyId)bone);
if (Verifyf(boneIdx != -1, "Invalid bone index in vehicle variation instance on slot %i, bone %d (%s)", i, bone, pVeh->GetModelName()))
{
if (Verifyf(pVeh->GetSkeleton(), "Vehicle instance %p of type %s has no skeleton!", pVeh, pVeh->GetModelName()))
{
Matrix34 boneMat;
Matrix33 rotMat;
pVeh->GetSkeleton()->GetGlobalMtx(boneIdx, RC_MAT34V(boneMat));
rotMat = boneMat;
SetMatrix(i, rotMat);
var.SetModMatrixStoredThisFrame(i);
}
}
}
}
#if __DEV
void CVehicleStreamRenderGfx::PoolFullCallback(void* pItem)
{
if (!pItem)
{
Printf("ERROR - CVehicleStreamRenderGfx Pool FULL!\n");
}
else
{
CVehicleStreamRenderGfx* pVehRenderGfx = static_cast<CVehicleStreamRenderGfx*>(pItem);
Displayf("CVehicleStreamRenderGfx* : %p", pVehRenderGfx);
CVehicle* pVehicle = pVehRenderGfx->m_targetEntity;
if (pVehicle){
CVehicle::PoolFullCallback(pVehicle);
}
Displayf("---");
}
}
#endif