Files
GTASource/game/Peds/PedFactory.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1617 lines
56 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
// FILE : PedFactory.cpp
// PURPOSE : The factory class for peds and dummy peds.
// AUTHOR : Obbe.
// Adam Croston.
// CREATED :
/////////////////////////////////////////////////////////////////////////////////
#include "Peds\pedfactory.h"
// rage includes
#include "debug/Debug.h"
#include "vector/matrix34.h"
// game includes
#include "animation/MovePed.h"
#include "camera/CamInterface.h"
#include "camera/gameplay/GameplayDirector.h"
#include "camera/helpers/Frame.h"
#include "camera/viewports/ViewportManager.h"
#include "frontend/MiniMap.h"
#include "game/ModelIndices.h"
#include "fwanimation\directorcomponentragdoll.h"
#include "ModelInfo\pedmodelinfo.h"
#include "network/live/livemanager.h"
#include "network/Network.h"
#include "network/NetworkInterface.h"
#include "Network/Objects/NetworkObjectPopulationMgr.h"
#include "network/players/NetGamePlayer.h"
#include "peds/PedHelmetComponent.h"
#include "Peds\PedIntelligence.h"
#include "Peds\pedtype.h"
#include "Peds/PedMoveBlend/PedMoveBlendOnFoot.h"
#include "Peds/PedMoveBlend/PedMoveBlendInWater.h"
#include "Peds/PedMoveBlend/PedMoveBlendOnSkis.h"
#include "peds/Ped.h"
#include "peds/PedCloth.h"
#include "peds/pedpopulation.h"
#include "Peds\PedWeapons\PedTargeting.h"
#include "Peds/rendering/PedProps.h"
#include "peds/rendering/PedVariationDS.h"
#include "peds/rendering/PedVariationStream.h"
#include "peds/rendering/PedVariationPack.h"
#include "Peds\QueriableInterface.h"
#include "Peds/pedpopulation.h" // For CPedPopulation.
#include "physics/physics.h"
#include "pickups\PickupManager.h"
#include "Task\Default\TaskWander.h"
#include "Task\General\TaskBasic.h"
#include "scene/world/GameWorld.h"
#include "script/handlers/GameScriptEntity.h"
#include "script/script.h"
#include "shaders/CustomShaderEffectPed.h"
#include "streaming/populationstreaming.h"
#include "streaming/streaming.h"
#include "system/memmanager.h"
#include "vfx/misc/Fire.h"
#include "vfx/particles/PtFxManager.h"
#include "weapons/inventory/PedInventoryLoadOut.h"
#include "weapons/Weapon.h"
#include "Control/replay/ReplaySettings.h"
#if GTA_REPLAY
#include "Control/replay/replay.h"
#endif
AI_OPTIMISATIONS()
#define LARGEST_MOVEBLENDER MAX(sizeof(CPedMoveBlendInWater), MAX(sizeof(CPedMoveBlendOnFoot), sizeof(CPedMoveBlendOnSkis)))
// The size of these pools will now be set through the InitPool() calls, when the value
// of MAXNOOFPEDS has been determined.
#if COMMERCE_CONTAINER
FW_INSTANTIATE_CLASS_POOL_NO_FLEX_SPILLOVER(CPed, CONFIGURED_FROM_FILE, 0.26f, atHashString("Peds",0x8da12117));
FW_INSTANTIATE_CLASS_POOL_NO_FLEX_SPILLOVER(CPedIntelligence, CONFIGURED_FROM_FILE, 0.26f, atHashString("PedIntelligence",0x394ac584));
#else
FW_INSTANTIATE_CLASS_POOL_SPILLOVER(CPed, CONFIGURED_FROM_FILE, 0.26f, atHashString("Peds",0x8da12117));
FW_INSTANTIATE_CLASS_POOL_SPILLOVER(CPedIntelligence, CONFIGURED_FROM_FILE, 0.26f, atHashString("PedIntelligence",0x394ac584));
#endif
PARAM(temppeddebug, "prints some temporary ped debug output");
dev_float COP_SHOT_RATE = 0.3f;
CPedFactory* CPedFactory::ms_pInstance = NULL;
#if __DEV
bool CPedFactory::ms_bWithinPedFactory = false;
bool CPedFactory::ms_bInDestroyPedFunction = false;
bool CPedFactory::ms_bInDestroyPlayerPedFunction = false;
#endif // __DEV
CPedFactory::sDestroyedPed CPedFactory::ms_reuseDestroyedPedArray[MAX_DESTROYED_PEDS_CACHED];
bool CPedFactory::ms_reuseDestroyedPeds = true; // when destroying peds we cache them for a while and reuse them when the same type is spawned again
u32 CPedFactory::ms_reuseDestroyedPedCacheTime = 10000; // time in ms how long we should keep cached peds around
u32 CPedFactory::ms_reuseDestroyedPedCount = 0; // keep track of how many peds we have cached
#if __BANK
bool CPedFactory::ms_reuseDestroyedPedsDebugOutput = false;
RegdPed CPedFactory::ms_pLastCreatedPed;
bool CPedFactory::ms_bLogCreatedPeds = false;
bool CPedFactory::ms_bLogDestroyedPeds = false;
u32 CPedFactory::ms_iCreatedPedCount = 0;
u32 CPedFactory::ms_iDestroyedPedCount = 0;
#endif // __BANK
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CPedFactory
// PURPOSE : Constructor. It also initializes the various pools used by objects
// created within the factory.
// PARAMETERS : None
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
CPedFactory::CPedFactory() :
m_pLocalPlayer(NULL)
{
// This used to be a CompileTimeAssert() before, when MAXNOOFPEDS was known
// at compile time.
Assert(MAX_NUM_NETOBJPEDS <= MAXNOOFPEDS);
CPed::InitPool( MAXNOOFPEDS, MEMBUCKET_GAMEPLAY );
#if __DEV
CPed::GetPool()->RegisterPoolCallback(CPed::PoolFullCallback);
#endif // __DEV
CPedTargetting::InitPool( MEMBUCKET_GAMEPLAY );
#if (__XENON || __PS3)
#define TASKINFO_HEAP_SIZE (40 << 10)
#else
// see MAX_NUM_TASK_INFOS in QueriableInterface.h
// 268 peds * 8 task infos per ped * average TaskInfo size of 90 bytes (overestimate) = 192 kB
#define TASKINFO_HEAP_SIZE ((40 << 10) * 10)
#endif
sysMemStartFlex();
CTaskInfo::InitPool(TASKINFO_HEAP_SIZE, MAX_NUM_TASK_INFOS, MEMBUCKET_GAMEPLAY);
sysMemEndFlex();
#if !__NO_OUTPUT && !__FINAL
CTaskInfo::GetPool()->RegisterPoolCallback(CTaskInfo::PoolFullCallback);
#endif
CChatHelper::InitPool( MEMBUCKET_GAMEPLAY );
// CChatHelper::GetPool()->SetCanDealWithNoMemory(true); // Existing code already checks number of slots free before calling NEW
TaskSequenceInfo::InitPool( MEMBUCKET_GAMEPLAY );
#if __BANK
TaskSequenceInfo::GetPool()->RegisterPoolCallback(TaskSequenceInfo::PoolFullCallback);
#endif
// CPedTargetting::GetPool()->SetCanDealWithNoMemory(true);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ~CPedFactory
// PURPOSE : Destructor. It also shuts down the various pools used by objects
// created within the factory.
// PARAMETERS : None
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
CPedFactory::~CPedFactory()
{
CPed::ShutdownPool();
CPedTargetting::ShutdownPool();
CTaskInfo::ShutdownPool();
CChatHelper::ShutdownPool();
TaskSequenceInfo::ShutdownPool();
ClearDestroyedPedCache();
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CreateFactory
// PURPOSE : Create the entire project specific factory.
// PARAMETERS : None
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::CreateFactory()
{
Assert(ms_pInstance == NULL);
ms_pInstance = rage_new CPedFactory;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : DestroyFactory
// PURPOSE : Default destroy entire factory. Individual projects
// can override this.
// PARAMETERS : None
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::DestroyFactory()
{
Assert(ms_pInstance);
delete ms_pInstance;
ms_pInstance = NULL;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CreatePed
// PURPOSE : This function allow us to create a project specific ped.
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// bShouldBeCloned - if set to true and a network game is running,
// this ped will be registered with the network object manager
// so that it will cloned and synchronized on all the remote
// machines involved in the network game.
// bCreatedByScript -
// bFailSilentlyIfOutOfPeds - if true, suppress asserts if we don't have space in the pool or in the cache,
// for use as a replacement to code that previously checked GetNoOfFreeSpaces().
// RETURNS : The new ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::CreatePed(const CControlledByInfo& pedControlInfo, fwModelId modelId, const Matrix34 *pMat, bool bApplyDefaultVariation, bool bShouldBeCloned, bool bCreatedByScript,
bool bFailSilentlyIfOutOfPeds, bool bScenarioPedCreatedByConcealeadPlayer)
{
if(pedControlInfo.IsControlledByLocalOrNetworkPlayer())
{
Assert(0); // must use CreatePlayer() instead
}
if (NetworkInterface::IsGameInProgress() && bShouldBeCloned && !bCreatedByScript)
{
NetworkObjectType objectType = static_cast<NetworkObjectType>(pedControlInfo.IsControlledByLocalOrNetworkPlayer() ? NET_OBJ_TYPE_PLAYER : NET_OBJ_TYPE_PED);
if(!CNetworkObjectPopulationMgr::CanRegisterLocalObjectOfType(objectType, false))
{
return NULL;
}
}
const CControlledByInfo localAiControl(false, false);
#if 0
CPed* pPed = bCreatedByScript ? NULL : CreatePedFromDestroyedCache(localAiControl, modelId, pMat, bShouldBeCloned);
#else
CPed* pPed = NULL;
#endif
if(!Verifyf(FRAGCACHEMGR->CanGetNewCacheEntries(1),"Unable to create ped because we have no available cache entries."))
{
return NULL;
}
#if !__FINAL
if (!gbAllowPedGeneration)
{
return pPed;
}
#endif //!__FINAL
#if (__ASSERT && GTA_REPLAY)
if( !CReplayMgr::IsReplayInControlOfWorld() )
#endif //(__ASSERT && GTA_REPLAY)
{
Assertf(!pMat || !IsCloseAll(RCC_VEC3V(pMat->d), Vec3V(V_ZERO), Vec3V(V_FLT_SMALL_1)),
"Ped is being created very close to the origin: (%.f, %f, %f); bCreatedByScript = %s",
pMat->d.x, pMat->d.y, pMat->d.z, bCreatedByScript ? "true":"false");
}
CPedModelInfo* pModelInfo = ((CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId));
Assert(pModelInfo);
if (!pPed)
{
ePedType pedType = pModelInfo->GetDefaultPedType();
switch(pedType)
{
case PEDTYPE_COP:
pPed=SetUpAsCopPed(localAiControl, modelId.GetModelIndex(), pMat, bCreatedByScript, bFailSilentlyIfOutOfPeds);
break;
default:
pPed=SetUpAsCivilianPed(localAiControl, modelId.GetModelIndex(), pMat, bCreatedByScript, bFailSilentlyIfOutOfPeds);
break;
}
if (!pPed)
{
return NULL;
}
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory, true );
if (bScenarioPedCreatedByConcealeadPlayer)
{
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_CreatedByConcealedPlayer, true);
}
// ******* NETWORK REGISTRATION HAS TO BE DONE BEFORE ANY FURTHER STATE CHANGES ON THE PED! *******************
if (bShouldBeCloned && NetworkInterface::IsGameInProgress())
{
bool bRegistered = NetworkInterface::RegisterObject(pPed, 0, bCreatedByScript ? CNetObjGame::GLOBALFLAG_SCRIPTOBJECT : 0);
// ped could not be registered as a network object (due to maximum number of ped objects being exceeded
// scripted peds are queued to be created
if (!bRegistered && !bCreatedByScript)
{
DestroyPedInternal(pPed);
return NULL;
}
}
}
pPed->m_nPhysicalFlags.bNotToBeNetworked = !bShouldBeCloned;
#if __BANK
if(pPed->m_nPhysicalFlags.bNotToBeNetworked && NetworkInterface::IsGameInProgress() && !pedControlInfo.IsControlledByNetwork())
{
if ((Channel_ai.FileLevel >= DIAG_SEVERITY_DEBUG2) || (Channel_ai.TtyLevel >= DIAG_SEVERITY_DEBUG2))
{
aiDebugf2("Creating a none networked ped in a network game! Ped = %p", pPed);
sysStack::PrintStackTrace();
}
}
#endif // __BANK
if (bApplyDefaultVariation)
VariationInitialisation(pPed);
if (bCreatedByScript)
CPedInventoryLoadOutManager::SetDefaultLoadOut(pPed);
pPed->SetStubbleGrowth(fwRandom::GetRandomNumberInRange(0.f, pModelInfo->GetStubble()));
BANK_ONLY(ms_pLastCreatedPed = pPed);
pPed->GetLodData().SetResetDisabled(true);
pPed->SetAlpha(LODTYPES_ALPHA_VISIBLE);
#if __BANK
if(pMat)
{
pPed->m_vecInitialSpawnPosition = VECTOR3_TO_VEC3V(pMat->GetVector(3));
}
#endif // __BANK
#if __BANK
LogCreatedPed(pPed, "CPedFactory::CreatePed()", 0, Color_green);
#endif // __BANK
REPLAY_ONLY(CReplayMgr::OnCreateEntity(pPed));
return pPed;
}
#if __BANK
void CPedFactory::LogCreatedPed(CPed * pPed, const char * pCallerDesc, s32 iTextOffsetY, Color32 iTextCol)
{
if(ms_bLogCreatedPeds && pPed)
{
CViewport* gameViewport = gVpMan.GetGameViewport();
if(gameViewport)
{
static const int sizeoftext = 128;
char text[sizeoftext];
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
const Vector3 vCameraPos = VEC3V_TO_VECTOR3(gameViewport->GetGrcViewport().GetCameraPosition());
const float fDist = (vCameraPos - vPedPos).Mag();
formatf(text, sizeoftext, "%i : %s", ms_iCreatedPedCount, pPed->GetModelName());
grcDebugDraw::Text(vPedPos, iTextCol, 0, -grcDebugDraw::GetScreenSpaceTextHeight() + iTextOffsetY, text, false, (s32)(10.0f / fwTimer::GetTimeStep()));
grcDebugDraw::Sphere(vPedPos, 0.25f, Color_green4, false, (s32)(10.0f / fwTimer::GetTimeStep()));
if (NetworkInterface::IsGameInProgress() && pPed->GetNetworkObject())
{
formatf(text, sizeoftext, " NETWORKNAME: %s,",pPed->GetNetworkObject()->GetLogName());
}
else
{
formatf(text, sizeoftext, " DEBUGNAME: %s,",pPed->GetDebugNameFromObjectID());
}
bool bOnScreen = CPedPopulation::IsCandidateInViewFrustum(vPedPos, 1.0f, CPedPopulation::SP_SpawnValidityCheck_GetCurrentParams().fAddRangeInViewMax, CPedPopulation::SP_SpawnValidityCheck_GetCurrentParams().fAddRangeInViewMin);
Displayf("**************************************************************************************************");
Displayf("%s : %i", pCallerDesc, ms_iCreatedPedCount);
Displayf("FRAME: %d, NAME : %s,%s POPTYPE: %s, PEDTYPE: %s, DISTANCE : %.1f, POSITION : %.1f, %.1f, %.1f, CAM POS: %.1f, %.1f, %.1f, PED : 0x%p, bAtStartup : %s, bInstantFill : %s, bOnScreen: %s, bIsClone: %s", fwTimer::GetFrameCount(), pPed->GetModelName(), text, CTheScripts::GetPopTypeName(pPed->PopTypeGet()), CPedType::GetPedTypeNameFromId(pPed->GetPedType()), fDist, vPedPos.x, vPedPos.y, vPedPos.z, vCameraPos.x, vCameraPos.y, vCameraPos.z, pPed, CPedPopulation::ShouldUseStartupMode() ? "true":"false", CPedPopulation::GetInstantFillPopulation() ? "true":"false", bOnScreen ? "true":"false", pPed->IsNetworkClone() ? "true":"false");
if (scrThread::GetActiveThread())
scrThread::GetActiveThread()->PrintStackTrace();
sysStack::PrintStackTrace();
Displayf("**************************************************************************************************");
ms_iCreatedPedCount++;
}
}
}
#endif
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CreatePedFromSource
// PURPOSE : Creates a new ped with the variation data from the specified source ped
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// pSourcePed - ped to copy variation data from
// bStallForAssetLoad - if true the streamed assets will be loaded synchronously and
// ped will be ready for render when this function returns
// bShouldBeCloned - if set to true and a network game is running,
// this ped will be registered with the network object manager
// so that it will cloned and synchronized on all the remote
// machines involved in the network game.
// bCreatedByScript -
// RETURNS : The new ped, which might not be ready for rendering.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::CreatePedFromSource(const CControlledByInfo& pedControlInfo, fwModelId modelId, const Matrix34 *pMat, const CPed* pSourcePed, bool bStallForAssetLoad, bool bShouldBeCloned, bool bCreatedByScript)
{
// ensure that the model is loaded
if (!CModelInfo::HaveAssetsLoaded(modelId))
{
CModelInfo::RequestAssets(modelId, STRFLAG_FORCE_LOAD | STRFLAG_PRIORITY_LOAD);
CStreaming::LoadAllRequestedObjects(true);
}
if (!CModelInfo::HaveAssetsLoaded(modelId))
{
Assertf(false, "Ped model %d hasn't loaded successfully, can't create ped!", modelId.GetModelIndex());
return NULL;
}
CPed* pPed = CreatePed(pedControlInfo, modelId, pMat, (pSourcePed == NULL), bShouldBeCloned, bCreatedByScript);
if (!pPed)
return NULL;
if (pSourcePed)
CopyVariation(pSourcePed, pPed, bStallForAssetLoad);
return pPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ClonePed
// PURPOSE : Creates a new ped by cloning the given one.
// PARAMETERS : source - ped instance to clone
// RETURNS : new ped instance cloned from the source
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::ClonePed(const CPed* source, bool bRegisterAsNetworkObject, bool bLinkBlends, bool bCloneCompressedDamage)
{
CPed* newPed = NULL;
if (source)
{
// Prevent streamed peds being cloned if we can't store their CPedStreamRenderGfx (pool full)
CPedModelInfo* pModelInfo = static_cast<CPedModelInfo*>(source->GetBaseModelInfo());
if( pModelInfo && pModelInfo->GetIsStreamedGfx() )
{
// If the number of requests is >= the space we have for them, we can't clone
if( CPedStreamRequestGfx::GetPool()->GetNoOfUsedSpaces() >= CPedStreamRenderGfx::GetPool()->GetNoOfFreeSpaces() )
{
return newPed; // Which is currently NULL!
}
}
Matrix34 tempMat;
tempMat.Identity();
tempMat.d = VEC3V_TO_VECTOR3(source->GetTransform().GetPosition());
const CControlledByInfo dummyControllInfo(false, false);
newPed = CPedFactory::GetFactory()->CreatePedFromSource(dummyControllInfo, source->GetModelId(), &tempMat, source, false, bRegisterAsNetworkObject, true);
if (Verifyf(newPed, "CLONE_PED - Couldn't create a new ped"))
{
newPed->SetLodDistance(CPed::ms_uMissionPedLodDistance);
newPed->ActivatePhysics();
newPed->m_nPhysicalFlags.bNotDamagedByAnything = source->m_nPhysicalFlags.bNotDamagedByAnything;
newPed->SetPedConfigFlag(CPED_CONFIG_FLAG_DisablePlayerLockon, source->GetPedConfigFlag(CPED_CONFIG_FLAG_DisablePlayerLockon));
newPed->SetHealth(source->GetHealth(), true);
newPed->SetEndurance(source->GetEndurance(), true);
newPed->SetArmour(source->GetArmour());
// We need to ensure the crew emblem is taken from the ped so it does not fall back to the local player
newPed->SetSavedClanId(source->GetSavedClanId());
newPed->GetPedDrawHandler().GetVarData().SetOverrideCrewLogoTxdHash(source->GetPedDrawHandler().GetVarData().GetOverrideCrewLogoTxdHash());
newPed->GetPedDrawHandler().GetVarData().SetOverrideCrewLogoTexHash(source->GetPedDrawHandler().GetVarData().GetOverrideCrewLogoTexHash());
newPed->CloneHeadBlend(source, bLinkBlends);
CGameWorld::Add(newPed, source->GetInteriorLocation());
newPed->GetPortalTracker()->RequestRescanNextUpdate();
newPed->GetPortalTracker()->Update(VEC3V_TO_VECTOR3(newPed->GetTransform().GetPosition()));
newPed->CloneDamage(source, bCloneCompressedDamage);
}
}
return newPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ClonePedToTarget
// PURPOSE : Clones settings from source to target ped
// PARAMETERS : source - ped instance to clone from
// target - ped instance to clone to
// RETURNS :
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::ClonePedToTarget(const CPed* source, CPed* target, bool bCloneCompressedDamage)
{
if (source && target)
{
// delete old head blend data so new variation doesn't trigger a reblend
CPedHeadBlendData* oldBlendData = target->GetExtensionList().GetExtension<CPedHeadBlendData>();
if (oldBlendData)
target->GetExtensionList().Destroy(CPedHeadBlendData::GetAutoId());
target->CloneHeadBlend(source, false);
CopyVariation(source, target, false);
target->ReleaseDamageSet();
target->CloneDamage(source, bCloneCompressedDamage);
// We need to ensure the crew emblem is taken from the ped so it does not fall back to the local player
target->SetSavedClanId(source->GetSavedClanId());
target->GetPedDrawHandler().GetVarData().SetOverrideCrewLogoTxdHash(source->GetPedDrawHandler().GetVarData().GetOverrideCrewLogoTxdHash());
target->GetPedDrawHandler().GetVarData().SetOverrideCrewLogoTexHash(source->GetPedDrawHandler().GetVarData().GetOverrideCrewLogoTexHash());
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : VariationInitialisation
// PURPOSE : Initialse the variation data for this ped (handle packed or streamed peds as required
// PARAMETERS : pPed - ped with variation data to be initialised
// RETURNS : N/A
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::VariationInitialisation(CPed* pPed)
{
pPed->SetVarDefault();
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : DestroyPedInternal
// PURPOSE : Default factory ped destroy function. Individual projects can
// override this.
// PARAMETERS : pPed - the ped to destroy
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
bool CPedFactory::DestroyPedInternal(CPed* pPed, bool cached)
{
Assert( pPed );
if( pPed->m_ClothCollision )
{
pPed->m_ClothCollision->Shutdown();
pPed->m_ClothCollision = NULL;
}
CPedPopulation::IncrementNumPedsDestroyedThisCycle();
CPedPopulation::RemovePedFromPopulationCount(pPed);
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_PedBeingDeleted, true );
CTheScripts::UnregisterEntity(pPed, cached); // if cached clear the script extension as well
#if 0 // CS
// drop any mission objects the ped is carrying (avoids assertions in the ped weapon manager destructor)
pPed->GetInventory().DropAllMissionObjects();
#endif // 0
// remove the ped from a ped group he may be in
CPedGroup* pPedGroup = pPed->GetPedsGroup();
if (pPedGroup && pPedGroup->IsLocallyControlled())
{
pPedGroup->GetGroupMembership()->RemoveMember(pPed);
}
Assert(pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory ));
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory, false );
CGameWorld::Remove(pPed);
#if 0
// if cached delete requested add the ped to the cache and don't free the memory, but ignore script peds
if (cached && !pPed->GetExtension<CScriptEntityExtension>())
{
return AddDestroyedPedToCache(pPed);
}
#endif
// We should be removed from the world now. If something re-inserts us, that can be a potentially fatal problem.
pedAssert(!CGameWorld::GetPedBeingDeleted());
CGameWorld::SetPedBeingDeleted(pPed);
// If this ped is holding a prop, we want to destroy it as well, so we don't have props hanging around for cutscenes or whathaveyou. [5/21/2013 mdawe]
CPropManagementHelper* pPropHelper = pPed->GetPedIntelligence()->GetActivePropManagementHelper();
if (pPropHelper)
{
pPropHelper->SetDestroyProp(true);
}
// make sure all tasks are aborted so they can clean up properly
pPed->GetPedIntelligence()->FlushImmediately(false, true, true, false);
pedAssert(CGameWorld::GetPedBeingDeleted() == pPed);
CGameWorld::SetPedBeingDeleted(NULL);
pedAssertf(!pPed->GetOwnerEntityContainer(), "Ped 0x%p (%s) should have been removed from the scene graph, did FlushImmediately() just reinsert it?", pPed, pPed->GetModelName());
#if __BANK
LogDestroyedPed(pPed, "CPedFactory::DestroyPedInternal()", 0, Color_yellow);
#endif // __BANK
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete pPed;
DEV_ONLY(ms_bInDestroyPedFunction = false);
return true;
}
#if __BANK
void CPedFactory::LogDestroyedPed(CPed * pPed, const char * pCallerDesc, s32 iTextOffsetY, Color32 iTextCol)
{
if(ms_bLogDestroyedPeds && pPed)
{
CViewport* gameViewport = gVpMan.GetGameViewport();
if(gameViewport)
{
static const int sizeoftext = 128;
char text[sizeoftext];
const Vector3 vPedPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
const Vector3 vCameraPos = VEC3V_TO_VECTOR3(gameViewport->GetGrcViewport().GetCameraPosition());
const float fDist = (vCameraPos - vPedPos).Mag();
formatf(text, sizeoftext, "%i : %s", ms_iDestroyedPedCount, pPed->GetModelName());
grcDebugDraw::Text(vPedPos, iTextCol, 0, -grcDebugDraw::GetScreenSpaceTextHeight() + iTextOffsetY, text, false, (s32)(10.0f / fwTimer::GetTimeStep()));
grcDebugDraw::Sphere(vPedPos, 0.25f, Color_yellow4, false, (s32)(10.0f / fwTimer::GetTimeStep()));
if (NetworkInterface::IsGameInProgress() && pPed->GetNetworkObject())
{
formatf(text, sizeoftext, " NETWORKNAME: %s,",pPed->GetNetworkObject()->GetLogName());
}
else
{
formatf(text, sizeoftext, " DEBUGNAME: %s,",pPed->GetDebugNameFromObjectID());
}
Displayf("**************************************************************************************************");
Displayf("%s : %i", pCallerDesc, ms_iDestroyedPedCount);
Displayf("NAME : %s,%s POPTYPE: %s, PEDTYPE: %s, DISTANCE : %.1f, POSITION : %.1f, %.1f, %.1f, PED : 0x%p", pPed->GetModelName(), text, CTheScripts::GetPopTypeName(pPed->PopTypeGet()), CPedType::GetPedTypeNameFromId(pPed->GetPedType()), fDist, vPedPos.x, vPedPos.y, vPedPos.z, pPed);
if (scrThread::GetActiveThread())
scrThread::GetActiveThread()->PrintStackTrace();
sysStack::PrintStackTrace();
Displayf("**************************************************************************************************");
ms_iDestroyedPedCount++;
}
}
}
#endif
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CopyVariation
// PURPOSE : Default factory ped destroy function. Individual projects can
// override this.
// PARAMETERS : source - source ped
// dest - destination ped
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::CopyVariation(const CPed* source, CPed* dest, bool stallForAssets)
{
if(source && dest)
{
int flags = 0;
if (stallForAssets)
flags = STRFLAG_FORCE_LOAD | STRFLAG_PRIORITY_LOAD;
for(int iComponent=0; iComponent < PV_MAX_COMP; iComponent++)
{
u32 iDrawableId =0;
u8 TextureVarId =0;
u8 Palette = 0;
iDrawableId = CPedVariationPack::GetCompVar(source, static_cast<ePedVarComp>(iComponent));
TextureVarId = CPedVariationPack::GetTexVar(source, static_cast<ePedVarComp>(iComponent));
// want to pick up current palette ID from the current debug ped (for this drawable)
Palette = CPedVariationPack::GetPaletteVar(source, static_cast<ePedVarComp>(iComponent));
//if (dest->IsVariationValid(static_cast<ePedVarComp>(iComponent),iDrawableId,TextureVarId, varIndex))
{
dest->SetVariation(static_cast<ePedVarComp>(iComponent), iDrawableId, 0, TextureVarId, Palette, flags, false);
}
}
}
CPedPropsMgr::SetPedPropsPacked(dest, CPedPropsMgr::GetPedPropsPacked(source, true));
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CreatePlayerPed
// PURPOSE : Create a player ped. This function allow us to create a project
// specific player.
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// infoIndex - addition player specific info.
// pMat - the placement and orientation matrix to use for the ped.
// bShouldBeCloned - if set to true and a network game is running,
// this ped will be registered with the network object manager
// so that it will cloned and synchronized on all the remote
// machines involved in the network game.
// RETURNS : The new player ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::CreatePlayerPed(const CControlledByInfo& pedControlInfo, u32 modelIndex, const Matrix34 *pMat, CPlayerInfo* pPlayerInfo )
{
CPed* pPlayerPed=0;
bool bRegisterAsNetworkObject = !pPlayerInfo; // if a player info is being passed in, a network object will already exist for this player
Assert(!pedControlInfo.IsControlledByLocalAi());// Must use different setup as not actually requesting a player.
if (!pPlayerInfo)
{
// get the player info from our network player if available, or create a new one
if (CNetwork::IsNetworkOpen())
{
pPlayerInfo = NetworkInterface::GetLocalPlayer()->GetPlayerInfo();
gnetDebug2("CPedFactory::CreatePlayerPed - Creating new player info (%p). Grabbing from local player", pPlayerInfo);
}
else
{
pPlayerInfo = rage_new CPlayerInfo(NULL, NetworkInterface::GetActiveGamerInfo());
gnetDebug2("CPedFactory::CreatePlayerPed Creating new player info (%p). Name = %s", pPlayerInfo, NetworkInterface::GetActiveGamerInfo() ? NetworkInterface::GetActiveGamerInfo()->GetName() : "");
}
sysStack::PrintStackTrace();
Assert(pPlayerInfo);
}
#if __DEV
ms_bWithinPedFactory = true;
#endif
pPlayerPed=SetUpAsPlayerPed(pedControlInfo, modelIndex, pMat, *pPlayerInfo);
if(pPlayerPed)
{
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory, true );
}
if(pedControlInfo.IsControlledByLocalPlayer())
{
#if __ASSERT
// it's only OK to overwrite an existing local player if we are changing the player model
if(m_pLocalPlayer)
{
Assertf(CPlayerInfo::IsChangingPlayerModel(), "Overwriting existing local player ped!");
}
#endif // __ASSERT
if(m_pLocalPlayer && m_pLocalPlayer != pPlayerPed)
{
m_pLocalPlayer->UpdateFirstPersonFlags(false);
}
m_pLocalPlayer = pPlayerPed;
if (bRegisterAsNetworkObject && NetworkInterface::IsGameInProgress())
{
NetworkInterface::RegisterObject(pPlayerPed, 0, netObject::GLOBALFLAG_CLONEALWAYS|CNetObjGame::GLOBALFLAG_SCRIPTOBJECT);
}
}
#if __DEV
ms_bWithinPedFactory = false;
#endif
BANK_ONLY(ms_pLastCreatedPed = pPlayerPed);
REPLAY_ONLY(CReplayMgr::OnCreateEntity(pPlayerPed));
return pPlayerPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : DestroyPlayerPed
// PURPOSE : Default factory player ped destroy function. Individual projects
// can override this.
// PARAMETERS : pPlayerPed - the player ped to destroy
// RETURNS : Nothing
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::DestroyPlayerPed(CPed* pPlayerPed)
{
#if __DEV
ms_bWithinPedFactory = true;
ms_bInDestroyPlayerPedFunction = true;
ms_bInDestroyPedFunction = true;
#endif // __DEV
bool bDestroyPlayerInfo = true;
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_PedBeingDeleted, true );
if (pPlayerPed->GetSpecialAbility())
{
pPlayerPed->GetSpecialAbility()->Deactivate();
}
CPlayerInfo* pInfo = pPlayerPed->GetPlayerInfo();
// make sure all tasks are aborted so they can clean up properly
pPlayerPed->GetPedIntelligence()->FlushImmediately();
// inform network object manager about this ped being deleted
if (pPlayerPed->GetNetworkObject())
{
// the network player representing this player ped will delete the player info when it is removed
if (pInfo && pPlayerPed->GetNetworkObject()->GetPlayerOwner() && pPlayerPed->GetNetworkObject()->GetPlayerOwner()->IsValid())
{
Assert(pPlayerPed->GetNetworkObject()->GetPlayerOwner()->GetPlayerInfo() == pInfo);
bDestroyPlayerInfo = false;
}
Assert(!pPlayerPed->IsNetworkClone());
NetworkInterface::UnregisterObject(pPlayerPed);
}
Assert(!pPlayerPed->GetNetworkObject());
Assert(pPlayerPed->GetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory ));
pPlayerPed->SetPedConfigFlag( CPED_CONFIG_FLAG_CreatedByFactory, false );
if (pInfo)
{
pPlayerPed->SetPlayerInfo(NULL);
pInfo->SetPlayerPed(NULL);
}
if (m_pLocalPlayer == pPlayerPed)
{
m_pLocalPlayer = NULL;
}
CGameWorld::Remove(pPlayerPed);
delete pPlayerPed;
if (pInfo && bDestroyPlayerInfo)
{
gnetDebug2("CPedFactory::DestroyPlayerPed - Deleting player info (%p). Name: %s", pInfo, pInfo->m_GamerInfo.GetName());
#if __BANK
// ensure no other peds are using this player info
CPed::Pool *pedPool = CPed::GetPool();
for(int index = 0; index < pedPool->GetSize(); index++)
{
CPed *pPed = pedPool->GetSlot(index);
if(pPed && (pPed != pPlayerPed))
{
if(pPed->GetPlayerInfo() == pInfo)
{
sysStack::PrintStackTrace();
gnetAssertf(0, "CPedFactory::DestroyPlayerPed - Deleting a player info pointer (%p) when it is still referenced by %s", pInfo, pPed->GetDebugName());
gnetError("CPedFactory::DestroyPlayerPed - Deleting a player info pointer (%p) when it is still referenced by %s", pInfo, pPed->GetDebugName());
}
}
}
#endif
delete pInfo;
}
#if __DEV
ms_bWithinPedFactory = false;
ms_bInDestroyPlayerPedFunction = false;
ms_bInDestroyPedFunction = false;
#endif // __DEV
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetUpAsCivilianPed
// PURPOSE : Sets up project specific civilian peds.
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// bCreatedFromScript -
// bFailSilentlyIfOutOfPeds - if true, suppress asserts if we don't have space in the pool or in the cache,
// for use as a replacement to code that previously checked GetNoOfFreeSpaces().
// RETURNS : The newly set up ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::SetUpAsCivilianPed(const CControlledByInfo& pedControlInfo, const u32 modelIndex, const Matrix34 *pMat, const bool bCreatedFromScript, bool bFailSilentlyIfOutOfPeds) const
{
// Create the ped.
CPed* pPed = ConcreteCreatePed(pedControlInfo, modelIndex, bFailSilentlyIfOutOfPeds);
if (!pPed)
{
return NULL;
}
// Set up the on foot move blend, the model index, and the entity matrix.
SetPedModelMatAndMovement(pPed, modelIndex, pMat);
if(!bCreatedFromScript)
{
// Allow randomly created civilians to be agitated.
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_CanBeAgitated, true);
// Prevent randomly created civilians from using seats on the running-board of vehicles
if (!pPed->IsLawEnforcementPed())
{
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_PreventUsingLowerPrioritySeats, true);
}
}
// Return the new civilian ped.
return pPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetUpAsCopPed
// PURPOSE : Sets up project specific cop peds.
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// bCreatedFromScript -
// bFailSilentlyIfOutOfPeds - if true, suppress asserts if we don't have space in the pool or in the cache,
// for use as a replacement to code that previously checked GetNoOfFreeSpaces().
// RETURNS : The newly set up ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::SetUpAsCopPed(const CControlledByInfo& pedControlInfo, const u32 modelIndex, const Matrix34 *pMat, const bool bCreatedFromScript, bool bFailSilentlyIfOutOfPeds) const
{
// Create the ped.
CPed* pPed = ConcreteCreatePed(pedControlInfo, modelIndex, bFailSilentlyIfOutOfPeds);
if (!pPed)
{
return NULL;
}
// Set up the on foot move blend, the model index, and the entity matrix.
SetPedModelMatAndMovement(pPed, modelIndex, pMat);
//Allow randomly created cops to be agitated.
if(!bCreatedFromScript)
{
pPed->SetPedConfigFlag(CPED_CONFIG_FLAG_CanBeAgitated, true);
}
// Don't give cop weapons if created from script
if (!bCreatedFromScript)
{
static const atHashWithStringNotFinal COP_LOADOUT("LOADOUT_COP",0xB01B33EA);
CPedInventoryLoadOutManager::SetLoadOut(pPed, COP_LOADOUT.GetHash());
if(pPed->GetWeaponManager())
pPed->GetPedIntelligence()->GetCombatBehaviour().SetShootRateModifier(COP_SHOT_RATE);
pPed->m_nArmour = 0;
}
bool bCanBust = !bCreatedFromScript;
pPed->GetPedIntelligence()->GetCombatBehaviour().ChangeFlag(CCombatData::BF_CanBust, bCanBust);
pPed->GetPedIntelligence()->AddTaskDefault(pPed->ComputeWanderTask(*pPed));
//Set up police to communicate events with friends/other cops.
pPed->GetPedIntelligence()->SetInformRespectedFriends(CPedIntelligence::MAX_INFORM_FRIEND_DISTANCE_MAX_VALUE, CPedIntelligence::MAX_NUM_FRIENDS_TO_INFORM_MAX_VALUE);
// Return the new cop ped.
return pPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetUpAsPlayerPed
// PURPOSE : Sets up project specific player peds.
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// RETURNS : The newly set up player ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::SetUpAsPlayerPed(const CControlledByInfo& pedControlInfo, const u32 modelIndex, const Matrix34 *pMat, CPlayerInfo& playerInfo) const
{
Assert(!pedControlInfo.IsControlledByLocalAi());// Must use different setup as not actually requesting a player.
#if __DEV
const bool wasWithinPedFactory = ms_bWithinPedFactory;
#endif
// Create the player ped.
CPed* pPlayerPed = ConcreteCreatePed(pedControlInfo, modelIndex, false);
Assert(pPlayerPed);
if(!pPlayerPed)
{
return NULL;
}
#if __DEV
ms_bWithinPedFactory = wasWithinPedFactory;
#endif
pPlayerPed->SetPlayerInfo(&playerInfo);
playerInfo.SetPlayerPed(pPlayerPed);
// Create a group with the player being the leader.
if (!pPlayerPed->IsControlledByNetworkPlayer() &&
(!NetworkInterface::IsNetworkOpen() || pPlayerPed->GetNetworkObject())) // we can't assign a group to the player in MP until he has a network object and a physical player index
{
pPlayerPed->GetPlayerInfo()->SetupPlayerGroup();
}
// Set up the on foot move blend, the model index, and the entity matrix.
SetPedModelMatAndMovement(pPlayerPed, modelIndex, pMat);
pPlayerPed->GetPedIntelligence()->GetCombatBehaviour().SetFlag(CCombatData::BF_CanTauntInVehicle);
pPlayerPed->GetPedIntelligence()->GetCombatBehaviour().SetFiringPattern(FIRING_PATTERN_FULL_AUTO);
// Return the new player ped.
return pPlayerPed;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetPedMovementModelAndMat
// PURPOSE : Sets up the on foot move blend, the model index, and the entity
// matrix.
// PARAMETERS : pPed - the ped to set up.
// modelIndex - the model to use for this ped.
// pMat - the placement and orientation matrix to use for the ped.
// RETURNS : Nothing.
/////////////////////////////////////////////////////////////////////////////////
void CPedFactory::SetPedModelMatAndMovement(CPed* pPed, const u32 modelIndex, const Matrix34 * pMat) const
{
// Set the position and orientation.
if(pMat)
{
pPed->SetMatrix(*pMat);
#if __TRACK_PEDS_IN_NAVMESH
pPed->GetNavMeshTracker().Teleport(pMat->d);
#endif
}
// Set the model index.
pPed->SetModelId(fwModelId((strLocalIndex(modelIndex))));
// Set the ped's fade out distance
pPed->SetLodDistance(CPed::ms_uDefaultPedLodDistance);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ConcreteCreatePed
// PURPOSE : This function allow us to create a ped using CPedFactory rather
// than the project specific version of CPedFactory
// PARAMETERS : pedControlInfo - How is this ped controlled (is it network, player, etc.)
// modelIndex - the model to use for this ped.
// bFailSilentlyIfOutOfPeds - if true, suppress asserts if we don't have space in the pool or in the cache,
// for use as a replacement to code that previously checked GetNoOfFreeSpaces().
// RETURNS : The new ped.
/////////////////////////////////////////////////////////////////////////////////
CPed* CPedFactory::ConcreteCreatePed(const CControlledByInfo& pedControlInfo, u32 modelIndex, bool ASSERT_ONLY(bFailSilentlyIfOutOfPeds)) const
{
#if __DEV
ms_bWithinPedFactory = true;
#endif // __DEV
CPed *result = NULL;
fwModelId modelId((strLocalIndex(modelIndex)));
if (modelId.IsValid() && CModelInfo::GetBaseModelInfo(modelId))
{
if (CPed::GetPool()->GetNoOfFreeSpaces() == 0)
{
#if 0
if (!EjectOnePedFromCache())
#endif
{
Assertf(bFailSilentlyIfOutOfPeds, "%s Pool Full, Size == %d (you need to raise %s PoolSize in common/data/gameconfig.xml)", CPed::GetPool()->GetName(), CPed::GetPool()->GetSize(), CPed::GetPool()->GetName());
#if __DEV
ms_bWithinPedFactory = false;
#endif // __DEV
return NULL;
}
}
result = rage_new CPed(ENTITY_OWNEDBY_POPULATION, pedControlInfo, modelId.GetModelIndex());
if (PARAM_temppeddebug.Get())
{
if (result)
{
Displayf("[PED DEBUG]: Created ped %p, %s, frame: %d", result, CModelInfo::GetBaseModelInfo(modelId)->GetModelName(), fwTimer::GetFrameCount());
sysStack::PrintStackTrace();
}
}
}
Assertf(result, "Unable to create ped with modelIndex: %d", modelIndex);
#if __DEV
ms_bWithinPedFactory = false;
#endif // __DEV
if (result)
{
CPedPopulation::IncrementNumPedsCreatedThisCycle();
}
return result;
}
namespace CPoolHelpers
{
int g_PedPoolSize = 0;
int GetPedPoolSize()
{
Assertf(g_PedPoolSize != 0, "Trying to get the ped pool size before it's been set.");
// We also do a trap here, to catch it in non-assert builds, but more importantly,
// to catch it in case it's used from static initialization code when asserts may
// not yet be working.
TrapZ(g_PedPoolSize);
return g_PedPoolSize;
}
void SetPedPoolSize(int poolSize)
{
Assert(g_PedPoolSize == 0); // Not really meant to call this more than once at this time.
g_PedPoolSize = poolSize;
}
}
//////////////////////////////////////////////////////////////////////////
// FUNCTION: DestroyPed
// PURPOSE: some types of ped can't be destroyed immediately - wait till next frame update
//////////////////////////////////////////////////////////////////////////
bool CPedFactory::DestroyPed(CPed* pPed, bool cached)
{
PF_AUTO_PUSH_DETAIL("Destroy Ped");
if (pPed)
{
#if 0
// don't destroy ped if it's on the population cache list
if (IsPedInDestroyedCache(pPed))
{
pPed->ClearAllKnownRefs();
return true;
}
#endif
// inform network object manager about this ped being deleted
if(pPed->GetNetworkObject())
{
if (pPed->IsNetworkClone() && pPed->GetNetworkObject()->IsScriptObject())
{
if (!pPed->PopTypeIsMission())
Assertf(0, "Warning: clone %s is being destroyed (it is not a mission ped anymore)!", pPed->GetNetworkObject()->GetLogName());
else
Assertf(0, "Warning: clone %s is being destroyed!", pPed->GetNetworkObject()->GetLogName());
}
Assert(!pPed->IsNetworkClone());
NetworkInterface::UnregisterObject(pPed);
}
Assert(!pPed->GetNetworkObject());
return DestroyPedInternal(pPed, cached);
}
return false;
}
bool CPedFactory::AddDestroyedPedToCache(CPed* ped)
{
if (!ped)
return false;
// if we have the cache turned off or this is a streamed ped, free it now
if (!ms_reuseDestroyedPeds || !ped->GetPedModelInfo() || ped->GetPedModelInfo()->GetIsStreamedGfx())
{
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
return true;
}
// make sure we don't already have this pointer in the list
s32 firstFree = -1;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
// skip if this ped is already on the list
if (ms_reuseDestroyedPedArray[i].ped == ped)
return true;
if (firstFree == -1 && ms_reuseDestroyedPedArray[i].ped == NULL)
firstFree = i;
}
// we have a slot, cache the ped
if (firstFree > -1)
{
ped->RemoveBlip(BLIP_TYPE_CHAR);
ped->GetFrameCollisionHistory()->Reset();
ped->GetPedIntelligence()->FlushImmediately(true, true, true, false);
ped->GetPedIntelligence()->ClearScanners();
ped->GetPedIntelligence()->SetOrder(NULL);
ped->ClearDamage();
ped->ClearDecorations();
ped->ClearPedBrainWhenDeletingPed();
// remove script guid so script can't grab this ped pointer while it's in the cache
fwScriptGuid::DeleteGuid(*ped);
ped->RemovePhysics();
CPhysics::GetLevel()->SetInstanceIncludeFlags(ped->GetRagdollInst()->GetLevelIndex(), 0);
Vec3V vPos = ped->GetRagdollInst()->GetPosition();
vPos.SetZf(-100.0f);
ped->GetRagdollInst()->SetPosition(vPos);
ped->ClearAllKnownRefs();
REPLAY_ONLY(CReplayMgr::OnDelete(ped));
ms_reuseDestroyedPedArray[firstFree].ped = ped;
ms_reuseDestroyedPedArray[firstFree].nukeTime = fwTimer::GetTimeInMilliseconds() + ms_reuseDestroyedPedCacheTime;
ms_reuseDestroyedPedArray[firstFree].framesUntilReuse = 1;
ms_reuseDestroyedPedCount++;
ped->SetIsInPopulationCache(true);
Assert(ms_reuseDestroyedPedArray[firstFree].ped);
return true;
}
// no space left, destroy ped
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
return true;
}
bool CPedFactory::IsPedInDestroyedCache(const CPed* ped)
{
if (!ped)
return true;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped && ms_reuseDestroyedPedArray[i].ped == ped)
return true;
}
return false;
}
void CPedFactory::ClearDestroyedPedCache()
{
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped)
{
CPed* ped = ms_reuseDestroyedPedArray[i].ped;
ms_reuseDestroyedPedArray[i].ped = NULL;
// we can delete the ped like this because we've done all work of removing it from the world when it was added to this list
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
}
}
ms_reuseDestroyedPedCount = 0;
}
void CPedFactory::RemovePedGroupFromCache(const CLoadedModelGroup& pedGroup)
{
const u32 numPedsInGroup = pedGroup.CountMembers();
if (!numPedsInGroup)
return;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
for (s32 f = 0; f < numPedsInGroup; ++f)
{
u32 pedModelIndex = pedGroup.GetMember(f);
if (pedModelIndex == fwModelId::MI_INVALID)
continue;
if (ms_reuseDestroyedPedArray[i].ped && ms_reuseDestroyedPedArray[i].ped->GetModelIndex() == pedModelIndex)
{
CPed* ped = ms_reuseDestroyedPedArray[i].ped;
ms_reuseDestroyedPedArray[i].ped = NULL;
// we can delete the ped like this because we've done all work of removing it from the world when it was added to this list
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
ms_reuseDestroyedPedCount--;
}
}
}
}
void CPedFactory::ProcessDestroyedPedsCache()
{
u32 curTime = fwTimer::GetTimeInMilliseconds();
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped)
{
Assertf(!ms_reuseDestroyedPedArray[i].ped->GetExtension<CScriptEntityExtension>(), "Ped '%s' in the ped cache suddenly became a script ped", ms_reuseDestroyedPedArray[i].ped->GetModelName());
if (curTime >= ms_reuseDestroyedPedArray[i].nukeTime)
{
CPed* ped = ms_reuseDestroyedPedArray[i].ped;
ms_reuseDestroyedPedArray[i].ped = NULL;
// we can delete the ped like this because we've done all work of removing it from the world when it was added to this list
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
ms_reuseDestroyedPedCount--;
}
else
ms_reuseDestroyedPedArray[i].framesUntilReuse = (s8)rage::Max(0, ms_reuseDestroyedPedArray[i].framesUntilReuse - 1);
}
}
#if __BANK
if (ms_reuseDestroyedPedsDebugOutput)
{
grcDebugDraw::AddDebugOutput("Cached destroyed peds: %d", ms_reuseDestroyedPedCount);
u32 testCount = 0;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped)
{
CPed* ped = ms_reuseDestroyedPedArray[i].ped;
grcDebugDraw::AddDebugOutput("Ped %2d - 0x%8x - %s - %d", testCount + 1, ped, ped->GetModelName(), (ms_reuseDestroyedPedArray[i].nukeTime - fwTimer::GetTimeInMilliseconds()) / 1000);
testCount++;
}
}
Assertf(testCount == ms_reuseDestroyedPedCount, "Reuse destroyed ped count is out of sync with array! (%d - %d)", testCount, ms_reuseDestroyedPedCount);
}
#endif
}
CPed* CPedFactory::CreatePedFromDestroyedCache(const CControlledByInfo& pedControlInfo, fwModelId modelId, const Matrix34 *pMat, bool bShouldBeCloned)
{
// we don't cache player peds
if (pedControlInfo.IsControlledByLocalOrNetworkPlayer())
return NULL;
// find a ped with the same model id in the cache
CPed* ped = NULL;
s32 cacheIndex = -1;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped && ms_reuseDestroyedPedArray[i].ped->GetModelIndex() == modelId.GetModelIndex() && ms_reuseDestroyedPedArray[i].framesUntilReuse == 0)
{
ped = ms_reuseDestroyedPedArray[i].ped;
cacheIndex = i;
break;
}
}
if (!ped)
return NULL;
Assert(cacheIndex != -1);
// set default values, as if ped was newly created by the factory
ped->m_PedConfigFlags.Init(ped);
ped->m_PedResetFlags.Init(ped);
ped->SetControlledByInfo(pedControlInfo);
ped->SetOwnedBy(ENTITY_OWNEDBY_POPULATION);
ped->DelayedRemovalTimeReset();
ped->DelayedConversionTimeReset();
ped->SetArrestState(ArrestState_None);
ped->SetDeathState(DeathState_Alive);
ped->InitHealth();
ped->GetPedAiLod().ResetAllLodFlags();
ped->SetPedConfigFlag(CPED_CONFIG_FLAG_DontActivateRagdollOnPedCollisionWhenDead, false);
ped->SetPedConfigFlag(CPED_CONFIG_FLAG_DontActivateRagdollOnVehicleCollisionWhenDead, false);
ped->SetPedConfigFlag(CPED_CONFIG_FLAG_CanBeAgitated, true);
if(pMat)
{
ped->SetMatrix(*pMat);
#if __TRACK_PEDS_IN_NAVMESH
ped->GetNavMeshTracker().Teleport(pMat->d);
#endif
}
CPedModelInfo* pModelInfo = ((CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId));
Assert(pModelInfo);
ePedType pedType = pModelInfo->GetDefaultPedType();
switch(pedType)
{
case PEDTYPE_COP:
{
// Don't give cop weapons if created from script
static const atHashWithStringNotFinal COP_LOADOUT("LOADOUT_COP",0xB01B33EA);
CPedInventoryLoadOutManager::SetLoadOut(ped, COP_LOADOUT.GetHash());
if(ped->GetWeaponManager())
ped->GetPedIntelligence()->GetCombatBehaviour().SetShootRateModifier(0.3f); // 0.3f as COP_SHOT_RATE in PedFactory.cpp
ped->SetArmour(0.f);
ped->GetPedIntelligence()->AddTaskDefault(ped->ComputeWanderTask(*ped));
//Set up police to communicate events with friends/other cops.
ped->GetPedIntelligence()->SetInformRespectedFriends(CPedIntelligence::MAX_INFORM_FRIEND_DISTANCE_MAX_VALUE, CPedIntelligence::MAX_NUM_FRIENDS_TO_INFORM_MAX_VALUE);
}
break;
default:
break;
}
// ******* NETWORK REGISTRATION HAS TO BE DONE BEFORE ANY FURTHER STATE CHANGES ON THE PED! *******************
if (bShouldBeCloned && NetworkInterface::IsGameInProgress())
{
bool bRegistered = NetworkInterface::RegisterObject(ped, 0, 0);
// ped could not be registered as a network object (due to maximum number of ped objects being exceeded
// scripted peds are queued to be created
if (!bRegistered)
{
return NULL;
}
}
// pretend the factory created this ped
ped->SetPedConfigFlag(CPED_CONFIG_FLAG_CreatedByFactory, true);
// clear cache entry
ms_reuseDestroyedPedArray[cacheIndex].ped = NULL;
ms_reuseDestroyedPedCount--;
CPedPropsMgr::ClearAllPedProps(ped);
InitCachedPed(ped);
ped->SetIsInPopulationCache(false);
//ped->ClearAllKnownRefs();
return ped;
}
void CPedFactory::InitCachedPed(CPed* ped)
{
DEV_ONLY(ms_bWithinPedFactory = true);
// Make sure that player's animated inst is allowed to activate. It might have been
// told not to do so as part of the dead ragdoll shenanigans.
Assert(ped->GetAnimatedInst());
ped->GetAnimatedInst()->SetInstFlag(phInst::FLAG_NEVER_ACTIVATE, false);
ped->ClearClothController();
//
// DETACH PLAYER FROM VEHICLE, ETC:
//
ped->SetPedOutOfVehicle(CPed::PVF_Warp);
if(ped->GetIsAttached())
{
ped->DetachFromParent(0);
}
Assert(ped->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle) == false);
Assert(!ped->GetIsAttached());
CPickupManager::DetachAllPortablePickupsFromPed(ped);
//
// CLEANUP ANY RENDERING / VISUAL EFFECTS:
//
ped->GetPedDrawHandler().GetPropData().SetSkipRender(false);
ped->m_nPhysicalFlags.bRenderScorched = false;
// clean up wet effect
ped->ClearWetClothing();
if (ped->GetPedVfx())
{
ped->GetPedVfx()->Init(ped);
}
g_ptFxManager.RemovePtFxFromEntity(ped);
g_fireMan.ExtinguishEntityFires(ped, true);
//
// CLEANUP ANIMATION:
//
// Remove any higher priority animation
ped->GetMovePed().SetTaskNetwork(NULL);
ped->GetMovePed().SetAdditiveNetwork(NULL);
// get rid of ragdoll frame
ped->GetMovePed().SwitchToAnimated(0.f);
// The player may have been posed in a different animation state, set posed to false so the current animation state is forced after the camera update
ped->GetAnimDirector()->SetPosed(false);
ped->RestoreHeadingChangeRate();
ped->GetPortalTracker()->Teleport();
ped->GetPortalTracker()->RequestRescanNextUpdate();
//
// RESET PED STATE
//
if (!ped->IsNetworkClone()) // a clone always has its state updated via sync messages
{
#if __DEV
ped->m_nDEflags.bCheckedForDead = true;
#endif
ped->SetIsVisibleForModule( SETISVISIBLE_MODULE_GAMEPLAY, true );
ped->SetIsVisibleForModule( SETISVISIBLE_MODULE_FIRST_PERSON, true );
ped->ClearDeathInfo();
ped->ResetWeaponDamageInfo();
if (ped->GetHelmetComponent())
ped->GetHelmetComponent()->DisableHelmet();
ped->SetIsCrouching(false);
ped->StopAllMotion(true);
ped->ClearBaseFlag(fwEntity::REMOVE_FROM_WORLD);
ped->SetPedConfigFlag( CPED_CONFIG_FLAG_BlockWeaponSwitching, false );
ped->SetPedConfigFlag( CPED_CONFIG_FLAG_PedBeingDeleted, false );
ped->SetVelocity(ORIGIN);
// Clear all gadgets
CPedWeaponManager* pWeapMgr = ped->GetWeaponManager();
if (pWeapMgr)
{
pWeapMgr->DestroyEquippedGadgets();
pWeapMgr->DestroyEquippedWeaponObject(true OUTPUT_ONLY(,"CPedFactory::InitCachedPed"));
// DestroyEquippedWeaponObject above just destroys the object, but doesn't change the weapon
// hash in CPedWeaponManager::m_equippedWeapon. Without this, peds that get recycled would
// appear to CTaskAmbientClips to spawn with props in their hand, and cause asserts to fail.
// Should fix B* 425305: "[ai_task] Error: Assertf(!m_AmbientClipRequestHelper.RequiresProp()
// || m_AmbientClipRequestHelper.GetRequiredProp() == m_Prop) FAILED: New clip requires new
// prop [Prop_CS_Hotdog_01], but ped has permanent prop [Prop_CS_Binder_01]".
pWeapMgr->EquipWeapon(ped->GetDefaultUnarmedWeaponHash());
}
}
ped->ReleaseCoverPoint();
// reset physics inst flags
fragInstNMGta* pFragInst = ped->GetRagdollInst();
// Deal with frag, get original include flags from type
if (Verifyf(pFragInst, "No ragdoll on cached ped '%s'", ped->GetModelName()))
{
if (Verifyf(pFragInst->GetTypePhysics(), "Ped ragdoll has no physics type info (%s)", ped->GetModelName()))
{
if (Verifyf(pFragInst->GetTypePhysics()->GetArchetype(), "Ped ragdoll has no physics archetype (%s)", ped->GetModelName()))
{
Assertf(!ped->GetIsAttached(), "Trying to reset collision on attached ped.");
u32 uOrigIncludeFlags = pFragInst->GetTypePhysics()->GetArchetype()->GetIncludeFlags();
CPhysics::GetLevel()->SetInstanceIncludeFlags(ped->GetRagdollInst()->GetLevelIndex(), uOrigIncludeFlags);
}
}
}
// CPed::Init() will clear out the group index, so if it's set to something else
// now we'll probably get an assert from the group code since the group will still
// have a pointer to us. This should have been avoided by leaving the group at the point
// where we got added to reuse pool.
Assert(ped->GetPedGroupIndex() == PEDGROUP_INDEX_NONE);
ped->CPed::Init(true);
// Make sure we are not set to be removed as soon as possible again!
ped->SetRemoveAsSoonAsPossible(false);
DEV_ONLY(ms_bWithinPedFactory = false);
}
bool CPedFactory::EjectOnePedFromCache()
{
if (!ms_reuseDestroyedPedCount)
return false;
for (s32 i = 0; i < MAX_DESTROYED_PEDS_CACHED; ++i)
{
if (ms_reuseDestroyedPedArray[i].ped)
{
CPed* ped = ms_reuseDestroyedPedArray[i].ped;
ms_reuseDestroyedPedArray[i].ped = NULL;
DEV_ONLY(ms_bInDestroyPedFunction = true);
delete ped;
DEV_ONLY(ms_bInDestroyPedFunction = false);
ms_reuseDestroyedPedCount--;
return true;
}
}
return false;
}